From ece7598fa21074826b733b6a4e59a9bedb9b1184 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 4 Aug 2026 12:30:19 +0200 Subject: [PATCH 01/31] relaunch scaffold --- .gitignore | 3 +- Anchor.toml | 1 + Cargo.lock | 11 ++ programs/relaunch/Cargo.toml | 24 ++++ programs/relaunch/Xargo.toml | 2 + programs/relaunch/src/error.rs | 1 + programs/relaunch/src/events.rs | 1 + programs/relaunch/src/instructions/mod.rs | 1 + programs/relaunch/src/lib.rs | 25 ++++ tasks.md | 137 ++++++++++++++++++++++ 10 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 programs/relaunch/Cargo.toml create mode 100644 programs/relaunch/Xargo.toml create mode 100644 programs/relaunch/src/error.rs create mode 100644 programs/relaunch/src/events.rs create mode 100644 programs/relaunch/src/instructions/mod.rs create mode 100644 programs/relaunch/src/lib.rs create mode 100644 tasks.md diff --git a/.gitignore b/.gitignore index 63b3efe57..f021870af 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ scripts/*.js # Test ledger accounts - used when dumping accounts from mainnet to localnet test-ledger-accounts # Surfpool -.surfpool/ \ No newline at end of file +.surfpool/ +vibes/ \ No newline at end of file diff --git a/Anchor.toml b/Anchor.toml index 1faca827e..018e15d57 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -17,6 +17,7 @@ liquidation = "LiQnowFbFQdYyZhF4pUbpsrZCjxRTQ1upKJxZ2VXjde" mint_governor = "gvnr27cVeyW3AVf3acL7VCJ5WjGAphytnsgcK1feHyH" performance_package_v2 = "pPV2pfrxnmstSb9j7kEeCLny5BGj6SNwCWGd6xbGGzz" price_based_performance_package = "pbPPQH7jyKoSLu8QYs3rSY3YkDRXEBojKbTgnUg7NDS" +relaunch = "vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik" [registry] url = "https://api.apr.dev" diff --git a/Cargo.lock b/Cargo.lock index 83881e267..53ab3c65e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,6 +1770,17 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relaunch" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", + "futarchy", + "solana-security-txt", + "squads-multisig-program", +] + [[package]] name = "rustc-hash" version = "1.1.0" diff --git a/programs/relaunch/Cargo.toml b/programs/relaunch/Cargo.toml new file mode 100644 index 000000000..c99ab0688 --- /dev/null +++ b/programs/relaunch/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "relaunch" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "relaunch" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] +production = [] + +[dependencies] +anchor-lang = { version = "=0.29.0", features = ["init-if-needed", "event-cpi"] } +anchor-spl = { version = "=0.29.0", features = ["metadata"] } +solana-security-txt = "=1.1.1" +futarchy = { path = "../futarchy", features = ["cpi"] } +squads-multisig-program = { git = "https://github.com/Squads-Protocol/v4", package = "squads-multisig-program", rev = "6d5235da621a2e9b7379ea358e48760e981053be", features = ["cpi"] } diff --git a/programs/relaunch/Xargo.toml b/programs/relaunch/Xargo.toml new file mode 100644 index 000000000..475fb71ed --- /dev/null +++ b/programs/relaunch/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/programs/relaunch/src/error.rs @@ -0,0 +1 @@ + diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/programs/relaunch/src/events.rs @@ -0,0 +1 @@ + diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/programs/relaunch/src/instructions/mod.rs @@ -0,0 +1 @@ + diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs new file mode 100644 index 000000000..a9ab10fce --- /dev/null +++ b/programs/relaunch/src/lib.rs @@ -0,0 +1,25 @@ +//! A smart contract that relaunches existing tokens as futarchic DAOs. +use anchor_lang::prelude::*; + +pub mod error; +pub mod events; +pub mod instructions; + +#[cfg(not(feature = "no-entrypoint"))] +use solana_security_txt::security_txt; + +#[cfg(not(feature = "no-entrypoint"))] +security_txt! { + name: "relaunch", + project_url: "https://metadao.fi", + contacts: "telegram:metaproph3t,telegram:kollan_house", + source_code: "https://github.com/metaDAOproject/programs", + source_release: "v0.1.0", + policy: "The market will decide whether we pay a bug bounty.", + acknowledgements: "DCF = (CF1 / (1 + r)^1) + (CF2 / (1 + r)^2) + ... (CFn / (1 + r)^n)" +} + +declare_id!("vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik"); + +#[program] +pub mod relaunch {} diff --git a/tasks.md b/tasks.md new file mode 100644 index 000000000..3b934eff2 --- /dev/null +++ b/tasks.md @@ -0,0 +1,137 @@ +# Relaunch Implementation Tasks + +## Instructions for Claude + +**READ THIS FIRST:** + +1. Look at this file and find the task marked with `[NEXT]` +2. Read the referenced section in `vibes/relaunch-implementation-plan.md` for full context (design rationale lives in `vibes/relaunch-program-design.html`) +3. Do ONLY that task - nothing else +4. After completing the task, verify: `./rebuild.sh` after any program/SDK change, then `anchor test --skip-build` (use `.only` on the new test file while iterating, remove it and run the full suite before finishing) +5. If successful, remove the completed task from this file +6. Mark the next task with `[NEXT]` +7. Stop and wait for the user + +**DO NOT:** +- Do multiple tasks at once +- Skip ahead +- Forget to verify + +**Reference:** Full implementation plan is in `vibes/relaunch-implementation-plan.md` + +--- + +## Tasks + +### Phase 0: Scaffold + +> Reference: `relaunch-implementation-plan.md` → "Stage 0 — scaffold" + +- [NEXT] 0.2 State + constants + - `state/`: `Relaunch`, `DepositRecord`, `RelaunchState` enum — field list per plan Stage 0 (no `usdc_swap_pool` field) + - `constants.rs`: `TOKEN_SCALE`, `TOKENS_TO_DEPOSITORS = 10M`, `TOKENS_TO_FUTARCHY_LIQUIDITY = 2M`, `MAX_SECONDS_FOR_DEPOSITS = 14d`, `PRICE_SCALE = 1e12`, `USDC_SWAP_POOL`, WSOL mint, pump_amm / pump_fees / whirlpool program IDs, pump `"pool-authority"` seed + +- [ ] 0.3 SDK module scaffold + - `sdk/src/relaunch/v0.1/`: `RelaunchClient.createClient({ provider })`, `pda.ts` (`getRelaunchAddr`, `getRelaunchSignerAddr`, `getDepositRecordAddr`), generated types, `index.ts` re-export + - Package export wiring in `sdk/package.json` + +- [ ] 0.4 Test utilities + - `tests/relaunch/unit/` folder + placeholder test that runs + - `setupRelaunch()` helper skeleton (old mint under either token program) + +- [ ] 0.5 External fixtures + pool helpers + - Dump `pump_amm.so`, `pump_fees.so`, `whirlpool.so` from mainnet into `tests/fixtures/`; Anchor.toml entries + - `writePumpPool()`: fabricate pump_amm `Pool` + funded pool vaults (+ global/fee config accounts, dumped or fabricated) via bankrun `setAccount` — canonical-shaped WSOL- and USDC-quoted variants, one Token-2022-base, plus non-canonical variants for negative tests. Fabrication is deliberate even with the real program present — see plan Stage 0 note + - Whirlpool helpers via real instructions: config + fee tier + WSOL/USDC pool + seeded tick arrays (crib `whirlpools-cpi-examples` test setup) + - Test-feature override for the `USDC_SWAP_POOL` constant (fixture pool won't sit at the mainnet address) + - Verify: smoke test — pump_amm buy/sell against a fabricated pool and a Whirlpool swap both execute in bankrun + +### Phase 1: initialize_relaunch + start_deposits + +> Reference: `relaunch-implementation-plan.md` → "Stage 1 — initialize_relaunch + start_deposits" + +- [ ] 1.1 `initialize_relaunch` (instruction + SDK + tests) + - Accounts/validations per plan §1: `mint_authority` Signer proof-of-control, new-mint checks, source-pool canonicality, quote mint ∈ {WSOL, USDC}, old-mint extension allowlist, threshold/window/spending-limit validations; USDC-quoted sources share one ATA for `source_quote_vault`/`usdc_vault` + - Handler order: `set_authority` → metadata CPI → `mint_to` 12M → snapshot old supply → `set_inner` → event + - SDK: `initializeRelaunch()` with create-mint-to-self pre-instructions + - Tests: full list in plan §1 (happy path, decoupled mint creation, authority footguns, extension allowlist, canonicality failures, arg bounds, PDA squat + rival relaunch) + +- [ ] 1.2 `start_deposits` (instruction + SDK + tests) + - `has_one = admin` signer gate, `Initialized → Live`, stamp `unix_timestamp_started`, event + - Tests: happy path, non-admin fails, double-start fails (compute-budget trick for unique sigs) + +### Phase 2: deposit + failure loop + +> Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" + +- [ ] 2.1 Token-2022 test availability check + - Confirm bankrun ships Token-2022 as a builtin; if not, add the fixture + Anchor.toml entry + - Extend `setupRelaunch()` to produce Token-2022 old mints (with allowed metadata extensions) + +- [ ] 2.2 `deposit` (instruction + SDK + tests) + - `Live` + window gate, `transfer_checked` under old mint's owner program, `init_if_needed` DepositRecord, accumulate record + total, event + - Tests per plan §3: both token programs, accumulation, multiple depositors, zero amount, window/state gates + +- [ ] 2.3 `close_deposits` (instruction + SDK + tests) + - Window elapsed; u128 threshold math → `SellPending` | `Failed`; stamp `unix_timestamp_closed`, event + - Tests per plan §5: exact-threshold boundary, one-short → Failed, early close fails, 10^15-supply overflow case, permissionless crank + +- [ ] 2.4 `mark_failed` (instruction + SDK + tests) + - `SellPending → Failed` once `now > closed + grace_period_seconds`, event + - Tests per plan §11: before/after grace, wrong states, permissionless + +- [ ] 2.5 `claim_refund` (instruction + SDK + tests) + - `Failed`, returns exact `amount_deposited`, sets `claimed`, event + - Tests per plan §10: both token programs, double-refund, wrong state, vault-empty conservation, both end-to-end failure paths + +### Phase 3: PumpSwap + Whirlpool legs + +> Reference: `relaunch-implementation-plan.md` → "Stage 3 — execute_sell, execute_usdc_swap, deposit_via_buy" + +- [ ] 3.1 `execute_sell` (instruction + SDK + tests) + - Hand-built `cpi/pump_amm.rs` sell builder (discriminator + borsh args + verified 21-account list from design §06) + - Admin + `SellPending` + grace gate; sell full vault; `quote_recovered` from measured delta; WSOL → `Sold`, USDC → `Swapped` + - Tests per plan §6: both quote variants, slippage failure leaves state unchanged, non-admin/wrong-state/after-grace, Token-2022 base pool + +- [ ] 3.2 `execute_usdc_swap` (instruction + SDK + tests) + - Whirlpool `swap_v2` CPI (`orca_whirlpools_client` if clean under 0.29, else hand-built; verified 15-account list incl. `memo_program`); pool pinned `address = USDC_SWAP_POOL` + - Admin + `Sold`; full WSOL balance a→b; set `usdc_recovered` + - SDK: tick-array derivation helper + `min_usdc_out` + - Tests per plan §7: happy path, wrong pool address, slippage failure, non-admin/wrong-state + +- [ ] 3.3 `deposit_via_buy` (instruction + SDK + tests) + - `cpi/pump_amm.rs` buy builder (23 accounts = sell + volume accumulators); pull quote → buy → refund unspent → credit measured old-vault delta + - Tests per plan §4: both quote variants, mixes with direct deposits, tight `max_quote_in` fails cleanly, window/state gates, credited tokens refund on later failure + - SDK: `depositViaBuy()` + SOL→WSOL wrap pre-instructions + +### Phase 4: complete_relaunch + +> Reference: `relaunch-implementation-plan.md` → "Stage 4 — complete_relaunch" + +- [ ] 4.1 `complete_relaunch` (instruction + SDK + tests) + - u128 `price_1e12`; CPI `futarchy::initialize_dao` (launchpad-parity params incl. 1.5M min stake) + `provide_liquidity` (2M base, `usdc_recovered / 5` quote, Squads-vault position); transfer remaining USDC + mint & metadata authorities to Squads vault; store `dao`/`dao_vault`; `Complete` + - Squads accounts pre-created/PDA-derived exactly as launchpad (reentrancy-safe shape) + - Tests per plan §8: param assertions, AMM-ratio == twap identity, treasury remainder, authority handoffs, permissionless crank, rounding conservation + +### Phase 5: claim + full lifecycle + +> Reference: `relaunch-implementation-plan.md` → "Stage 5 — claim + full lifecycle" + +- [ ] 5.1 `claim` (instruction + SDK + tests) + - `Complete`, u128 floor pro-rata, transfer to depositor ATA (`associated_token` constraints), set `claimed`; dust stays in vault (no closes in v1) + - Tests per plan §9: pro-rata correctness, dust bound, double-claim, wrong state, buy-depositor parity + +- [ ] 5.2 Full-lifecycle integration tests + - `tests/integration/relaunch.test.ts`: happy path with money-math identities at each hop; failure path A (threshold miss → refunds); failure path B (grace lapse → `mark_failed` → refunds) + - Matrix: WSOL × USDC source, classic-SPL × Token-2022 old mint (happy path minimum per combination) + - Finish: full `anchor test --skip-build` suite green, no `.only` anywhere + +### Phase 6: Release prep + +> Reference: `relaunch-implementation-plan.md` → "After the code" + +- [ ] 6.1 Final program ID + - Grind vanity keypair, set `declare_id!` + Anchor.toml, `./rebuild.sh`, full suite green + +- [ ] 6.2 README deployments-table entry + verifiable build + - Note: first mainnet deploy itself is manual (Squads flow is upgrade-only) — user-led, not a task here From fde4225bc88a9945e497801b3ba33168a6519c2b Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 4 Aug 2026 15:26:22 +0200 Subject: [PATCH 02/31] state, constants, scaffolding --- programs/relaunch/src/constants.rs | 61 +++++++++++++ programs/relaunch/src/lib.rs | 5 ++ programs/relaunch/src/state/deposit_record.rs | 19 ++++ programs/relaunch/src/state/mod.rs | 5 ++ programs/relaunch/src/state/relaunch.rs | 88 +++++++++++++++++++ tasks.md | 6 +- 6 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 programs/relaunch/src/constants.rs create mode 100644 programs/relaunch/src/state/deposit_record.rs create mode 100644 programs/relaunch/src/state/mod.rs create mode 100644 programs/relaunch/src/state/relaunch.rs diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs new file mode 100644 index 000000000..a18e221c1 --- /dev/null +++ b/programs/relaunch/src/constants.rs @@ -0,0 +1,61 @@ +pub const TOKEN_SCALE: u64 = 1_000_000; + +pub const PRICE_SCALE: u128 = 1_000_000_000_000; + +/// 10M tokens with 6 decimals, distributed pro-rata to depositors. +pub const TOKENS_TO_DEPOSITORS: u64 = 10_000_000 * TOKEN_SCALE; +/// 2M tokens with 6 decimals, paired with recovered USDC in the futarchy AMM. +pub const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 2_000_000 * TOKEN_SCALE; + +/// 14 days, mirroring launchpad's launch-window cap. +pub const MAX_SECONDS_FOR_DEPOSITS: u32 = 60 * 60 * 24 * 14; + +pub mod wsol_mint { + use anchor_lang::prelude::declare_id; + + declare_id!("So11111111111111111111111111111111111111112"); +} + +// pump + +/// Seed of the pump pool-authority PDA, derived under the pump bonding-curve +/// program: `["pool-authority", mint]`. The canonical PumpSwap pool for a +/// mint has this PDA as its `creator`. +pub const PUMP_POOL_AUTHORITY_SEED: &[u8] = b"pool-authority"; + +/// The pump bonding-curve program. The pool-authority PDA that creates +/// canonical PumpSwap pools at graduation is derived under this program. +pub mod pump_program { + use anchor_lang::prelude::declare_id; + + declare_id!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); +} + +/// PumpSwap — the AMM that canonical graduation pools live on. +pub mod pump_amm_program { + use anchor_lang::prelude::declare_id; + + declare_id!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"); +} + +/// The pump fee program that PumpSwap consults for its dynamic fee tiers. +pub mod pump_fees_program { + use anchor_lang::prelude::declare_id; + + declare_id!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"); +} + +// Orca + +pub mod whirlpool_program { + use anchor_lang::prelude::declare_id; + + declare_id!("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"); +} + +/// Orca Whirlpool SOL/USDC 0.04% — the pinned venue for the WSOL→USDC swap leg. +pub mod usdc_swap_pool { + use anchor_lang::prelude::declare_id; + + declare_id!("Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE"); +} diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index a9ab10fce..aa98e1137 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -1,9 +1,14 @@ //! A smart contract that relaunches existing tokens as futarchic DAOs. use anchor_lang::prelude::*; +pub mod constants; pub mod error; pub mod events; pub mod instructions; +pub mod state; + +pub use constants::*; +pub use state::*; #[cfg(not(feature = "no-entrypoint"))] use solana_security_txt::security_txt; diff --git a/programs/relaunch/src/state/deposit_record.rs b/programs/relaunch/src/state/deposit_record.rs new file mode 100644 index 000000000..1faddf6b3 --- /dev/null +++ b/programs/relaunch/src/state/deposit_record.rs @@ -0,0 +1,19 @@ +use anchor_lang::prelude::*; + +#[account] +#[derive(InitSpace)] +pub struct DepositRecord { + /// The relaunch this record belongs to. + pub relaunch: Pubkey, + /// The depositor. + pub depositor: Pubkey, + /// The amount of old tokens deposited, including tokens bought via + /// `deposit_via_buy`. + pub amount_deposited: u64, + /// Whether the record has been settled by `claim` / `claim_refund`. + pub claimed: bool, + /// The sequence number of this record. Useful for sorting events. + pub seq_num: u64, + /// The PDA bump. + pub pda_bump: u8, +} diff --git a/programs/relaunch/src/state/mod.rs b/programs/relaunch/src/state/mod.rs new file mode 100644 index 000000000..3ee33f40b --- /dev/null +++ b/programs/relaunch/src/state/mod.rs @@ -0,0 +1,5 @@ +pub mod deposit_record; +pub mod relaunch; + +pub use deposit_record::*; +pub use relaunch::*; diff --git a/programs/relaunch/src/state/relaunch.rs b/programs/relaunch/src/state/relaunch.rs new file mode 100644 index 000000000..9eb9d20f8 --- /dev/null +++ b/programs/relaunch/src/state/relaunch.rs @@ -0,0 +1,88 @@ +use anchor_lang::prelude::*; +use futarchy::MAX_SPENDING_LIMIT_MEMBERS; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, PartialEq, Eq, InitSpace)] +pub enum RelaunchState { + Initialized, + Live, + SellPending, + Sold, + Swapped, + Complete, + Failed, +} + +#[account] +#[derive(InitSpace)] +pub struct Relaunch { + // identity & authority + /// The initializer; executes the sell + swap legs. + pub admin: Pubkey, + /// The token that will be distributed to depositors and that will control the DAO. + pub new_mint: Pubkey, + /// The token being relaunched. + pub old_mint: Pubkey, + /// The canonical PumpSwap pool for the old mint, validated at init. + pub source_pool: Pubkey, + /// The source pool's quote mint — WSOL or USDC. WSOL sources swap through + /// the `usdc_swap_pool` constant. + pub source_quote_mint: Pubkey, + + // signer & vaults (all ATAs of relaunch_signer) + /// The PDA that signs all CPIs and owns the vaults: `["relaunch_signer", relaunch]`. + pub relaunch_signer: Pubkey, + /// The PDA bump for the relaunch signer. + pub relaunch_signer_bump: u8, + /// The vault that escrows deposited old tokens. + pub old_token_vault: Pubkey, + /// The vault that holds the minted new tokens until claim / liquidity provision. + pub new_token_vault: Pubkey, + /// The vault that receives raw sell proceeds (WSOL or USDC). + pub source_quote_vault: Pubkey, + /// The vault that receives the swap-leg output; == `source_quote_vault` + /// for USDC sources. + pub usdc_vault: Pubkey, + + // config + /// The minimum participation, denominated in bps of old-token total supply. + pub threshold_bps: u16, + /// The old mint supply captured at init (threshold denominator). + pub old_supply_snapshot: u64, + /// The number of seconds that deposits will be open for. + pub seconds_for_deposits: u32, + /// The admin's window to sell after deposits close. + pub grace_period_seconds: u32, + + // DAO passthrough config (launchpad-style) + /// The monthly spending limit the DAO allocates to the team. + pub monthly_spending_limit_amount: u64, + /// The wallets that have access to the monthly spending limit. + #[max_len(MAX_SPENDING_LIMIT_MEMBERS)] + pub monthly_spending_limit_members: Vec, + /// The initial address used to sponsor team proposals. + pub team_address: Pubkey, + + // progress + /// The state of the relaunch. + pub state: RelaunchState, + /// The amount of old tokens deposited across all depositors. + pub total_deposited: u64, + /// The raw sell proceeds, in the source quote asset. + pub quote_recovered: u64, + /// The post-swap USDC (== `quote_recovered` for USDC sources). + pub usdc_recovered: u64, + /// The unix timestamp when deposits were opened. + pub unix_timestamp_started: Option, + /// The unix timestamp when deposits were closed. + pub unix_timestamp_closed: Option, + /// The unix timestamp when the relaunch was completed. + pub unix_timestamp_completed: Option, + /// The DAO, if the relaunch is complete. + pub dao: Option, + /// The DAO's Squads multisig vault, if the relaunch is complete. + pub dao_vault: Option, + /// The sequence number of this relaunch used for sorting events. + pub seq_num: u64, + /// The PDA bump. + pub pda_bump: u8, +} diff --git a/tasks.md b/tasks.md index 3b934eff2..4330f241e 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 0 — scaffold" -- [NEXT] 0.2 State + constants - - `state/`: `Relaunch`, `DepositRecord`, `RelaunchState` enum — field list per plan Stage 0 (no `usdc_swap_pool` field) - - `constants.rs`: `TOKEN_SCALE`, `TOKENS_TO_DEPOSITORS = 10M`, `TOKENS_TO_FUTARCHY_LIQUIDITY = 2M`, `MAX_SECONDS_FOR_DEPOSITS = 14d`, `PRICE_SCALE = 1e12`, `USDC_SWAP_POOL`, WSOL mint, pump_amm / pump_fees / whirlpool program IDs, pump `"pool-authority"` seed - -- [ ] 0.3 SDK module scaffold +- [NEXT] 0.3 SDK module scaffold - `sdk/src/relaunch/v0.1/`: `RelaunchClient.createClient({ provider })`, `pda.ts` (`getRelaunchAddr`, `getRelaunchSignerAddr`, `getDepositRecordAddr`), generated types, `index.ts` re-export - Package export wiring in `sdk/package.json` From 65a1ed413651444655c580c7350b2ed491473780 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 4 Aug 2026 15:40:56 +0200 Subject: [PATCH 03/31] sdk scaffold --- sdk/package.json | 2 + sdk/src/constants.ts | 3 + sdk/src/index.ts | 1 + sdk/src/relaunch/index.ts | 1 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 134 ++++++ sdk/src/relaunch/v0.1/index.ts | 3 + sdk/src/relaunch/v0.1/pda.ts | 43 ++ sdk/src/relaunch/v0.1/types/index.ts | 9 + sdk/src/relaunch/v0.1/types/relaunch.ts | 553 ++++++++++++++++++++++++ sdk/sync-types.sh | 1 + tasks.md | 6 +- 11 files changed, 751 insertions(+), 5 deletions(-) create mode 100644 sdk/src/relaunch/index.ts create mode 100644 sdk/src/relaunch/v0.1/RelaunchClient.ts create mode 100644 sdk/src/relaunch/v0.1/index.ts create mode 100644 sdk/src/relaunch/v0.1/pda.ts create mode 100644 sdk/src/relaunch/v0.1/types/index.ts create mode 100644 sdk/src/relaunch/v0.1/types/relaunch.ts diff --git a/sdk/package.json b/sdk/package.json index 150579ac6..5f01f7250 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -18,6 +18,7 @@ "./mint_governor": "./dist/mint_governor/index.js", "./performance_package_v2": "./dist/performance_package_v2/index.js", "./price_based_performance_package": "./dist/price_based_performance_package/index.js", + "./relaunch": "./dist/relaunch/index.js", "./shared_liquidity_manager": "./dist/shared_liquidity_manager/index.js", "./amm/*": "./dist/amm/*/index.js", "./autocrat/*": "./dist/autocrat/*/index.js", @@ -30,6 +31,7 @@ "./mint_governor/*": "./dist/mint_governor/*/index.js", "./performance_package_v2/*": "./dist/performance_package_v2/*/index.js", "./price_based_performance_package/*": "./dist/price_based_performance_package/*/index.js", + "./relaunch/*": "./dist/relaunch/*/index.js", "./shared_liquidity_manager/*": "./dist/shared_liquidity_manager/*/index.js" }, "license": "BSL-1.0", diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index a9e431a81..338fead3d 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -68,6 +68,9 @@ export const PERFORMANCE_PACKAGE_V2_PROGRAM_ID = new PublicKey( export const LIQUIDATION_V0_7_PROGRAM_ID = new PublicKey( "LiQnowFbFQdYyZhF4pUbpsrZCjxRTQ1upKJxZ2VXjde", ); +export const RELAUNCH_V0_1_PROGRAM_ID = new PublicKey( + "vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik", +); export const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey( "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", diff --git a/sdk/src/index.ts b/sdk/src/index.ts index b909c2750..b55f0ce09 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -8,6 +8,7 @@ export * from "./liquidation/index.js"; export * from "./mint_governor/index.js"; export * from "./performance_package_v2/index.js"; export * from "./price_based_performance_package/index.js"; +export * from "./relaunch/index.js"; // Shared exports export * from "./utils.js"; diff --git a/sdk/src/relaunch/index.ts b/sdk/src/relaunch/index.ts new file mode 100644 index 000000000..727de0c64 --- /dev/null +++ b/sdk/src/relaunch/index.ts @@ -0,0 +1 @@ +export * from "./v0.1/index.js"; diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts new file mode 100644 index 000000000..92e56a912 --- /dev/null +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -0,0 +1,134 @@ +import { AnchorProvider, Program } from "@coral-xyz/anchor"; +import { AccountInfo, PublicKey } from "@solana/web3.js"; +import { RELAUNCH_V0_1_PROGRAM_ID } from "../../constants.js"; +import { + RelaunchProgram, + RelaunchIDL, + RelaunchAccount, + DepositRecordAccount, +} from "./types/index.js"; +import { + getRelaunchAddr, + getRelaunchSignerAddr, + getDepositRecordAddr, +} from "./pda.js"; +import { getEventAuthorityAddr } from "../../pda.js"; + +export type CreateRelaunchClientParams = { + provider: AnchorProvider; + relaunchProgramId?: PublicKey; +}; + +export class RelaunchClient { + public readonly provider: AnchorProvider; + public readonly relaunchProgram: Program; + public readonly programId: PublicKey; + + constructor(provider: AnchorProvider, relaunchProgramId: PublicKey) { + this.provider = provider; + this.programId = relaunchProgramId; + this.relaunchProgram = new Program( + RelaunchIDL, + relaunchProgramId, + provider, + ); + } + + public static createClient( + createRelaunchClientParams: CreateRelaunchClientParams, + ): RelaunchClient { + let { provider, relaunchProgramId } = createRelaunchClientParams; + + return new RelaunchClient( + provider, + relaunchProgramId || RELAUNCH_V0_1_PROGRAM_ID, + ); + } + + public getProgramId(): PublicKey { + return this.programId; + } + + async fetchRelaunch(relaunch: PublicKey): Promise { + return this.relaunchProgram.account.relaunch.fetchNullable(relaunch); + } + + async deserializeRelaunch( + accountInfo: AccountInfo, + ): Promise { + return this.relaunchProgram.coder.accounts.decode( + "relaunch", + accountInfo.data, + ); + } + + async fetchDepositRecord( + depositRecord: PublicKey, + ): Promise { + return this.relaunchProgram.account.depositRecord.fetchNullable( + depositRecord, + ); + } + + async deserializeDepositRecord( + accountInfo: AccountInfo, + ): Promise { + return this.relaunchProgram.coder.accounts.decode( + "depositRecord", + accountInfo.data, + ); + } + + async getRelaunch({ + newMint, + }: { + newMint: PublicKey; + }): Promise { + const relaunch = this.getRelaunchAddress({ newMint }); + return this.fetchRelaunch(relaunch); + } + + async getDepositRecord({ + relaunch, + depositor, + }: { + relaunch: PublicKey; + depositor: PublicKey; + }): Promise { + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + return this.fetchDepositRecord(depositRecord); + } + + public getRelaunchAddress({ newMint }: { newMint: PublicKey }): PublicKey { + return getRelaunchAddr({ programId: this.programId, newMint })[0]; + } + + public getRelaunchSignerAddress({ + relaunch, + }: { + relaunch: PublicKey; + }): PublicKey { + return getRelaunchSignerAddr({ programId: this.programId, relaunch })[0]; + } + + public getDepositRecordAddress({ + relaunch, + depositor, + }: { + relaunch: PublicKey; + depositor: PublicKey; + }): PublicKey { + return getDepositRecordAddr({ + programId: this.programId, + relaunch, + depositor, + })[0]; + } + + public getEventAuthorityAddress(): PublicKey { + return getEventAuthorityAddr(this.programId)[0]; + } +} diff --git a/sdk/src/relaunch/v0.1/index.ts b/sdk/src/relaunch/v0.1/index.ts new file mode 100644 index 000000000..aa5f243a4 --- /dev/null +++ b/sdk/src/relaunch/v0.1/index.ts @@ -0,0 +1,3 @@ +export * from "./types/index.js"; +export * from "./pda.js"; +export * from "./RelaunchClient.js"; diff --git a/sdk/src/relaunch/v0.1/pda.ts b/sdk/src/relaunch/v0.1/pda.ts new file mode 100644 index 000000000..40edb26a1 --- /dev/null +++ b/sdk/src/relaunch/v0.1/pda.ts @@ -0,0 +1,43 @@ +import { PublicKey } from "@solana/web3.js"; +import { RELAUNCH_V0_1_PROGRAM_ID } from "../../constants.js"; + +export const getRelaunchAddr = ({ + programId = RELAUNCH_V0_1_PROGRAM_ID, + newMint, +}: { + programId?: PublicKey; + newMint: PublicKey; +}) => { + return PublicKey.findProgramAddressSync( + [Buffer.from("relaunch"), newMint.toBuffer()], + programId, + ); +}; + +export const getRelaunchSignerAddr = ({ + programId = RELAUNCH_V0_1_PROGRAM_ID, + relaunch, +}: { + programId?: PublicKey; + relaunch: PublicKey; +}) => { + return PublicKey.findProgramAddressSync( + [Buffer.from("relaunch_signer"), relaunch.toBuffer()], + programId, + ); +}; + +export const getDepositRecordAddr = ({ + programId = RELAUNCH_V0_1_PROGRAM_ID, + relaunch, + depositor, +}: { + programId?: PublicKey; + relaunch: PublicKey; + depositor: PublicKey; +}) => { + return PublicKey.findProgramAddressSync( + [Buffer.from("deposit_record"), relaunch.toBuffer(), depositor.toBuffer()], + programId, + ); +}; diff --git a/sdk/src/relaunch/v0.1/types/index.ts b/sdk/src/relaunch/v0.1/types/index.ts new file mode 100644 index 000000000..2198a1250 --- /dev/null +++ b/sdk/src/relaunch/v0.1/types/index.ts @@ -0,0 +1,9 @@ +import { IdlAccounts } from "@coral-xyz/anchor"; + +import { Relaunch as RelaunchProgram, IDL as RelaunchIDL } from "./relaunch.js"; + +export { RelaunchProgram, RelaunchIDL }; + +export type RelaunchAccount = IdlAccounts["relaunch"]; +export type DepositRecordAccount = + IdlAccounts["depositRecord"]; diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts new file mode 100644 index 000000000..cccb58a58 --- /dev/null +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -0,0 +1,553 @@ +export type Relaunch = { + version: "0.1.0"; + name: "relaunch"; + instructions: []; + accounts: [ + { + name: "depositRecord"; + type: { + kind: "struct"; + fields: [ + { + name: "relaunch"; + docs: ["The relaunch this record belongs to."]; + type: "publicKey"; + }, + { + name: "depositor"; + docs: ["The depositor."]; + type: "publicKey"; + }, + { + name: "amountDeposited"; + docs: [ + "The amount of old tokens deposited, including tokens bought via", + "`deposit_via_buy`.", + ]; + type: "u64"; + }, + { + name: "claimed"; + docs: [ + "Whether the record has been settled by `claim` / `claim_refund`.", + ]; + type: "bool"; + }, + { + name: "seqNum"; + docs: [ + "The sequence number of this record. Useful for sorting events.", + ]; + type: "u64"; + }, + { + name: "pdaBump"; + docs: ["The PDA bump."]; + type: "u8"; + }, + ]; + }; + }, + { + name: "relaunch"; + type: { + kind: "struct"; + fields: [ + { + name: "admin"; + docs: ["The initializer; executes the sell + swap legs."]; + type: "publicKey"; + }, + { + name: "newMint"; + docs: [ + "The token that will be distributed to depositors and that will control the DAO.", + ]; + type: "publicKey"; + }, + { + name: "oldMint"; + docs: ["The token being relaunched."]; + type: "publicKey"; + }, + { + name: "sourcePool"; + docs: [ + "The canonical PumpSwap pool for the old mint, validated at init.", + ]; + type: "publicKey"; + }, + { + name: "sourceQuoteMint"; + docs: [ + "The source pool's quote mint — WSOL or USDC. WSOL sources swap through", + "the `usdc_swap_pool` constant.", + ]; + type: "publicKey"; + }, + { + name: "relaunchSigner"; + docs: [ + 'The PDA that signs all CPIs and owns the vaults: `["relaunch_signer", relaunch]`.', + ]; + type: "publicKey"; + }, + { + name: "relaunchSignerBump"; + docs: ["The PDA bump for the relaunch signer."]; + type: "u8"; + }, + { + name: "oldTokenVault"; + docs: ["The vault that escrows deposited old tokens."]; + type: "publicKey"; + }, + { + name: "newTokenVault"; + docs: [ + "The vault that holds the minted new tokens until claim / liquidity provision.", + ]; + type: "publicKey"; + }, + { + name: "sourceQuoteVault"; + docs: ["The vault that receives raw sell proceeds (WSOL or USDC)."]; + type: "publicKey"; + }, + { + name: "usdcVault"; + docs: [ + "The vault that receives the swap-leg output; == `source_quote_vault`", + "for USDC sources.", + ]; + type: "publicKey"; + }, + { + name: "thresholdBps"; + docs: [ + "The minimum participation, denominated in bps of old-token total supply.", + ]; + type: "u16"; + }, + { + name: "oldSupplySnapshot"; + docs: [ + "The old mint supply captured at init (threshold denominator).", + ]; + type: "u64"; + }, + { + name: "secondsForDeposits"; + docs: ["The number of seconds that deposits will be open for."]; + type: "u32"; + }, + { + name: "gracePeriodSeconds"; + docs: ["The admin's window to sell after deposits close."]; + type: "u32"; + }, + { + name: "monthlySpendingLimitAmount"; + docs: ["The monthly spending limit the DAO allocates to the team."]; + type: "u64"; + }, + { + name: "monthlySpendingLimitMembers"; + docs: [ + "The wallets that have access to the monthly spending limit.", + ]; + type: { + vec: "publicKey"; + }; + }, + { + name: "teamAddress"; + docs: ["The initial address used to sponsor team proposals."]; + type: "publicKey"; + }, + { + name: "state"; + docs: ["The state of the relaunch."]; + type: { + defined: "RelaunchState"; + }; + }, + { + name: "totalDeposited"; + docs: ["The amount of old tokens deposited across all depositors."]; + type: "u64"; + }, + { + name: "quoteRecovered"; + docs: ["The raw sell proceeds, in the source quote asset."]; + type: "u64"; + }, + { + name: "usdcRecovered"; + docs: [ + "The post-swap USDC (== `quote_recovered` for USDC sources).", + ]; + type: "u64"; + }, + { + name: "unixTimestampStarted"; + docs: ["The unix timestamp when deposits were opened."]; + type: { + option: "i64"; + }; + }, + { + name: "unixTimestampClosed"; + docs: ["The unix timestamp when deposits were closed."]; + type: { + option: "i64"; + }; + }, + { + name: "unixTimestampCompleted"; + docs: ["The unix timestamp when the relaunch was completed."]; + type: { + option: "i64"; + }; + }, + { + name: "dao"; + docs: ["The DAO, if the relaunch is complete."]; + type: { + option: "publicKey"; + }; + }, + { + name: "daoVault"; + docs: [ + "The DAO's Squads multisig vault, if the relaunch is complete.", + ]; + type: { + option: "publicKey"; + }; + }, + { + name: "seqNum"; + docs: [ + "The sequence number of this relaunch used for sorting events.", + ]; + type: "u64"; + }, + { + name: "pdaBump"; + docs: ["The PDA bump."]; + type: "u8"; + }, + ]; + }; + }, + ]; + types: [ + { + name: "RelaunchState"; + type: { + kind: "enum"; + variants: [ + { + name: "Initialized"; + }, + { + name: "Live"; + }, + { + name: "SellPending"; + }, + { + name: "Sold"; + }, + { + name: "Swapped"; + }, + { + name: "Complete"; + }, + { + name: "Failed"; + }, + ]; + }; + }, + ]; +}; + +export const IDL: Relaunch = { + version: "0.1.0", + name: "relaunch", + instructions: [], + accounts: [ + { + name: "depositRecord", + type: { + kind: "struct", + fields: [ + { + name: "relaunch", + docs: ["The relaunch this record belongs to."], + type: "publicKey", + }, + { + name: "depositor", + docs: ["The depositor."], + type: "publicKey", + }, + { + name: "amountDeposited", + docs: [ + "The amount of old tokens deposited, including tokens bought via", + "`deposit_via_buy`.", + ], + type: "u64", + }, + { + name: "claimed", + docs: [ + "Whether the record has been settled by `claim` / `claim_refund`.", + ], + type: "bool", + }, + { + name: "seqNum", + docs: [ + "The sequence number of this record. Useful for sorting events.", + ], + type: "u64", + }, + { + name: "pdaBump", + docs: ["The PDA bump."], + type: "u8", + }, + ], + }, + }, + { + name: "relaunch", + type: { + kind: "struct", + fields: [ + { + name: "admin", + docs: ["The initializer; executes the sell + swap legs."], + type: "publicKey", + }, + { + name: "newMint", + docs: [ + "The token that will be distributed to depositors and that will control the DAO.", + ], + type: "publicKey", + }, + { + name: "oldMint", + docs: ["The token being relaunched."], + type: "publicKey", + }, + { + name: "sourcePool", + docs: [ + "The canonical PumpSwap pool for the old mint, validated at init.", + ], + type: "publicKey", + }, + { + name: "sourceQuoteMint", + docs: [ + "The source pool's quote mint — WSOL or USDC. WSOL sources swap through", + "the `usdc_swap_pool` constant.", + ], + type: "publicKey", + }, + { + name: "relaunchSigner", + docs: [ + 'The PDA that signs all CPIs and owns the vaults: `["relaunch_signer", relaunch]`.', + ], + type: "publicKey", + }, + { + name: "relaunchSignerBump", + docs: ["The PDA bump for the relaunch signer."], + type: "u8", + }, + { + name: "oldTokenVault", + docs: ["The vault that escrows deposited old tokens."], + type: "publicKey", + }, + { + name: "newTokenVault", + docs: [ + "The vault that holds the minted new tokens until claim / liquidity provision.", + ], + type: "publicKey", + }, + { + name: "sourceQuoteVault", + docs: ["The vault that receives raw sell proceeds (WSOL or USDC)."], + type: "publicKey", + }, + { + name: "usdcVault", + docs: [ + "The vault that receives the swap-leg output; == `source_quote_vault`", + "for USDC sources.", + ], + type: "publicKey", + }, + { + name: "thresholdBps", + docs: [ + "The minimum participation, denominated in bps of old-token total supply.", + ], + type: "u16", + }, + { + name: "oldSupplySnapshot", + docs: [ + "The old mint supply captured at init (threshold denominator).", + ], + type: "u64", + }, + { + name: "secondsForDeposits", + docs: ["The number of seconds that deposits will be open for."], + type: "u32", + }, + { + name: "gracePeriodSeconds", + docs: ["The admin's window to sell after deposits close."], + type: "u32", + }, + { + name: "monthlySpendingLimitAmount", + docs: ["The monthly spending limit the DAO allocates to the team."], + type: "u64", + }, + { + name: "monthlySpendingLimitMembers", + docs: [ + "The wallets that have access to the monthly spending limit.", + ], + type: { + vec: "publicKey", + }, + }, + { + name: "teamAddress", + docs: ["The initial address used to sponsor team proposals."], + type: "publicKey", + }, + { + name: "state", + docs: ["The state of the relaunch."], + type: { + defined: "RelaunchState", + }, + }, + { + name: "totalDeposited", + docs: ["The amount of old tokens deposited across all depositors."], + type: "u64", + }, + { + name: "quoteRecovered", + docs: ["The raw sell proceeds, in the source quote asset."], + type: "u64", + }, + { + name: "usdcRecovered", + docs: [ + "The post-swap USDC (== `quote_recovered` for USDC sources).", + ], + type: "u64", + }, + { + name: "unixTimestampStarted", + docs: ["The unix timestamp when deposits were opened."], + type: { + option: "i64", + }, + }, + { + name: "unixTimestampClosed", + docs: ["The unix timestamp when deposits were closed."], + type: { + option: "i64", + }, + }, + { + name: "unixTimestampCompleted", + docs: ["The unix timestamp when the relaunch was completed."], + type: { + option: "i64", + }, + }, + { + name: "dao", + docs: ["The DAO, if the relaunch is complete."], + type: { + option: "publicKey", + }, + }, + { + name: "daoVault", + docs: [ + "The DAO's Squads multisig vault, if the relaunch is complete.", + ], + type: { + option: "publicKey", + }, + }, + { + name: "seqNum", + docs: [ + "The sequence number of this relaunch used for sorting events.", + ], + type: "u64", + }, + { + name: "pdaBump", + docs: ["The PDA bump."], + type: "u8", + }, + ], + }, + }, + ], + types: [ + { + name: "RelaunchState", + type: { + kind: "enum", + variants: [ + { + name: "Initialized", + }, + { + name: "Live", + }, + { + name: "SellPending", + }, + { + name: "Sold", + }, + { + name: "Swapped", + }, + { + name: "Complete", + }, + { + name: "Failed", + }, + ], + }, + }, + ], +}; diff --git a/sdk/sync-types.sh b/sdk/sync-types.sh index d8eb5a75c..60f2e40f8 100755 --- a/sdk/sync-types.sh +++ b/sdk/sync-types.sh @@ -14,3 +14,4 @@ cp "$TYPES_DIR/liquidation.ts" ./src/liquidation/v0.7/types/ cp "$TYPES_DIR/mint_governor.ts" ./src/mint_governor/v0.7/types/ cp "$TYPES_DIR/performance_package_v2.ts" ./src/performance_package_v2/v0.7/types/ cp "$TYPES_DIR/price_based_performance_package.ts" ./src/price_based_performance_package/v0.6/types/ +cp "$TYPES_DIR/relaunch.ts" ./src/relaunch/v0.1/types/ diff --git a/tasks.md b/tasks.md index 4330f241e..f8d0c07e2 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 0 — scaffold" -- [NEXT] 0.3 SDK module scaffold - - `sdk/src/relaunch/v0.1/`: `RelaunchClient.createClient({ provider })`, `pda.ts` (`getRelaunchAddr`, `getRelaunchSignerAddr`, `getDepositRecordAddr`), generated types, `index.ts` re-export - - Package export wiring in `sdk/package.json` - -- [ ] 0.4 Test utilities +- [NEXT] 0.4 Test utilities - `tests/relaunch/unit/` folder + placeholder test that runs - `setupRelaunch()` helper skeleton (old mint under either token program) From 45cb9bce5d2e13030fb56bee9a6f67dcb86d7930 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 4 Aug 2026 16:11:38 +0200 Subject: [PATCH 04/31] more scaffolding --- tasks.md | 8 +- tests/main.test.ts | 4 + tests/relaunch/main.test.ts | 14 ++++ tests/relaunch/unit/scaffold.test.ts | 53 ++++++++++++++ tests/relaunch/utils.ts | 106 +++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 tests/relaunch/main.test.ts create mode 100644 tests/relaunch/unit/scaffold.test.ts create mode 100644 tests/relaunch/utils.ts diff --git a/tasks.md b/tasks.md index f8d0c07e2..bd759a6f6 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 0 — scaffold" -- [NEXT] 0.4 Test utilities - - `tests/relaunch/unit/` folder + placeholder test that runs - - `setupRelaunch()` helper skeleton (old mint under either token program) - -- [ ] 0.5 External fixtures + pool helpers +- [NEXT] 0.5 External fixtures + pool helpers - Dump `pump_amm.so`, `pump_fees.so`, `whirlpool.so` from mainnet into `tests/fixtures/`; Anchor.toml entries - `writePumpPool()`: fabricate pump_amm `Pool` + funded pool vaults (+ global/fee config accounts, dumped or fabricated) via bankrun `setAccount` — canonical-shaped WSOL- and USDC-quoted variants, one Token-2022-base, plus non-canonical variants for negative tests. Fabrication is deliberate even with the real program present — see plan Stage 0 note - Whirlpool helpers via real instructions: config + fee tier + WSOL/USDC pool + seeded tick arrays (crib `whirlpools-cpi-examples` test setup) @@ -57,7 +53,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" - [ ] 2.1 Token-2022 test availability check - - Confirm bankrun ships Token-2022 as a builtin; if not, add the fixture + Anchor.toml entry + - ~~Confirm bankrun ships Token-2022 as a builtin~~ — confirmed during 0.4: it's a builtin (`tests/relaunch/unit/scaffold.test.ts` runs a plain Token-2022 old mint), no fixture needed - Extend `setupRelaunch()` to produce Token-2022 old mints (with allowed metadata extensions) - [ ] 2.2 `deposit` (instruction + SDK + tests) diff --git a/tests/main.test.ts b/tests/main.test.ts index 7c4d63071..8da2aa5ac 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -9,6 +9,7 @@ import mintGovernor from "./mintGovernor/main.test.js"; import performancePackageV2 from "./performancePackageV2/main.test.js"; import liquidation from "./liquidation/main.test.js"; import gatedMint from "./gatedMint/main.test.js"; +import relaunch from "./relaunch/main.test.js"; import { BanksClient, @@ -40,6 +41,7 @@ import { MintGovernorClient, GatedMintClient, LiquidationClient, + RelaunchClient, LOW_FEE_RAYDIUM_CONFIG, sha256, } from "@metadaoproject/programs"; @@ -103,6 +105,7 @@ export interface TestContext { mintGovernor: MintGovernorClient; gatedMint: GatedMintClient; liquidation: LiquidationClient; + relaunch: RelaunchClient; payer: Keypair; squadsConnection: Connection; createTokenAccount: (mint: PublicKey, owner: PublicKey) => Promise; @@ -762,6 +765,7 @@ describe("mint_governor", mintGovernor); describe("performance_package_v2", performancePackageV2); describe("liquidation", liquidation); describe("gated_mint", gatedMint); +describe("relaunch", relaunch); describe("project-wide integration tests", function () { it.skip("mint and swap in a single transaction", mintAndSwap); describe("full launch v6", fullLaunch); diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts new file mode 100644 index 000000000..cd179658b --- /dev/null +++ b/tests/relaunch/main.test.ts @@ -0,0 +1,14 @@ +import scaffold from "./unit/scaffold.test.js"; +import { RelaunchClient } from "@metadaoproject/programs"; +import { BankrunProvider } from "anchor-bankrun"; + +export default function suite() { + before(async function () { + const provider = new BankrunProvider(this.context); + this.relaunch = RelaunchClient.createClient({ + provider: provider as any, + }); + }); + + describe("scaffold", scaffold); +} diff --git a/tests/relaunch/unit/scaffold.test.ts b/tests/relaunch/unit/scaffold.test.ts new file mode 100644 index 000000000..bdd86f91b --- /dev/null +++ b/tests/relaunch/unit/scaffold.test.ts @@ -0,0 +1,53 @@ +import { PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { RELAUNCH_V0_1_PROGRAM_ID } from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; + +export default function suite() { + it("relaunch program is deployed", async function () { + const program = await this.banksClient.getAccount(RELAUNCH_V0_1_PROGRAM_ID); + + assert.isNotNull(program); + assert.isTrue(program!.executable); + }); + + const assertOldMintSetup = async function ( + this: Mocha.Context, + oldTokenProgram: PublicKey, + ) { + const { oldMint, payerOldTokenAccount } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram, + }); + + const rawMint = await this.banksClient.getAccount(oldMint); + assert.isTrue(rawMint!.owner.equals(oldTokenProgram)); + + const mint = token.unpackMint( + oldMint, + { ...rawMint!, data: Buffer.from(rawMint!.data) }, + oldTokenProgram, + ); + assert.equal(mint.decimals, 6); + assert.equal(mint.supply.toString(), DEFAULT_OLD_SUPPLY.toString()); + + const rawTokenAccount = + await this.banksClient.getAccount(payerOldTokenAccount); + const tokenAccount = token.unpackAccount( + payerOldTokenAccount, + { ...rawTokenAccount!, data: Buffer.from(rawTokenAccount!.data) }, + oldTokenProgram, + ); + assert.equal(tokenAccount.amount.toString(), DEFAULT_OLD_SUPPLY.toString()); + }; + + it("setupRelaunch creates a classic SPL old mint with supply", async function () { + await assertOldMintSetup.call(this, token.TOKEN_PROGRAM_ID); + }); + + it("setupRelaunch creates a Token-2022 old mint with supply", async function () { + await assertOldMintSetup.call(this, token.TOKEN_2022_PROGRAM_ID); + }); +} diff --git a/tests/relaunch/utils.ts b/tests/relaunch/utils.ts new file mode 100644 index 000000000..fd95c5ff3 --- /dev/null +++ b/tests/relaunch/utils.ts @@ -0,0 +1,106 @@ +import { + Keypair, + PublicKey, + Signer, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { BanksClient } from "solana-bankrun"; + +// 1B tokens at 6 decimals — the supply of a pump token. +export const DEFAULT_OLD_SUPPLY = 1_000_000_000n * 10n ** 6n; + +export async function createOldMint( + banksClient: BanksClient, + payer: Signer, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, + decimals: number = 6, +): Promise { + const mintKeypair = Keypair.generate(); + const rent = await banksClient.getRent(); + const lamports = Number(rent.minimumBalance(BigInt(token.MINT_SIZE))); + + const tx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mintKeypair.publicKey, + lamports, + space: token.MINT_SIZE, + programId: tokenProgram, + }), + token.createInitializeMint2Instruction( + mintKeypair.publicKey, + decimals, + payer.publicKey, + null, + tokenProgram, + ), + ); + + tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; + tx.feePayer = payer.publicKey; + tx.sign(payer, mintKeypair); + + await banksClient.processTransaction(tx); + + return mintKeypair.publicKey; +} + +export type SetupRelaunchParams = { + banksClient: BanksClient; + payer: Keypair; + oldTokenProgram?: PublicKey; + oldSupply?: bigint; +}; + +export type RelaunchSetup = { + oldMint: PublicKey; + oldTokenProgram: PublicKey; + payerOldTokenAccount: PublicKey; +}; + +// Creates a pump-style old mint under the given token program, with the +// initial supply minted to the payer (who funds depositors in tests). The +// payer keeps mint authority so tests can fund depositors directly. +export async function setupRelaunch({ + banksClient, + payer, + oldTokenProgram = token.TOKEN_PROGRAM_ID, + oldSupply = DEFAULT_OLD_SUPPLY, +}: SetupRelaunchParams): Promise { + const oldMint = await createOldMint(banksClient, payer, oldTokenProgram); + + const payerOldTokenAccount = token.getAssociatedTokenAddressSync( + oldMint, + payer.publicKey, + false, + oldTokenProgram, + ); + + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, + payerOldTokenAccount, + payer.publicKey, + oldMint, + oldTokenProgram, + ), + token.createMintToInstruction( + oldMint, + payerOldTokenAccount, + payer.publicKey, + oldSupply, + [], + oldTokenProgram, + ), + ); + + tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; + tx.feePayer = payer.publicKey; + tx.sign(payer); + + await banksClient.processTransaction(tx); + + return { oldMint, oldTokenProgram, payerOldTokenAccount }; +} From ee4eec7cd5d48e84b9c786ffcafeadec32c83c43 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 4 Aug 2026 16:35:43 +0200 Subject: [PATCH 05/31] fix default strict null-checks introduced by newer default tsconfig in Cursor --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index f7ad19bd9..85b5e1bd9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "strict": false, "types": [ "mocha", "chai" From 36418ab44b42abb28e5f0ffc410757e0dd5c9809 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 5 Aug 2026 19:59:31 +0200 Subject: [PATCH 06/31] amm fixtures --- Anchor.toml | 12 + sdk/src/constants.ts | 18 + tasks.md | 13 +- tests/fixtures/pump-fee-config | Bin 0 -> 4073 bytes tests/fixtures/pump-global-config | Bin 0 -> 940 bytes tests/fixtures/pump-global-volume-accumulator | Bin 0 -> 600 bytes tests/fixtures/pump_amm.so | Bin 0 -> 1013488 bytes tests/fixtures/pump_fees.so | Bin 0 -> 868280 bytes tests/fixtures/whirlpool-config | Bin 0 -> 108 bytes tests/fixtures/whirlpool-fee-tier | Bin 0 -> 44 bytes tests/fixtures/whirlpool.json | 5036 +++++++++++++++++ tests/fixtures/whirlpool.so | Bin 0 -> 1537784 bytes tests/fixtures/wsol-mint | Bin 0 -> 82 bytes tests/main.test.ts | 77 + tests/relaunch/main.test.ts | 2 + tests/relaunch/pumpAmm.ts | 607 ++ tests/relaunch/unit/venues.test.ts | 320 ++ tests/relaunch/whirlpool.ts | 389 ++ 18 files changed, 6462 insertions(+), 12 deletions(-) create mode 100644 tests/fixtures/pump-fee-config create mode 100644 tests/fixtures/pump-global-config create mode 100644 tests/fixtures/pump-global-volume-accumulator create mode 100644 tests/fixtures/pump_amm.so create mode 100644 tests/fixtures/pump_fees.so create mode 100644 tests/fixtures/whirlpool-config create mode 100644 tests/fixtures/whirlpool-fee-tier create mode 100644 tests/fixtures/whirlpool.json create mode 100644 tests/fixtures/whirlpool.so create mode 100644 tests/fixtures/wsol-mint create mode 100644 tests/relaunch/pumpAmm.ts create mode 100644 tests/relaunch/unit/venues.test.ts create mode 100644 tests/relaunch/whirlpool.ts diff --git a/Anchor.toml b/Anchor.toml index 018e15d57..e371f7eb6 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -94,3 +94,15 @@ program = "./tests/fixtures/openbook_twap.so" [[test.genesis]] address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" program = "./tests/fixtures/mpl_token_metadata.so" + +[[test.genesis]] +address = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" +program = "./tests/fixtures/pump_amm.so" + +[[test.genesis]] +address = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" +program = "./tests/fixtures/pump_fees.so" + +[[test.genesis]] +address = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc" +program = "./tests/fixtures/whirlpool.so" diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index 338fead3d..3a3070de0 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -112,6 +112,24 @@ export const DEVNET_RAYDIUM_AUTHORITY = PublicKey.findProgramAddressSync( DEVNET_RAYDIUM_CP_SWAP_PROGRAM_ID, )[0]; +/// The pump bonding-curve program. Canonical PumpSwap pools have the +/// `["pool-authority", mint]` PDA of this program as their creator. +export const PUMP_PROGRAM_ID = new PublicKey( + "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", +); + +export const PUMP_AMM_PROGRAM_ID = new PublicKey( + "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", +); + +export const PUMP_FEES_PROGRAM_ID = new PublicKey( + "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", +); + +export const WHIRLPOOL_PROGRAM_ID = new PublicKey( + "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", +); + export const DAMM_V2_PROGRAM_ID = new PublicKey( "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG", ); diff --git a/tasks.md b/tasks.md index bd759a6f6..9f5b426e8 100644 --- a/tasks.md +++ b/tasks.md @@ -23,22 +23,11 @@ ## Tasks -### Phase 0: Scaffold - -> Reference: `relaunch-implementation-plan.md` → "Stage 0 — scaffold" - -- [NEXT] 0.5 External fixtures + pool helpers - - Dump `pump_amm.so`, `pump_fees.so`, `whirlpool.so` from mainnet into `tests/fixtures/`; Anchor.toml entries - - `writePumpPool()`: fabricate pump_amm `Pool` + funded pool vaults (+ global/fee config accounts, dumped or fabricated) via bankrun `setAccount` — canonical-shaped WSOL- and USDC-quoted variants, one Token-2022-base, plus non-canonical variants for negative tests. Fabrication is deliberate even with the real program present — see plan Stage 0 note - - Whirlpool helpers via real instructions: config + fee tier + WSOL/USDC pool + seeded tick arrays (crib `whirlpools-cpi-examples` test setup) - - Test-feature override for the `USDC_SWAP_POOL` constant (fixture pool won't sit at the mainnet address) - - Verify: smoke test — pump_amm buy/sell against a fabricated pool and a Whirlpool swap both execute in bankrun - ### Phase 1: initialize_relaunch + start_deposits > Reference: `relaunch-implementation-plan.md` → "Stage 1 — initialize_relaunch + start_deposits" -- [ ] 1.1 `initialize_relaunch` (instruction + SDK + tests) +- [NEXT] 1.1 `initialize_relaunch` (instruction + SDK + tests) - Accounts/validations per plan §1: `mint_authority` Signer proof-of-control, new-mint checks, source-pool canonicality, quote mint ∈ {WSOL, USDC}, old-mint extension allowlist, threshold/window/spending-limit validations; USDC-quoted sources share one ATA for `source_quote_vault`/`usdc_vault` - Handler order: `set_authority` → metadata CPI → `mint_to` 12M → snapshot old supply → `set_inner` → event - SDK: `initializeRelaunch()` with create-mint-to-self pre-instructions diff --git a/tests/fixtures/pump-fee-config b/tests/fixtures/pump-fee-config new file mode 100644 index 0000000000000000000000000000000000000000..4f3bed9db004e6cb92f20ed75f90827b7c4eeac5 GIT binary patch literal 4073 zcmeA-nY8C?^(5%Ry;|jL#<%VQe5R0%gO@jEBj+x$G3i2;*R?i-Ji>XF5%aE1!forQ(XW|YDK#YD+`Q+sm>cFHDgZdI}R8JQ=J=3>O|R_bY2(-Q=KDB zO2k$Ez95W)sm=x_l^`4xCI;hRsxya4t$EmTRtmx{~!Hqbewq%VCOcSO$czWUadQI67w>lY^RK#J<*`_cKrg`u* z+A(*6h!t*i;xMTz*@wIBU>r>IVCjtE)fGc#XWZ(9U{X08npN&F4yJkhFsU8aqCdhD z8KydTQc{>{l?G3=nCiG-=9$!sJ_>1)RM}$(`dA)DV9=Lmf z>S199GYg*Acck2OMKTXv9Xzjh1Z9=rQ3ucK5e~l>;ZX+fqUX!r4lFcml$-7oOJ_6!}ST;x-SS*EfhOGr|)trg`wZeqhh{ zmw42{)98i7Gncq=+Y8U@57-v&=E1EF7ETNwR+lb8O3*NISkS}sIzymWFCT96;CWr( zn_L;3gXv$mRSL5|#|hvz4;~Z-4kz64)FJSwa(Ft&0O44S^aAq_!p2e7Xb6mkz-S1J OhQMeDjE2Ba4FLdT!iMSq literal 0 HcmV?d00001 diff --git a/tests/fixtures/pump-global-config b/tests/fixtures/pump-global-config new file mode 100644 index 0000000000000000000000000000000000000000..05b6fadebfa495d5a3148187ccfed4bafb66288e GIT binary patch literal 940 zcmV;d15^B!2%O5G{IJ>6yNs(e9No&j=>d`LKsYpR#-~`Bg z>zMjnUf2!_7NwUX%IyxbT;o!*Ug@ z#j-|+d#J<71poj5000002O(XVKzTNl;T8sL*CYeO?~7n9LAcKNE?^W1$4Uv6~)f*|=qXK(`} zdrfOaH(9i$#*np~HQBoudI=1@Dl?dRp1TugnX6fnTNCBni!g!T09~{v!AFmzZ}`JU zpmga}KR!32&_*J9-OEm#cT7Yjj6?16N~o&g@B$QOTiQ%G&26_sk6E+`yPm_e%AShR z@$yDf#F@2uY#`hDvn}18>OVQNauHl{gnSr9&!slTg$GUp4n)u)-ah%`Tuirs@Q&jC z=u7NGYFBtA+Gx}{`ifnnu*J{y4`cLHYXL)^We~tUW%}7M=$!<<3%3nXmmg8Y75g+y z2irlPAtC}JRTili6HI4Vj7c~kt`%PCW@T4;!M7ONN8QgV1rFw?;vi_dh-3<-e~^<1 z#vku-8UbVVr&YjkyoL=QV`m_I?Alw*Y_aO_@oi{8MP{*$R?Q zLHJ2n+;gXDh1hj(RlqFT%89xeGI2!j@EM)=k5yhJT literal 0 HcmV?d00001 diff --git a/tests/fixtures/pump-global-volume-accumulator b/tests/fixtures/pump-global-volume-accumulator new file mode 100644 index 0000000000000000000000000000000000000000..69f29718d3315c9f0898f237228cdcfcf1174eae GIT binary patch literal 600 XcmX@5^-a5PpWJ_jQ7{?;R15(CdJF`A literal 0 HcmV?d00001 diff --git a/tests/fixtures/pump_amm.so b/tests/fixtures/pump_amm.so new file mode 100644 index 0000000000000000000000000000000000000000..f87cb45a264d9f91da4a0a081f752a68506a7fba GIT binary patch literal 1013488 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�W~ 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

IKM4)m3wZ*qQ_@FM8V%@BqkBKO-FTqv*L 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 literal 0 HcmV?d00001 diff --git a/tests/fixtures/pump_fees.so b/tests/fixtures/pump_fees.so new file mode 100644 index 0000000000000000000000000000000000000000..aa4ea3b84005f40274308d64cbe35bea027866f9 GIT binary patch literal 868280 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 bf4{ZQ^~b)<`%#{DN&Th9OgE`wuA literal 0 HcmV?d00001 diff --git a/tests/fixtures/whirlpool-config b/tests/fixtures/whirlpool-config new file mode 100644 index 0000000000000000000000000000000000000000..97f4f21a5e27c27b45031df63d2109b1772fb729 GIT binary patch literal 108 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%0DLjLHkIfV249+M!4E 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 z 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 xKb~cO { + const globalConfig = await banksClient.getAccount(PUMP_GLOBAL_CONFIG); + const data = Buffer.from(globalConfig!.data); + const recipients: PublicKey[] = []; + for (let i = 0; i < 8; i++) { + const recipient = new PublicKey( + data.subarray(offset + i * 32, offset + (i + 1) * 32), + ); + if (!recipient.equals(PublicKey.default)) { + recipients.push(recipient); + } + } + return recipients; +} + +// GlobalConfig layout +// +// offset size field +// 0 8 discriminator +// 8 32 admin +// 40 8 lp_fee_basis_points +// 48 8 protocol_fee_basis_points +// 56 1 disable_flags +// 57 256 protocol_fee_recipients [Pubkey; 8] <- getProtocolFeeRecipient +// 313 8 coin_creator_fee_basis_points +// 321 32 admin_set_coin_creator_authority +// 353 32 whitelist_pda +// 385 32 reserved_fee_recipient +// 417 1 mayhem_mode_enabled +// 418 224 reserved_fee_recipients [Pubkey; 7] +// 642 1 is_cashback_enabled +// 643 256 buyback_fee_recipients [Pubkey; 8] <- getBuybackFeeRecipients +// 899 8 buyback_basis_points +// 907 32 boost_authority +// 939 1 boost_enabled +// 940 total +export async function getProtocolFeeRecipient( + banksClient: BanksClient, +): Promise { + const recipients = await globalConfigRecipients(banksClient, 57); + if (recipients.length === 0) { + throw new Error("no protocol fee recipient in global config"); + } + return recipients[0]; +} + +export async function getBuybackFeeRecipients( + banksClient: BanksClient, +): Promise { + return globalConfigRecipients(banksClient, 643); +} + +function packTokenAccount({ + mint, + owner, + amount, +}: { + mint: PublicKey; + owner: PublicKey; + amount: bigint; +}): Buffer { + const isNative = mint.equals(token.NATIVE_MINT); + const data = Buffer.alloc(token.ACCOUNT_SIZE); + token.AccountLayout.encode( + { + mint, + owner, + amount, + delegateOption: 0, + delegate: PublicKey.default, + state: token.AccountState.Initialized, + isNativeOption: isNative ? 1 : 0, + isNative: isNative ? TOKEN_ACCOUNT_RENT : 0n, + delegatedAmount: 0n, + closeAuthorityOption: 0, + closeAuthority: PublicKey.default, + }, + data, + ); + return data; +} + +// Writes a token account at `address` via setAccount. WSOL accounts get +// amount-backed lamports so the token program can move lamports alongside +// native transfers. +export function writeTokenAccount( + context: ProgramTestContext, + { + address, + mint, + owner, + amount, + tokenProgram = token.TOKEN_PROGRAM_ID, + }: { + address: PublicKey; + mint: PublicKey; + owner: PublicKey; + amount: bigint; + tokenProgram?: PublicKey; + }, +) { + const isNative = mint.equals(token.NATIVE_MINT); + context.setAccount(address, { + data: packTokenAccount({ mint, owner, amount }), + owner: tokenProgram, + lamports: Number(TOKEN_ACCOUNT_RENT + (isNative ? amount : 0n)), + executable: false, + }); +} + +export type WritePumpPoolParams = { + context: ProgramTestContext; + baseMint: PublicKey; + quoteMint: PublicKey; + baseReserve: bigint; + quoteReserve: bigint; + baseTokenProgram?: PublicKey; + // Overrides below fabricate non-canonical pools for negative tests. + index?: number; + creator?: PublicKey; + owner?: PublicKey; + coinCreator?: PublicKey; +}; + +export type PumpPool = { + pool: PublicKey; + baseMint: PublicKey; + quoteMint: PublicKey; + baseTokenProgram: PublicKey; + poolBaseTokenAccount: PublicKey; + poolQuoteTokenAccount: PublicKey; + coinCreator: PublicKey; +}; + +// Pool account layout +// +// offset size field type +// 0 8 discriminator [241,154,109,4,17,177,109,188] +// 8 1 pool_bump u8 +// 9 2 index u16 LE (0 = canonical) +// 11 32 creator Pubkey (pool-authority PDA when canonical) +// 43 32 base_mint Pubkey +// 75 32 quote_mint Pubkey +// 107 32 lp_mint Pubkey (["pool_lp_mint", pool] PDA) +// 139 32 pool_base_token_account Pubkey (ATA of pool, base token program) +// 171 32 pool_quote_token_account Pubkey (ATA of pool, classic SPL) +// 203 8 lp_supply u64 LE (unread by swaps) +// 211 32 coin_creator Pubkey +// 243 1 is_mayhem_mode bool +// 244 1 is_cashback_coin bool +// 245 16 virtual_quote_reserves i128 LE (0 = legacy behavior) +// 261 40 (reserved) zero on mainnet pools +// 301 total +// +// Fabricates a pump_amm Pool plus funded vaults directly via setAccount. +// Deliberate even with the real program loaded: only the pump bonding-curve +// program can sign for the canonical creator PDA, so a real create_pool can +// never produce the canonical fingerprint in tests. +export async function writePumpPool({ + context, + baseMint, + quoteMint, + baseReserve, + quoteReserve, + baseTokenProgram = token.TOKEN_PROGRAM_ID, + index = 0, + creator, + owner = PUMP_AMM_PROGRAM_ID, + coinCreator = PUMP_TEST_COIN_CREATOR, +}: WritePumpPoolParams): Promise { + creator = creator ?? getPumpPoolAuthorityAddr(baseMint); + const [pool, poolBump] = getPumpPoolAddr({ + index, + creator, + baseMint, + quoteMint, + }); + + const poolBaseTokenAccount = token.getAssociatedTokenAddressSync( + baseMint, + pool, + true, + baseTokenProgram, + ); + const poolQuoteTokenAccount = token.getAssociatedTokenAddressSync( + quoteMint, + pool, + true, + ); + const lpMint = PublicKey.findProgramAddressSync( + [Buffer.from("pool_lp_mint"), pool.toBuffer()], + PUMP_AMM_PROGRAM_ID, + )[0]; + + const data = Buffer.alloc(301); + let offset = 0; + Buffer.from([241, 154, 109, 4, 17, 177, 109, 188]).copy(data, offset); // Pool discriminator + offset += 8; + data.writeUInt8(poolBump, offset); + offset += 1; + data.writeUInt16LE(index, offset); + offset += 2; + for (const key of [ + creator, + baseMint, + quoteMint, + lpMint, + poolBaseTokenAccount, + poolQuoteTokenAccount, + ]) { + key.toBuffer().copy(data, offset); + offset += 32; + } + data.writeBigUInt64LE(1_000_000_000n, offset); // lp_supply, unread by swaps + offset += 8; + coinCreator.toBuffer().copy(data, offset); + offset += 32; + // is_mayhem_mode, is_cashback_coin, virtual_quote_reserves and the 40 + // reserved tail bytes stay zeroed. + + context.setAccount(pool, { + data, + owner, + lamports: 2_985_840, + executable: false, + }); + + writeTokenAccount(context, { + address: poolBaseTokenAccount, + mint: baseMint, + owner: pool, + amount: baseReserve, + tokenProgram: baseTokenProgram, + }); + writeTokenAccount(context, { + address: poolQuoteTokenAccount, + mint: quoteMint, + owner: pool, + amount: quoteReserve, + }); + + // Fee destinations must exist before swaps: the coin creator's quote vault + // ATA and the protocol fee recipient's quote ATA. Skip any that another + // fabricated pool already wrote so accrued balances survive. + const creatorVaultAta = token.getAssociatedTokenAddressSync( + quoteMint, + getCreatorVaultAuthorityAddr(coinCreator), + true, + ); + if (!(await context.banksClient.getAccount(creatorVaultAta))) { + writeTokenAccount(context, { + address: creatorVaultAta, + mint: quoteMint, + owner: getCreatorVaultAuthorityAddr(coinCreator), + amount: 0n, + }); + } + for (const recipient of [ + await getProtocolFeeRecipient(context.banksClient), + ...(await getBuybackFeeRecipients(context.banksClient)), + ]) { + const recipientAta = token.getAssociatedTokenAddressSync( + quoteMint, + recipient, + true, + ); + if (!(await context.banksClient.getAccount(recipientAta))) { + writeTokenAccount(context, { + address: recipientAta, + mint: quoteMint, + owner: recipient, + amount: 0n, + }); + } + } + + return { + pool, + baseMint, + quoteMint, + baseTokenProgram, + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreator, + }; +} + +function swapAccountMetas( + pool: PumpPool, + user: PublicKey, + protocolFeeRecipient: PublicKey, +) { + const userBaseTokenAccount = token.getAssociatedTokenAddressSync( + pool.baseMint, + user, + true, + pool.baseTokenProgram, + ); + const userQuoteTokenAccount = token.getAssociatedTokenAddressSync( + pool.quoteMint, + user, + true, + ); + const protocolFeeRecipientTokenAccount = token.getAssociatedTokenAddressSync( + pool.quoteMint, + protocolFeeRecipient, + true, + ); + const coinCreatorVaultAuthority = getCreatorVaultAuthorityAddr( + pool.coinCreator, + ); + const coinCreatorVaultAta = token.getAssociatedTokenAddressSync( + pool.quoteMint, + coinCreatorVaultAuthority, + true, + ); + + return [ + { pubkey: pool.pool, isSigner: false, isWritable: true }, + { pubkey: user, isSigner: true, isWritable: true }, + { pubkey: PUMP_GLOBAL_CONFIG, isSigner: false, isWritable: false }, + { pubkey: pool.baseMint, isSigner: false, isWritable: false }, + { pubkey: pool.quoteMint, isSigner: false, isWritable: false }, + { pubkey: userBaseTokenAccount, isSigner: false, isWritable: true }, + { pubkey: userQuoteTokenAccount, isSigner: false, isWritable: true }, + { pubkey: pool.poolBaseTokenAccount, isSigner: false, isWritable: true }, + { pubkey: pool.poolQuoteTokenAccount, isSigner: false, isWritable: true }, + { pubkey: protocolFeeRecipient, isSigner: false, isWritable: false }, + { + pubkey: protocolFeeRecipientTokenAccount, + isSigner: false, + isWritable: true, + }, + { pubkey: pool.baseTokenProgram, isSigner: false, isWritable: false }, + { pubkey: token.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + { + pubkey: token.ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { pubkey: PUMP_EVENT_AUTHORITY, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false }, + { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true }, + { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false }, + ]; +} + +// sell(base_amount_in, min_quote_amount_out) — 21 accounts plus the same +// remaining-account tail as buy: pool_v2, then a buyback fee recipient with +// its quote ATA (mirrors pump's own SDK; required once buyback fees have +// accrued). +export function pumpSellIx({ + pool, + user, + protocolFeeRecipient, + buybackFeeRecipient, + baseAmountIn, + minQuoteAmountOut, +}: { + pool: PumpPool; + user: PublicKey; + protocolFeeRecipient: PublicKey; + buybackFeeRecipient: PublicKey; + baseAmountIn: bigint; + minQuoteAmountOut: bigint; +}): TransactionInstruction { + const data = Buffer.alloc(8 + 8 + 8); + Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]).copy(data, 0); + data.writeBigUInt64LE(baseAmountIn, 8); + data.writeBigUInt64LE(minQuoteAmountOut, 16); + + const keys = [ + ...swapAccountMetas(pool, user, protocolFeeRecipient), + { pubkey: PUMP_FEE_CONFIG, isSigner: false, isWritable: false }, + { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false }, + { + pubkey: getPoolV2Addr(pool.baseMint), + isSigner: false, + isWritable: false, + }, + { pubkey: buybackFeeRecipient, isSigner: false, isWritable: false }, + { + pubkey: token.getAssociatedTokenAddressSync( + pool.quoteMint, + buybackFeeRecipient, + true, + ), + isSigner: false, + isWritable: true, + }, + ]; + + return new TransactionInstruction({ + programId: PUMP_AMM_PROGRAM_ID, + keys, + data, + }); +} + +// buy(base_amount_out, max_quote_amount_in, track_volume) — sell's accounts +// plus the two volume accumulators, 23 total, then the remaining accounts: +// pool_v2 and (required for buys, unlike sells) one buyback fee recipient +// with its quote ATA. Any member of the global config's buyback list works. +export function pumpBuyIx({ + pool, + user, + protocolFeeRecipient, + buybackFeeRecipient, + baseAmountOut, + maxQuoteAmountIn, + trackVolume = false, +}: { + pool: PumpPool; + user: PublicKey; + protocolFeeRecipient: PublicKey; + buybackFeeRecipient: PublicKey; + baseAmountOut: bigint; + maxQuoteAmountIn: bigint; + trackVolume?: boolean; +}): TransactionInstruction { + const data = Buffer.alloc(8 + 8 + 8 + 1); + Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]).copy(data, 0); + data.writeBigUInt64LE(baseAmountOut, 8); + data.writeBigUInt64LE(maxQuoteAmountIn, 16); + data.writeUInt8(trackVolume ? 1 : 0, 24); + + const keys = [ + ...swapAccountMetas(pool, user, protocolFeeRecipient), + { + pubkey: PUMP_GLOBAL_VOLUME_ACCUMULATOR, + isSigner: false, + isWritable: false, + }, + { + pubkey: getUserVolumeAccumulatorAddr(user), + isSigner: false, + isWritable: true, + }, + { pubkey: PUMP_FEE_CONFIG, isSigner: false, isWritable: false }, + { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false }, + { + pubkey: getPoolV2Addr(pool.baseMint), + isSigner: false, + isWritable: false, + }, + { pubkey: buybackFeeRecipient, isSigner: false, isWritable: false }, + { + pubkey: token.getAssociatedTokenAddressSync( + pool.quoteMint, + buybackFeeRecipient, + true, + ), + isSigner: false, + isWritable: true, + }, + ]; + + return new TransactionInstruction({ + programId: PUMP_AMM_PROGRAM_ID, + keys, + data, + }); +} + +export function pumpInitUserVolumeAccumulatorIx({ + payer, + user, +}: { + payer: PublicKey; + user: PublicKey; +}): TransactionInstruction { + return new TransactionInstruction({ + programId: PUMP_AMM_PROGRAM_ID, + keys: [ + { pubkey: payer, isSigner: true, isWritable: true }, + { pubkey: user, isSigner: false, isWritable: false }, + { + pubkey: getUserVolumeAccumulatorAddr(user), + isSigner: false, + isWritable: true, + }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + { pubkey: PUMP_EVENT_AUTHORITY, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false }, + ], + data: Buffer.from([94, 6, 202, 115, 255, 96, 232, 183]), + }); +} diff --git a/tests/relaunch/unit/venues.test.ts b/tests/relaunch/unit/venues.test.ts new file mode 100644 index 000000000..f3496d6fc --- /dev/null +++ b/tests/relaunch/unit/venues.test.ts @@ -0,0 +1,320 @@ +import { PublicKey, Transaction } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BankrunProvider } from "anchor-bankrun"; +import { BanksClient } from "solana-bankrun"; +import { setupRelaunch } from "../utils.js"; +import { + getBuybackFeeRecipients, + getCanonicalPumpPoolAddr, + getCreatorVaultAuthorityAddr, + getProtocolFeeRecipient, + getUserVolumeAccumulatorAddr, + pumpBuyIx, + pumpInitUserVolumeAccumulatorIx, + pumpSellIx, + writePumpPool, + PumpPool, +} from "../pumpAmm.js"; +import { + FIXTURE_USDC_SWAP_POOL, + setupWhirlpool, + whirlpoolSwapV2Ix, + wrapSol, +} from "../whirlpool.js"; +import { MAINNET_USDC } from "@metadaoproject/programs"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC +const SELL_AMOUNT = 10_000n * 10n ** 6n; // 10k old tokens + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount( + address, + { ...raw, data: Buffer.from(raw.data) } as any, + tokenProgram, + ).amount; +} + +export default function suite() { + before(function () { + this.bankrunProvider = new BankrunProvider(this.context); + }); + + const sellIntoPumpPool = async function ( + this: Mocha.Context, + pool: PumpPool, + quoteTokenAccount: PublicKey, + ) { + const protocolFeeRecipient = await getProtocolFeeRecipient( + this.banksClient, + ); + const buybackFeeRecipient = ( + await getBuybackFeeRecipients(this.banksClient) + )[0]; + const feeAtas = [ + protocolFeeRecipient, + buybackFeeRecipient, + getCreatorVaultAuthorityAddr(pool.coinCreator), + ].map((owner) => + token.getAssociatedTokenAddressSync(pool.quoteMint, owner, true), + ); + const payerOldTokenAccount = token.getAssociatedTokenAddressSync( + pool.baseMint, + this.payer.publicKey, + true, + pool.baseTokenProgram, + ); + + const baseBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + pool.baseTokenProgram, + ); + const quoteBefore = await tokenBalance(this.banksClient, quoteTokenAccount); + const poolQuoteBefore = await tokenBalance( + this.banksClient, + pool.poolQuoteTokenAccount, + ); + const feesBefore = await Promise.all( + feeAtas.map((ata) => tokenBalance(this.banksClient, ata)), + ); + + const tx = new Transaction().add( + pumpSellIx({ + pool, + user: this.payer.publicKey, + protocolFeeRecipient, + buybackFeeRecipient, + baseAmountIn: SELL_AMOUNT, + minQuoteAmountOut: 0n, + }), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + const baseAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + pool.baseTokenProgram, + ); + const quoteAfter = await tokenBalance(this.banksClient, quoteTokenAccount); + const poolBaseAfter = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + pool.baseTokenProgram, + ); + + assert.equal((baseBefore - baseAfter).toString(), SELL_AMOUNT.toString()); + assert.equal( + poolBaseAfter.toString(), + (POOL_BASE_RESERVE + SELL_AMOUNT).toString(), + ); + + // ~0.99 quote units gross on these reserves; fees shave a little more. + const quoteReceived = quoteAfter - quoteBefore; + assert.isTrue(quoteReceived > 900_000_000n && quoteReceived < 990_099_010n); + + // Everything the pool paid out landed with the seller or the fee ATAs. + const poolQuoteAfter = await tokenBalance( + this.banksClient, + pool.poolQuoteTokenAccount, + ); + const feesAfter = await Promise.all( + feeAtas.map((ata) => tokenBalance(this.banksClient, ata)), + ); + const feesPaid = feesAfter.reduce( + (sum, after, i) => sum + (after - feesBefore[i]), + 0n, + ); + assert.equal( + (poolQuoteBefore - poolQuoteAfter).toString(), + (quoteReceived + feesPaid).toString(), + ); + + return quoteReceived; + }; + + it("pump_amm sell and buy execute against a fabricated WSOL-quoted pool", async function () { + const { oldMint, oldTokenProgram } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + assert.isTrue( + pool.pool.equals(getCanonicalPumpPoolAddr(oldMint, token.NATIVE_MINT)[0]), + ); + + const wsolAta = await wrapSol(this.bankrunProvider, this.payer, 0n); + await sellIntoPumpPool.call(this, pool, wsolAta); + + // Exact-output buy of the same 10k tokens, capped by max_quote_in. + const protocolFeeRecipient = await getProtocolFeeRecipient( + this.banksClient, + ); + await wrapSol(this.bankrunProvider, this.payer, 2n * 10n ** 9n); + + const payerOldTokenAccount = token.getAssociatedTokenAddressSync( + oldMint, + this.payer.publicKey, + true, + oldTokenProgram, + ); + const baseBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + oldTokenProgram, + ); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const buyTx = new Transaction(); + if ( + !(await this.banksClient.getAccount( + getUserVolumeAccumulatorAddr(this.payer.publicKey), + )) + ) { + buyTx.add( + pumpInitUserVolumeAccumulatorIx({ + payer: this.payer.publicKey, + user: this.payer.publicKey, + }), + ); + } + buyTx.add( + pumpBuyIx({ + pool, + user: this.payer.publicKey, + protocolFeeRecipient, + buybackFeeRecipient: ( + await getBuybackFeeRecipients(this.banksClient) + )[0], + baseAmountOut: SELL_AMOUNT, + maxQuoteAmountIn: 2n * 10n ** 9n, + }), + ); + buyTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + buyTx.feePayer = this.payer.publicKey; + buyTx.sign(this.payer); + await this.banksClient.processTransaction(buyTx); + + const baseAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + oldTokenProgram, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + + assert.equal((baseAfter - baseBefore).toString(), SELL_AMOUNT.toString()); + const wsolSpent = wsolBefore - wsolAfter; + assert.isTrue(wsolSpent > 900_000_000n && wsolSpent <= 2n * 10n ** 9n); + }); + + it("pump_amm sell executes against a fabricated USDC-quoted pool", async function () { + const { oldMint, oldTokenProgram } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: MAINNET_USDC, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + + const usdcAta = token.getAssociatedTokenAddressSync( + MAINNET_USDC, + this.payer.publicKey, + ); + await sellIntoPumpPool.call(this, pool, usdcAta); + }); + + it("pump_amm sell executes against a Token-2022-base pool", async function () { + const { oldMint, oldTokenProgram } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + + const wsolAta = await wrapSol(this.bankrunProvider, this.payer, 0n); + await sellIntoPumpPool.call(this, pool, wsolAta); + }); + + it("whirlpool fixture pool sits at the pinned address and swaps WSOL to USDC", async function () { + const fixture = await setupWhirlpool({ + provider: this.bankrunProvider, + payer: this.payer, + }); + + // The relaunch program's usdc_swap_pool constant pins this exact + // address — the fixture pool recreates the mainnet pool's PDA because + // it derives from the dumped mainnet config. + assert.equal( + fixture.whirlpool.toBase58(), + "Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE", + ); + assert.isTrue(fixture.whirlpool.equals(FIXTURE_USDC_SWAP_POOL)); + + const wsolAta = await wrapSol(this.bankrunProvider, this.payer, 10n ** 9n); + const usdcAta = token.getAssociatedTokenAddressSync( + MAINNET_USDC, + this.payer.publicKey, + ); + + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + const usdcBefore = await tokenBalance(this.banksClient, usdcAta); + + const swapIx = await whirlpoolSwapV2Ix(fixture, { + tokenAuthority: this.payer.publicKey, + tokenOwnerAccountA: wsolAta, + tokenOwnerAccountB: usdcAta, + amountIn: 10n ** 9n, // 1 SOL + minAmountOut: 95n * 10n ** 6n, + aToB: true, + }); + const tx = new Transaction().add(swapIx); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + const usdcAfter = await tokenBalance(this.banksClient, usdcAta); + + assert.equal((wsolBefore - wsolAfter).toString(), (10n ** 9n).toString()); + // ~100 USDC per SOL at the default sqrt price, minus the 0.04% fee and + // a little price impact. + const usdcReceived = usdcAfter - usdcBefore; + assert.isTrue( + usdcReceived > 95n * 10n ** 6n && usdcReceived < 100n * 10n ** 6n, + ); + }); +} diff --git a/tests/relaunch/whirlpool.ts b/tests/relaunch/whirlpool.ts new file mode 100644 index 000000000..50367c557 --- /dev/null +++ b/tests/relaunch/whirlpool.ts @@ -0,0 +1,389 @@ +import * as anchor from "@coral-xyz/anchor"; +import { + ComputeBudgetProgram, + Keypair, + PublicKey, + Signer, + SystemProgram, + SYSVAR_RENT_PUBKEY, + Transaction, + TransactionInstruction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; +import { BN } from "bn.js"; +import * as fs from "fs"; +import { MAINNET_USDC, WHIRLPOOL_PROGRAM_ID } from "@metadaoproject/programs"; + +// The mainnet WhirlpoolsConfig the pinned SOL/USDC pool lives under, loaded +// into bankrun as a dumped fixture (initialize_config is admin-gated on the +// current program, and reusing the real config makes the fixture pool's PDA +// land at the exact mainnet USDC_SWAP_POOL address). +export const WHIRLPOOLS_CONFIG = new PublicKey( + "2LecshUwdy9xi7meFgHtFJQNSKk4KdTrcpvaB56dP2NQ", +); + +// Mirrors the pinned mainnet SOL/USDC 0.04% pool's parameters. +export const TICK_SPACING = 4; + +const TICKS_PER_ARRAY = 88; +const TICK_ARRAY_SPAN = TICK_SPACING * TICKS_PER_ARRAY; +// Whirlpool's global tick bounds; both are divisible by TICK_SPACING, so a +// full-range position can sit exactly on them. +const MIN_TICK = -443636; +const MAX_TICK = 443636; + +export const MIN_SQRT_PRICE = 4_295_048_016n; +export const MAX_SQRT_PRICE = 79226673515401279992447579055n; + +function bigintSqrt(n: bigint): bigint { + let x = n; + let y = (x + 1n) / 2n; + while (y < x) { + x = y; + y = (x + n / x) / 2n; + } + return x; +} + +// sqrt(0.1) << 64: raw price 0.1 USDC-per-lamport-ish units — i.e. 100 USDC +// per SOL at 9/6 decimals. +export const DEFAULT_SQRT_PRICE = bigintSqrt((1n << 128n) / 10n); + +export function getWhirlpoolAddr({ + config = WHIRLPOOLS_CONFIG, + tokenMintA = token.NATIVE_MINT, + tokenMintB = MAINNET_USDC, + tickSpacing = TICK_SPACING, +}: { + config?: PublicKey; + tokenMintA?: PublicKey; + tokenMintB?: PublicKey; + tickSpacing?: number; +} = {}): PublicKey { + const spacingBuf = Buffer.alloc(2); + spacingBuf.writeUInt16LE(tickSpacing); + return PublicKey.findProgramAddressSync( + [ + Buffer.from("whirlpool"), + config.toBuffer(), + tokenMintA.toBuffer(), + tokenMintB.toBuffer(), + spacingBuf, + ], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +// Identical to the relaunch program's `usdc_swap_pool` constant: the pool +// PDA under the dumped mainnet config is the mainnet pool address. +export const FIXTURE_USDC_SWAP_POOL = getWhirlpoolAddr(); + +export function getFeeTierAddr(config: PublicKey): PublicKey { + const spacingBuf = Buffer.alloc(2); + spacingBuf.writeUInt16LE(TICK_SPACING); + return PublicKey.findProgramAddressSync( + [Buffer.from("fee_tier"), config.toBuffer(), spacingBuf], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +// The mainnet fee tier for tick spacing 4 (0.04%), also a dumped fixture. +export const WHIRLPOOL_FEE_TIER = getFeeTierAddr(WHIRLPOOLS_CONFIG); + +export function getOracleAddr(whirlpool: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("oracle"), whirlpool.toBuffer()], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +export function getTickArrayAddr( + whirlpool: PublicKey, + startTickIndex: number, +): PublicKey { + return PublicKey.findProgramAddressSync( + [ + Buffer.from("tick_array"), + whirlpool.toBuffer(), + Buffer.from(startTickIndex.toString()), + ], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +export function getPositionAddr(positionMint: PublicKey): [PublicKey, number] { + return PublicKey.findProgramAddressSync( + [Buffer.from("position"), positionMint.toBuffer()], + WHIRLPOOL_PROGRAM_ID, + ); +} + +export function startTickIndex(tick: number): number { + return Math.floor(tick / TICK_ARRAY_SPAN) * TICK_ARRAY_SPAN; +} + +function sqrtPriceToTick(sqrtPrice: bigint): number { + const sqrtPriceFloat = Number(sqrtPrice) / 2 ** 64; + return Math.floor((2 * Math.log(sqrtPriceFloat)) / Math.log(1.0001)); +} + +export function whirlpoolProgram( + provider: anchor.AnchorProvider, +): anchor.Program { + const idl = JSON.parse( + fs.readFileSync("./tests/fixtures/whirlpool.json", "utf-8"), + ); + return new anchor.Program(idl, WHIRLPOOL_PROGRAM_ID, provider); +} + +export async function wrapSol( + provider: anchor.AnchorProvider, + payer: Signer, + lamports: bigint, +): Promise { + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + payer.publicKey, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, + wsolAta, + payer.publicKey, + token.NATIVE_MINT, + ), + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: wsolAta, + lamports: Number(lamports), + }), + token.createSyncNativeInstruction(wsolAta), + ); + await provider.sendAndConfirm!(tx, [payer]); + return wsolAta; +} + +export type WhirlpoolFixture = { + program: anchor.Program; + config: PublicKey; + whirlpool: PublicKey; + oracle: PublicKey; + tokenVaultA: PublicKey; + tokenVaultB: PublicKey; + tickArrayStarts: number[]; +}; + +// Builds a real WSOL/USDC whirlpool through the program's own instructions +// under the dumped mainnet config: pool + tick arrays + a full-range +// position. The payer funds both sides (WSOL is wrapped here; USDC must +// already be in the payer's ATA). +export async function setupWhirlpool({ + provider, + payer, + sqrtPrice = DEFAULT_SQRT_PRICE, + solAmount = 1_000n * 10n ** 9n, + usdcAmount = 100_000n * 10n ** 6n, +}: { + provider: anchor.AnchorProvider; + payer: Signer; + sqrtPrice?: bigint; + solAmount?: bigint; + usdcAmount?: bigint; +}): Promise { + const program = whirlpoolProgram(provider); + const config = WHIRLPOOLS_CONFIG; + const feeTier = WHIRLPOOL_FEE_TIER; + const whirlpool = getWhirlpoolAddr({ config }); + const oracle = getOracleAddr(whirlpool); + const tokenVaultA = Keypair.generate(); + const tokenVaultB = Keypair.generate(); + + await program.methods + .initializePool( + { whirlpoolBump: 0 }, // ignored by the program; PDA is re-derived + TICK_SPACING, + new BN(sqrtPrice.toString()), + ) + .accounts({ + whirlpoolsConfig: config, + tokenMintA: token.NATIVE_MINT, + tokenMintB: MAINNET_USDC, + funder: payer.publicKey, + whirlpool, + tokenVaultA: tokenVaultA.publicKey, + tokenVaultB: tokenVaultB.publicKey, + feeTier, + tokenProgram: token.TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + rent: SYSVAR_RENT_PUBKEY, + }) + .signers([tokenVaultA, tokenVaultB]) + .rpc(); + + // Seed tick arrays around the current price (three on each side keeps + // moderate swaps in both directions inside initialized arrays) plus the + // two arrays holding the full-range position's boundary ticks. + const currentStart = startTickIndex(sqrtPriceToTick(sqrtPrice)); + const tickArrayStarts = [ + ...[-3, -2, -1, 0, 1, 2, 3].map((k) => currentStart + k * TICK_ARRAY_SPAN), + startTickIndex(MIN_TICK), + startTickIndex(MAX_TICK), + ].filter((start, i, all) => all.indexOf(start) === i); + + const tickArrayIxs: TransactionInstruction[] = []; + for (const start of tickArrayStarts) { + tickArrayIxs.push( + await program.methods + .initializeTickArray(start) + .accounts({ + whirlpool, + funder: payer.publicKey, + tickArray: getTickArrayAddr(whirlpool, start), + systemProgram: SystemProgram.programId, + }) + .instruction(), + ); + } + const tickArrayTx = new Transaction().add( + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), + ...tickArrayIxs, + ); + await provider.sendAndConfirm!(tickArrayTx, [payer]); + + // Full-range position funded by the payer. + const positionMint = Keypair.generate(); + const [position, positionBump] = getPositionAddr(positionMint.publicKey); + const positionTokenAccount = token.getAssociatedTokenAddressSync( + positionMint.publicKey, + payer.publicKey, + ); + + await program.methods + .openPosition({ positionBump }, MIN_TICK, MAX_TICK) + .accounts({ + funder: payer.publicKey, + owner: payer.publicKey, + position, + positionMint: positionMint.publicKey, + positionTokenAccount, + whirlpool, + tokenProgram: token.TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + rent: SYSVAR_RENT_PUBKEY, + associatedTokenProgram: token.ASSOCIATED_TOKEN_PROGRAM_ID, + }) + .signers([positionMint]) + .rpc(); + + // For a full-range position, amountA ≈ L / sqrtP and amountB ≈ L * sqrtP + // (float sqrtP). The program computes exact requirements from on-chain + // state; the 1% headroom on the maxes absorbs rounding. + const sqrtPriceFloat = Number(sqrtPrice) / 2 ** 64; + const liquidity = BigInt( + Math.floor( + Math.min( + Number(solAmount) * sqrtPriceFloat, + Number(usdcAmount) / sqrtPriceFloat, + ), + ), + ); + const tokenMaxA = (solAmount * 101n) / 100n; + const tokenMaxB = (usdcAmount * 101n) / 100n; + + const wsolAta = await wrapSol(provider, payer, tokenMaxA); + const usdcAta = token.getAssociatedTokenAddressSync( + MAINNET_USDC, + payer.publicKey, + ); + + await program.methods + .increaseLiquidity( + new BN(liquidity.toString()), + new BN(tokenMaxA.toString()), + new BN(tokenMaxB.toString()), + ) + .accounts({ + whirlpool, + tokenProgram: token.TOKEN_PROGRAM_ID, + positionAuthority: payer.publicKey, + position, + positionTokenAccount, + tokenOwnerAccountA: wsolAta, + tokenOwnerAccountB: usdcAta, + tokenVaultA: tokenVaultA.publicKey, + tokenVaultB: tokenVaultB.publicKey, + tickArrayLower: getTickArrayAddr(whirlpool, startTickIndex(MIN_TICK)), + tickArrayUpper: getTickArrayAddr(whirlpool, startTickIndex(MAX_TICK)), + }) + .rpc(); + + return { + program, + config, + whirlpool, + oracle, + tokenVaultA: tokenVaultA.publicKey, + tokenVaultB: tokenVaultB.publicKey, + tickArrayStarts, + }; +} + +// Builds a swap_v2 instruction with the three tick arrays derived from the +// pool's live tick, walking in the swap's direction. +export async function whirlpoolSwapV2Ix( + fixture: WhirlpoolFixture, + { + tokenAuthority, + tokenOwnerAccountA, + tokenOwnerAccountB, + amountIn, + minAmountOut, + aToB, + }: { + tokenAuthority: PublicKey; + tokenOwnerAccountA: PublicKey; + tokenOwnerAccountB: PublicKey; + amountIn: bigint; + minAmountOut: bigint; + aToB: boolean; + }, +): Promise { + const pool = await fixture.program.account.whirlpool.fetch(fixture.whirlpool); + const currentStart = startTickIndex(pool.tickCurrentIndex as number); + const direction = aToB ? -1 : 1; + const tickArrays = [0, 1, 2].map((k) => + getTickArrayAddr( + fixture.whirlpool, + currentStart + k * direction * TICK_ARRAY_SPAN, + ), + ); + + return fixture.program.methods + .swapV2( + new BN(amountIn.toString()), + new BN(minAmountOut.toString()), + new BN((aToB ? MIN_SQRT_PRICE : MAX_SQRT_PRICE).toString()), + true, // amount specified is input + aToB, + null, // no supplemental tick arrays / transfer hook accounts + ) + .accounts({ + tokenProgramA: token.TOKEN_PROGRAM_ID, + tokenProgramB: token.TOKEN_PROGRAM_ID, + memoProgram: MEMO_PROGRAM_ID, + tokenAuthority, + whirlpool: fixture.whirlpool, + tokenMintA: token.NATIVE_MINT, + tokenMintB: MAINNET_USDC, + tokenOwnerAccountA, + tokenVaultA: fixture.tokenVaultA, + tokenOwnerAccountB, + tokenVaultB: fixture.tokenVaultB, + tickArray0: tickArrays[0], + tickArray1: tickArrays[1], + tickArray2: tickArrays[2], + oracle: fixture.oracle, + }) + .instruction(); +} From bf5509d2dcf7af5b229533c1dfdbaa706b8512b6 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 5 Aug 2026 22:28:12 +0200 Subject: [PATCH 07/31] initialize_relaunch ix, sdk, tests --- programs/relaunch/src/constants.rs | 14 +- programs/relaunch/src/error.rs | 24 + programs/relaunch/src/events.rs | 42 + .../src/instructions/initialize_relaunch.rs | 384 ++++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 15 +- programs/relaunch/src/pump_amm.rs | 28 + programs/relaunch/src/state/relaunch.rs | 3 +- sdk/src/relaunch/v0.1/RelaunchClient.ts | 232 ++++- sdk/src/relaunch/v0.1/types/relaunch.ts | 762 ++++++++++++++- tasks.md | 8 +- ...minEnqueueMultisigProposalApproval.test.ts | 3 +- .../executeMultisigProposalApproval.test.ts | 3 +- .../unit/executeSpendingLimitChange.test.ts | 2 +- tests/main.test.ts | 14 + .../unit/completeUnlock.test.ts | 9 + tests/relaunch/main.test.ts | 2 + .../relaunch/unit/initializeRelaunch.test.ts | 869 ++++++++++++++++++ 18 files changed, 2395 insertions(+), 21 deletions(-) create mode 100644 programs/relaunch/src/instructions/initialize_relaunch.rs create mode 100644 programs/relaunch/src/pump_amm.rs create mode 100644 tests/relaunch/unit/initializeRelaunch.test.ts diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index a18e221c1..047099d2d 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -7,8 +7,8 @@ pub const TOKENS_TO_DEPOSITORS: u64 = 10_000_000 * TOKEN_SCALE; /// 2M tokens with 6 decimals, paired with recovered USDC in the futarchy AMM. pub const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 2_000_000 * TOKEN_SCALE; -/// 14 days, mirroring launchpad's launch-window cap. -pub const MAX_SECONDS_FOR_DEPOSITS: u32 = 60 * 60 * 24 * 14; +/// 1 year. +pub const MAX_SECONDS_FOR_DEPOSITS: u32 = 60 * 60 * 24 * 365; pub mod wsol_mint { use anchor_lang::prelude::declare_id; @@ -16,6 +16,12 @@ pub mod wsol_mint { declare_id!("So11111111111111111111111111111111111111112"); } +pub mod usdc_mint { + use anchor_lang::prelude::declare_id; + + declare_id!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); +} + // pump /// Seed of the pump pool-authority PDA, derived under the pump bonding-curve @@ -23,6 +29,10 @@ pub mod wsol_mint { /// mint has this PDA as its `creator`. pub const PUMP_POOL_AUTHORITY_SEED: &[u8] = b"pool-authority"; +/// Seed of PumpSwap pool PDAs, derived under pump_amm: +/// `["pool", index_le_u16, creator, base_mint, quote_mint]`. +pub const PUMP_POOL_SEED: &[u8] = b"pool"; + /// The pump bonding-curve program. The pool-authority PDA that creates /// canonical PumpSwap pools at graduation is derived under this program. pub mod pump_program { diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 8b1378917..94ca9dcc3 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -1 +1,25 @@ +use anchor_lang::prelude::*; +#[error_code] +pub enum RelaunchError { + #[msg("New mint supply must be zero")] + SupplyNonZero, + #[msg("New mint must not have a freeze authority")] + FreezeAuthoritySet, + #[msg("Source pool is not the canonical PumpSwap pool for the old mint")] + SourcePoolNotCanonical, + #[msg("Source quote mint does not match the source pool's quote mint")] + SourcePoolQuoteMintMismatch, + #[msg("Source quote mint must be WSOL or USDC")] + InvalidQuoteMint, + #[msg("Old mint carries a Token-2022 extension outside the metadata allowlist")] + ForbiddenOldMintExtension, + #[msg("Threshold must be between 1 and 10000 bps")] + InvalidThresholdBps, + #[msg("Deposit period must be at most 1 year")] + InvalidSecondsForDeposits, + #[msg("Monthly spending limit amount and members must both be set or both be empty")] + InvalidMonthlySpendingLimit, + #[msg("There can be at most 10 monthly spending limit members, without duplicates")] + InvalidMonthlySpendingLimitMembers, +} diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 8b1378917..e8b105b8e 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -1 +1,43 @@ +use anchor_lang::prelude::*; +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct CommonFields { + pub slot: u64, + pub unix_timestamp: i64, + pub relaunch_seq_num: u64, +} + +impl CommonFields { + pub fn new(clock: &Clock, relaunch_seq_num: u64) -> Self { + Self { + slot: clock.slot, + unix_timestamp: clock.unix_timestamp, + relaunch_seq_num, + } + } +} + +#[event] +pub struct RelaunchInitializedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub admin: Pubkey, + pub new_mint: Pubkey, + pub old_mint: Pubkey, + pub source_pool: Pubkey, + pub source_quote_mint: Pubkey, + pub relaunch_signer: Pubkey, + pub relaunch_signer_bump: u8, + pub old_token_vault: Pubkey, + pub new_token_vault: Pubkey, + pub source_quote_vault: Pubkey, + pub usdc_vault: Pubkey, + pub threshold_bps: u16, + pub old_supply_snapshot: u64, + pub seconds_for_deposits: u32, + pub grace_period_seconds: u32, + pub monthly_spending_limit_amount: u64, + pub monthly_spending_limit_members: Vec, + pub team_address: Pubkey, + pub pda_bump: u8, +} diff --git a/programs/relaunch/src/instructions/initialize_relaunch.rs b/programs/relaunch/src/instructions/initialize_relaunch.rs new file mode 100644 index 000000000..08f8218e2 --- /dev/null +++ b/programs/relaunch/src/instructions/initialize_relaunch.rs @@ -0,0 +1,384 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::metadata::{ + create_metadata_accounts_v3, mpl_token_metadata::types::DataV2, + mpl_token_metadata::ID as MPL_TOKEN_METADATA_PROGRAM_ID, CreateMetadataAccountsV3, Metadata, +}; +use anchor_spl::token::{ + self, spl_token::instruction::AuthorityType, Mint, MintTo, SetAuthority, Token, TokenAccount, +}; +use anchor_spl::token_2022::spl_token_2022::{ + extension::{BaseStateWithExtensions, ExtensionType, StateWithExtensions}, + state::Mint as MintWithExtensions, +}; +use anchor_spl::token_interface; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, RelaunchInitializedEvent}; +use crate::pump_amm; +use crate::state::{Relaunch, RelaunchState}; +use crate::{ + pump_amm_program, pump_program, usdc_mint, wsol_mint, MAX_SECONDS_FOR_DEPOSITS, + PUMP_POOL_AUTHORITY_SEED, PUMP_POOL_SEED, TOKENS_TO_DEPOSITORS, TOKENS_TO_FUTARCHY_LIQUIDITY, +}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct InitializeRelaunchArgs { + pub token_name: String, + pub token_symbol: String, + pub token_uri: String, + pub seconds_for_deposits: u32, + pub grace_period_seconds: u32, + pub threshold_bps: u16, + pub monthly_spending_limit_amount: u64, + pub monthly_spending_limit_members: Vec, + pub team_address: Pubkey, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct InitializeRelaunch<'info> { + #[account( + init, + payer = payer, + space = 8 + Relaunch::INIT_SPACE, + seeds = [b"relaunch", new_mint.key().as_ref()], + bump + )] + pub relaunch: Box>, + + #[account( + mut, + mint::decimals = 6, + mint::authority = mint_authority, + )] + pub new_mint: Box>, + + /// Proof that the initializer controls the new mint: must sign, and the + /// handler CPIs `set_authority` to hand minting to `relaunch_signer`. + pub mint_authority: Signer<'info>, + + /// CHECK: PDA that signs CPIs and owns the vaults + #[account( + seeds = [b"relaunch_signer", relaunch.key().as_ref()], + bump + )] + pub relaunch_signer: UncheckedAccount<'info>, + + #[account(mint::token_program = old_token_program)] + pub old_mint: Box>, + + /// CHECK: fingerprint-checked in validate() + pub source_pool: UncheckedAccount<'info>, + + pub source_quote_mint: Box>, + + #[account(address = usdc_mint::id())] + pub usdc_mint: Box>, + + #[account( + init_if_needed, + payer = payer, + associated_token::mint = old_mint, + associated_token::authority = relaunch_signer, + associated_token::token_program = old_token_program, + )] + pub old_token_vault: Box>, + + #[account( + init_if_needed, + payer = payer, + associated_token::mint = new_mint, + associated_token::authority = relaunch_signer, + )] + pub new_token_vault: Box>, + + #[account( + init_if_needed, + payer = payer, + associated_token::mint = source_quote_mint, + associated_token::authority = relaunch_signer, + )] + pub source_quote_vault: Box>, + + /// The same account as `source_quote_vault` for USDC-quoted sources, in + /// which case the `init_if_needed` is a no-op revalidation. + #[account( + init_if_needed, + payer = payer, + associated_token::mint = usdc_mint, + associated_token::authority = relaunch_signer, + )] + pub usdc_vault: Box>, + + /// CHECK: This is the token metadata + #[account( + mut, + seeds = [b"metadata", MPL_TOKEN_METADATA_PROGRAM_ID.as_ref(), new_mint.key().as_ref()], + seeds::program = MPL_TOKEN_METADATA_PROGRAM_ID, + bump + )] + pub token_metadata: UncheckedAccount<'info>, + + /// CHECK: The initializer; gains the sell/swap monopoly during the grace + /// period. Not required to sign, mirroring launchpad's launch_authority. + pub admin: UncheckedAccount<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + pub rent: Sysvar<'info, Rent>, + + pub old_token_program: Interface<'info, token_interface::TokenInterface>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, + pub token_metadata_program: Program<'info, Metadata>, +} + +impl InitializeRelaunch<'_> { + pub fn validate(&self, args: &InitializeRelaunchArgs) -> Result<()> { + require_eq!(self.new_mint.supply, 0, RelaunchError::SupplyNonZero); + + require!( + self.new_mint.freeze_authority.is_none(), + RelaunchError::FreezeAuthoritySet + ); + + require!( + self.source_quote_mint.key() == wsol_mint::id() + || self.source_quote_mint.key() == usdc_mint::id(), + RelaunchError::InvalidQuoteMint + ); + + require_keys_eq!( + *self.source_pool.owner, + pump_amm_program::id(), + RelaunchError::SourcePoolNotCanonical + ); + + let pool = pump_amm::PumpSwapPool::try_parse(&self.source_pool.try_borrow_data()?)?; + + require_eq!(pool.index, 0, RelaunchError::SourcePoolNotCanonical); + + require_keys_eq!( + pool.base_mint, + self.old_mint.key(), + RelaunchError::SourcePoolNotCanonical + ); + + let (pool_authority, _) = Pubkey::find_program_address( + &[PUMP_POOL_AUTHORITY_SEED, self.old_mint.key().as_ref()], + &pump_program::id(), + ); + require_keys_eq!( + pool.creator, + pool_authority, + RelaunchError::SourcePoolNotCanonical + ); + + require_keys_eq!( + pool.quote_mint, + self.source_quote_mint.key(), + RelaunchError::SourcePoolQuoteMintMismatch + ); + + // The fields above are also the pool PDA's seeds, so re-deriving the + // address re-checks them without relying on pump_amm keeping stored + // fields consistent with seeds. + let (canonical_pool, _) = Pubkey::find_program_address( + &[ + PUMP_POOL_SEED, + &0u16.to_le_bytes(), + pool_authority.as_ref(), + self.old_mint.key().as_ref(), + self.source_quote_mint.key().as_ref(), + ], + &pump_amm_program::id(), + ); + require_keys_eq!( + self.source_pool.key(), + canonical_pool, + RelaunchError::SourcePoolNotCanonical + ); + + // Old mints may carry only mint-embedded metadata extensions; anything + // else (transfer fees, hooks, ...) is rejected rather than assumed safe. + let old_mint_info = self.old_mint.to_account_info(); + if *old_mint_info.owner == anchor_spl::token_2022::ID { + let old_mint_data = old_mint_info.try_borrow_data()?; + let old_mint_state = StateWithExtensions::::unpack(&old_mint_data)?; + for extension in old_mint_state.get_extension_types()? { + require!( + matches!( + extension, + ExtensionType::MetadataPointer | ExtensionType::TokenMetadata + ), + RelaunchError::ForbiddenOldMintExtension + ); + } + } + + require_gt!(args.threshold_bps, 0, RelaunchError::InvalidThresholdBps); + require_gte!( + 10_000, + args.threshold_bps, + RelaunchError::InvalidThresholdBps + ); + + require_gte!( + MAX_SECONDS_FOR_DEPOSITS, + args.seconds_for_deposits, + RelaunchError::InvalidSecondsForDeposits + ); + + // A zero amount with no members means the DAO launches without a + // spending limit; a config missing either half is invalid. + require!( + (args.monthly_spending_limit_amount == 0) + == args.monthly_spending_limit_members.is_empty(), + RelaunchError::InvalidMonthlySpendingLimit + ); + + require_gte!( + futarchy::MAX_SPENDING_LIMIT_MEMBERS, + args.monthly_spending_limit_members.len(), + RelaunchError::InvalidMonthlySpendingLimitMembers + ); + + let mut sorted_members = args.monthly_spending_limit_members.clone(); + sorted_members.sort(); + let has_duplicates = sorted_members.windows(2).any(|win| win[0] == win[1]); + require!( + !has_duplicates, + RelaunchError::InvalidMonthlySpendingLimitMembers + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: InitializeRelaunchArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.bumps.relaunch_signer], + ]; + let signer = &[&seeds[..]]; + + token::set_authority( + CpiContext::new( + ctx.accounts.token_program.to_account_info(), + SetAuthority { + account_or_mint: ctx.accounts.new_mint.to_account_info(), + current_authority: ctx.accounts.mint_authority.to_account_info(), + }, + ), + AuthorityType::MintTokens, + Some(ctx.accounts.relaunch_signer.key()), + )?; + + create_metadata_accounts_v3( + CpiContext::new( + ctx.accounts.token_metadata_program.to_account_info(), + CreateMetadataAccountsV3 { + metadata: ctx.accounts.token_metadata.to_account_info(), + mint: ctx.accounts.new_mint.to_account_info(), + mint_authority: ctx.accounts.relaunch_signer.to_account_info(), + payer: ctx.accounts.payer.to_account_info(), + update_authority: ctx.accounts.relaunch_signer.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + rent: ctx.accounts.rent.to_account_info(), + }, + ) + .with_signer(signer), + DataV2 { + name: args.token_name.clone(), + symbol: args.token_symbol.clone(), + uri: args.token_uri.clone(), + seller_fee_basis_points: 0, + creators: None, + collection: None, + uses: None, + }, + true, + true, + None, + )?; + + token::mint_to( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + MintTo { + mint: ctx.accounts.new_mint.to_account_info(), + to: ctx.accounts.new_token_vault.to_account_info(), + authority: ctx.accounts.relaunch_signer.to_account_info(), + }, + signer, + ), + TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY, + )?; + + let old_supply_snapshot = ctx.accounts.old_mint.supply; + + ctx.accounts.relaunch.set_inner(Relaunch { + admin: ctx.accounts.admin.key(), + new_mint: ctx.accounts.new_mint.key(), + old_mint: ctx.accounts.old_mint.key(), + source_pool: ctx.accounts.source_pool.key(), + source_quote_mint: ctx.accounts.source_quote_mint.key(), + relaunch_signer: ctx.accounts.relaunch_signer.key(), + relaunch_signer_bump: ctx.bumps.relaunch_signer, + old_token_vault: ctx.accounts.old_token_vault.key(), + new_token_vault: ctx.accounts.new_token_vault.key(), + source_quote_vault: ctx.accounts.source_quote_vault.key(), + usdc_vault: ctx.accounts.usdc_vault.key(), + threshold_bps: args.threshold_bps, + old_supply_snapshot, + seconds_for_deposits: args.seconds_for_deposits, + grace_period_seconds: args.grace_period_seconds, + monthly_spending_limit_amount: args.monthly_spending_limit_amount, + monthly_spending_limit_members: args.monthly_spending_limit_members.clone(), + team_address: args.team_address, + state: RelaunchState::Initialized, + total_deposited: 0, + quote_recovered: 0, + usdc_recovered: 0, + unix_timestamp_started: None, + unix_timestamp_closed: None, + unix_timestamp_completed: None, + dao: None, + dao_vault: None, + seq_num: 0, + pda_bump: ctx.bumps.relaunch, + }); + + let clock = Clock::get()?; + emit_cpi!(RelaunchInitializedEvent { + common: CommonFields::new(&clock, 0), + relaunch: relaunch_key, + admin: ctx.accounts.admin.key(), + new_mint: ctx.accounts.new_mint.key(), + old_mint: ctx.accounts.old_mint.key(), + source_pool: ctx.accounts.source_pool.key(), + source_quote_mint: ctx.accounts.source_quote_mint.key(), + relaunch_signer: ctx.accounts.relaunch_signer.key(), + relaunch_signer_bump: ctx.bumps.relaunch_signer, + old_token_vault: ctx.accounts.old_token_vault.key(), + new_token_vault: ctx.accounts.new_token_vault.key(), + source_quote_vault: ctx.accounts.source_quote_vault.key(), + usdc_vault: ctx.accounts.usdc_vault.key(), + threshold_bps: args.threshold_bps, + old_supply_snapshot, + seconds_for_deposits: args.seconds_for_deposits, + grace_period_seconds: args.grace_period_seconds, + monthly_spending_limit_amount: args.monthly_spending_limit_amount, + monthly_spending_limit_members: args.monthly_spending_limit_members, + team_address: args.team_address, + pda_bump: ctx.bumps.relaunch, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 8b1378917..1d616aa77 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1 +1,3 @@ +pub mod initialize_relaunch; +pub use initialize_relaunch::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index aa98e1137..ef8f164ea 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -5,11 +5,14 @@ pub mod constants; pub mod error; pub mod events; pub mod instructions; +pub mod pump_amm; pub mod state; pub use constants::*; pub use state::*; +use instructions::*; + #[cfg(not(feature = "no-entrypoint"))] use solana_security_txt::security_txt; @@ -27,4 +30,14 @@ security_txt! { declare_id!("vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik"); #[program] -pub mod relaunch {} +pub mod relaunch { + use super::*; + + #[access_control(ctx.accounts.validate(&args))] + pub fn initialize_relaunch( + ctx: Context, + args: InitializeRelaunchArgs, + ) -> Result<()> { + InitializeRelaunch::handle(ctx, args) + } +} diff --git a/programs/relaunch/src/pump_amm.rs b/programs/relaunch/src/pump_amm.rs new file mode 100644 index 000000000..f8d46193f --- /dev/null +++ b/programs/relaunch/src/pump_amm.rs @@ -0,0 +1,28 @@ +//! Types for reading pump_amm accounts. +use anchor_lang::prelude::*; + +use crate::error::RelaunchError; + +pub const POOL_DISCRIMINATOR: [u8; 8] = [241, 154, 109, 4, 17, 177, 109, 188]; + +/// The prefix of pump_amm's `Pool` account that canonicality validation +/// reads; trailing fields are ignored. +#[derive(AnchorDeserialize)] +pub struct PumpSwapPool { + pub pool_bump: u8, + pub index: u16, + pub creator: Pubkey, + pub base_mint: Pubkey, + pub quote_mint: Pubkey, +} + +impl PumpSwapPool { + pub fn try_parse(data: &[u8]) -> Result { + require!( + data.len() > POOL_DISCRIMINATOR.len() && data[..8] == POOL_DISCRIMINATOR, + RelaunchError::SourcePoolNotCanonical + ); + Self::deserialize(&mut &data[8..]) + .map_err(|_| error!(RelaunchError::SourcePoolNotCanonical)) + } +} diff --git a/programs/relaunch/src/state/relaunch.rs b/programs/relaunch/src/state/relaunch.rs index 9eb9d20f8..bfabd49ea 100644 --- a/programs/relaunch/src/state/relaunch.rs +++ b/programs/relaunch/src/state/relaunch.rs @@ -54,7 +54,8 @@ pub struct Relaunch { pub grace_period_seconds: u32, // DAO passthrough config (launchpad-style) - /// The monthly spending limit the DAO allocates to the team. + /// The monthly spending limit the DAO allocates to the team. Zero, with + /// no members, means the DAO launches without a spending limit. pub monthly_spending_limit_amount: u64, /// The wallets that have access to the monthly spending limit. #[max_len(MAX_SPENDING_LIMIT_MEMBERS)] diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 92e56a912..ecb074990 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -1,6 +1,24 @@ import { AnchorProvider, Program } from "@coral-xyz/anchor"; -import { AccountInfo, PublicKey } from "@solana/web3.js"; -import { RELAUNCH_V0_1_PROGRAM_ID } from "../../constants.js"; +import { + AccountInfo, + ComputeBudgetProgram, + Keypair, + PublicKey, + SystemProgram, + TransactionSignature, +} from "@solana/web3.js"; +import { + createInitializeMint2Instruction, + getAssociatedTokenAddressSync, + MINT_SIZE, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import BN from "bn.js"; +import { + MAINNET_USDC, + MPL_TOKEN_METADATA_PROGRAM_ID, + RELAUNCH_V0_1_PROGRAM_ID, +} from "../../constants.js"; import { RelaunchProgram, RelaunchIDL, @@ -12,7 +30,7 @@ import { getRelaunchSignerAddr, getDepositRecordAddr, } from "./pda.js"; -import { getEventAuthorityAddr } from "../../pda.js"; +import { getEventAuthorityAddr, getMetadataAddr } from "../../pda.js"; export type CreateRelaunchClientParams = { provider: AnchorProvider; @@ -49,6 +67,214 @@ export class RelaunchClient { return this.programId; } + initializeRelaunchIx({ + newMint, + oldMint, + oldTokenProgram, + sourcePool, + sourceQuoteMint, + tokenName, + tokenSymbol, + tokenUri, + secondsForDeposits, + gracePeriodSeconds, + thresholdBps, + // Zero amount with no members initializes without a spending limit. + monthlySpendingLimitAmount = new BN(0), + monthlySpendingLimitMembers = [], + teamAddress, + mintAuthority = this.provider.publicKey, + admin = this.provider.publicKey, + payer = this.provider.publicKey, + }: { + newMint: PublicKey; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + sourcePool: PublicKey; + sourceQuoteMint: PublicKey; + tokenName: string; + tokenSymbol: string; + tokenUri: string; + secondsForDeposits: number; + gracePeriodSeconds: number; + thresholdBps: number; + monthlySpendingLimitAmount?: BN; + monthlySpendingLimitMembers?: PublicKey[]; + teamAddress: PublicKey; + mintAuthority?: PublicKey; + admin?: PublicKey; + payer?: PublicKey; + }) { + const relaunch = this.getRelaunchAddress({ newMint }); + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + oldTokenProgram, + ); + const newTokenVault = getAssociatedTokenAddressSync( + newMint, + relaunchSigner, + true, + ); + const sourceQuoteVault = getAssociatedTokenAddressSync( + sourceQuoteMint, + relaunchSigner, + true, + ); + const usdcVault = getAssociatedTokenAddressSync( + MAINNET_USDC, + relaunchSigner, + true, + ); + const [tokenMetadata] = getMetadataAddr(newMint); + + return this.relaunchProgram.methods + .initializeRelaunch({ + tokenName, + tokenSymbol, + tokenUri, + secondsForDeposits, + gracePeriodSeconds, + thresholdBps, + monthlySpendingLimitAmount, + monthlySpendingLimitMembers, + teamAddress, + }) + .accounts({ + relaunch, + newMint, + mintAuthority, + relaunchSigner, + oldMint, + sourcePool, + sourceQuoteMint, + usdcMint: MAINNET_USDC, + oldTokenVault, + newTokenVault, + sourceQuoteVault, + usdcVault, + tokenMetadata, + admin, + payer, + oldTokenProgram, + tokenProgram: TOKEN_PROGRAM_ID, + tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, + }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ]); + } + + // Builds the create-mint-to-self pre-instructions: a `createAccountWithSeed` + // + `initializeMint2` pair with the payer as mint authority, so + // `initialize_relaunch` can take the authority from a mint the payer + // provably controls. + async createNewMintIxs({ + payer = this.provider.publicKey, + seed = Keypair.generate().publicKey.toBase58().slice(0, 32), + }: { + payer?: PublicKey; + seed?: string; + } = {}) { + const newMint = await PublicKey.createWithSeed( + payer, + seed, + TOKEN_PROGRAM_ID, + ); + const lamports = + await this.provider.connection.getMinimumBalanceForRentExemption( + MINT_SIZE, + ); + + const instructions = [ + SystemProgram.createAccountWithSeed({ + fromPubkey: payer, + basePubkey: payer, + seed, + newAccountPubkey: newMint, + lamports, + space: MINT_SIZE, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction(newMint, 6, payer, null), + ]; + + return { newMint, instructions }; + } + + // Creates the new mint and initializes the relaunch in a single + // transaction, signed entirely by the provider wallet. + async initializeRelaunch({ + oldMint, + sourcePool, + sourceQuoteMint, + tokenName, + tokenSymbol, + tokenUri, + secondsForDeposits, + gracePeriodSeconds, + thresholdBps, + monthlySpendingLimitAmount, + monthlySpendingLimitMembers, + teamAddress, + admin, + }: { + oldMint: PublicKey; + sourcePool: PublicKey; + sourceQuoteMint: PublicKey; + tokenName: string; + tokenSymbol: string; + tokenUri: string; + secondsForDeposits: number; + gracePeriodSeconds: number; + thresholdBps: number; + monthlySpendingLimitAmount?: BN; + monthlySpendingLimitMembers?: PublicKey[]; + teamAddress: PublicKey; + admin?: PublicKey; + }): Promise<{ + newMint: PublicKey; + relaunch: PublicKey; + txSignature: TransactionSignature; + }> { + const { newMint, instructions } = await this.createNewMintIxs(); + + const oldMintAccount = + await this.provider.connection.getAccountInfo(oldMint); + if (oldMintAccount === null) { + throw new Error(`old mint ${oldMint.toBase58()} does not exist`); + } + + const txSignature = await this.initializeRelaunchIx({ + newMint, + oldMint, + oldTokenProgram: oldMintAccount.owner, + sourcePool, + sourceQuoteMint, + tokenName, + tokenSymbol, + tokenUri, + secondsForDeposits, + gracePeriodSeconds, + thresholdBps, + monthlySpendingLimitAmount, + monthlySpendingLimitMembers, + teamAddress, + admin, + }) + .preInstructions(instructions) + .rpc(); + + return { + newMint, + relaunch: this.getRelaunchAddress({ newMint }), + txSignature, + }; + } + async fetchRelaunch(relaunch: PublicKey): Promise { return this.relaunchProgram.account.relaunch.fetchNullable(relaunch); } diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index cccb58a58..caf7bcec4 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -1,7 +1,147 @@ export type Relaunch = { version: "0.1.0"; name: "relaunch"; - instructions: []; + instructions: [ + { + name: "initializeRelaunch"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "newMint"; + isMut: true; + isSigner: false; + }, + { + name: "mintAuthority"; + isMut: false; + isSigner: true; + docs: [ + "Proof that the initializer controls the new mint: must sign, and the", + "handler CPIs `set_authority` to hand minting to `relaunch_signer`.", + ]; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "oldMint"; + isMut: false; + isSigner: false; + }, + { + name: "sourcePool"; + isMut: false; + isSigner: false; + }, + { + name: "sourceQuoteMint"; + isMut: false; + isSigner: false; + }, + { + name: "usdcMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "newTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "usdcVault"; + isMut: true; + isSigner: false; + docs: [ + "The same account as `source_quote_vault` for USDC-quoted sources, in", + "which case the `init_if_needed` is a no-op revalidation.", + ]; + }, + { + name: "tokenMetadata"; + isMut: true; + isSigner: false; + }, + { + name: "admin"; + isMut: false; + isSigner: false; + docs: [ + "period. Not required to sign, mirroring launchpad's launch_authority.", + ]; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "rent"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenMetadataProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "InitializeRelaunchArgs"; + }; + }, + ]; + }, + ]; accounts: [ { name: "depositRecord"; @@ -148,7 +288,10 @@ export type Relaunch = { }, { name: "monthlySpendingLimitAmount"; - docs: ["The monthly spending limit the DAO allocates to the team."]; + docs: [ + "The monthly spending limit the DAO allocates to the team. Zero, with", + "no members, means the DAO launches without a spending limit.", + ]; type: "u64"; }, { @@ -243,6 +386,72 @@ export type Relaunch = { }, ]; types: [ + { + name: "CommonFields"; + type: { + kind: "struct"; + fields: [ + { + name: "slot"; + type: "u64"; + }, + { + name: "unixTimestamp"; + type: "i64"; + }, + { + name: "relaunchSeqNum"; + type: "u64"; + }, + ]; + }; + }, + { + name: "InitializeRelaunchArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "tokenName"; + type: "string"; + }, + { + name: "tokenSymbol"; + type: "string"; + }, + { + name: "tokenUri"; + type: "string"; + }, + { + name: "secondsForDeposits"; + type: "u32"; + }, + { + name: "gracePeriodSeconds"; + type: "u32"; + }, + { + name: "thresholdBps"; + type: "u16"; + }, + { + name: "monthlySpendingLimitAmount"; + type: "u64"; + }, + { + name: "monthlySpendingLimitMembers"; + type: { + vec: "publicKey"; + }; + }, + { + name: "teamAddress"; + type: "publicKey"; + }, + ]; + }; + }, { name: "RelaunchState"; type: { @@ -273,12 +482,320 @@ export type Relaunch = { }; }, ]; + events: [ + { + name: "RelaunchInitializedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "admin"; + type: "publicKey"; + index: false; + }, + { + name: "newMint"; + type: "publicKey"; + index: false; + }, + { + name: "oldMint"; + type: "publicKey"; + index: false; + }, + { + name: "sourcePool"; + type: "publicKey"; + index: false; + }, + { + name: "sourceQuoteMint"; + type: "publicKey"; + index: false; + }, + { + name: "relaunchSigner"; + type: "publicKey"; + index: false; + }, + { + name: "relaunchSignerBump"; + type: "u8"; + index: false; + }, + { + name: "oldTokenVault"; + type: "publicKey"; + index: false; + }, + { + name: "newTokenVault"; + type: "publicKey"; + index: false; + }, + { + name: "sourceQuoteVault"; + type: "publicKey"; + index: false; + }, + { + name: "usdcVault"; + type: "publicKey"; + index: false; + }, + { + name: "thresholdBps"; + type: "u16"; + index: false; + }, + { + name: "oldSupplySnapshot"; + type: "u64"; + index: false; + }, + { + name: "secondsForDeposits"; + type: "u32"; + index: false; + }, + { + name: "gracePeriodSeconds"; + type: "u32"; + index: false; + }, + { + name: "monthlySpendingLimitAmount"; + type: "u64"; + index: false; + }, + { + name: "monthlySpendingLimitMembers"; + type: { + vec: "publicKey"; + }; + index: false; + }, + { + name: "teamAddress"; + type: "publicKey"; + index: false; + }, + { + name: "pdaBump"; + type: "u8"; + index: false; + }, + ]; + }, + ]; + errors: [ + { + code: 6000; + name: "SupplyNonZero"; + msg: "New mint supply must be zero"; + }, + { + code: 6001; + name: "FreezeAuthoritySet"; + msg: "New mint must not have a freeze authority"; + }, + { + code: 6002; + name: "SourcePoolNotCanonical"; + msg: "Source pool is not the canonical PumpSwap pool for the old mint"; + }, + { + code: 6003; + name: "SourcePoolQuoteMintMismatch"; + msg: "Source quote mint does not match the source pool's quote mint"; + }, + { + code: 6004; + name: "InvalidQuoteMint"; + msg: "Source quote mint must be WSOL or USDC"; + }, + { + code: 6005; + name: "ForbiddenOldMintExtension"; + msg: "Old mint carries a Token-2022 extension outside the metadata allowlist"; + }, + { + code: 6006; + name: "InvalidThresholdBps"; + msg: "Threshold must be between 1 and 10000 bps"; + }, + { + code: 6007; + name: "InvalidSecondsForDeposits"; + msg: "Deposit period must be at most 1 year"; + }, + { + code: 6008; + name: "InvalidMonthlySpendingLimit"; + msg: "Monthly spending limit amount and members must both be set or both be empty"; + }, + { + code: 6009; + name: "InvalidMonthlySpendingLimitMembers"; + msg: "There can be at most 10 monthly spending limit members, without duplicates"; + }, + ]; }; export const IDL: Relaunch = { version: "0.1.0", name: "relaunch", - instructions: [], + instructions: [ + { + name: "initializeRelaunch", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "newMint", + isMut: true, + isSigner: false, + }, + { + name: "mintAuthority", + isMut: false, + isSigner: true, + docs: [ + "Proof that the initializer controls the new mint: must sign, and the", + "handler CPIs `set_authority` to hand minting to `relaunch_signer`.", + ], + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "oldMint", + isMut: false, + isSigner: false, + }, + { + name: "sourcePool", + isMut: false, + isSigner: false, + }, + { + name: "sourceQuoteMint", + isMut: false, + isSigner: false, + }, + { + name: "usdcMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "newTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "usdcVault", + isMut: true, + isSigner: false, + docs: [ + "The same account as `source_quote_vault` for USDC-quoted sources, in", + "which case the `init_if_needed` is a no-op revalidation.", + ], + }, + { + name: "tokenMetadata", + isMut: true, + isSigner: false, + }, + { + name: "admin", + isMut: false, + isSigner: false, + docs: [ + "period. Not required to sign, mirroring launchpad's launch_authority.", + ], + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "rent", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenMetadataProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "InitializeRelaunchArgs", + }, + }, + ], + }, + ], accounts: [ { name: "depositRecord", @@ -425,7 +942,10 @@ export const IDL: Relaunch = { }, { name: "monthlySpendingLimitAmount", - docs: ["The monthly spending limit the DAO allocates to the team."], + docs: [ + "The monthly spending limit the DAO allocates to the team. Zero, with", + "no members, means the DAO launches without a spending limit.", + ], type: "u64", }, { @@ -520,6 +1040,72 @@ export const IDL: Relaunch = { }, ], types: [ + { + name: "CommonFields", + type: { + kind: "struct", + fields: [ + { + name: "slot", + type: "u64", + }, + { + name: "unixTimestamp", + type: "i64", + }, + { + name: "relaunchSeqNum", + type: "u64", + }, + ], + }, + }, + { + name: "InitializeRelaunchArgs", + type: { + kind: "struct", + fields: [ + { + name: "tokenName", + type: "string", + }, + { + name: "tokenSymbol", + type: "string", + }, + { + name: "tokenUri", + type: "string", + }, + { + name: "secondsForDeposits", + type: "u32", + }, + { + name: "gracePeriodSeconds", + type: "u32", + }, + { + name: "thresholdBps", + type: "u16", + }, + { + name: "monthlySpendingLimitAmount", + type: "u64", + }, + { + name: "monthlySpendingLimitMembers", + type: { + vec: "publicKey", + }, + }, + { + name: "teamAddress", + type: "publicKey", + }, + ], + }, + }, { name: "RelaunchState", type: { @@ -550,4 +1136,172 @@ export const IDL: Relaunch = { }, }, ], + events: [ + { + name: "RelaunchInitializedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "admin", + type: "publicKey", + index: false, + }, + { + name: "newMint", + type: "publicKey", + index: false, + }, + { + name: "oldMint", + type: "publicKey", + index: false, + }, + { + name: "sourcePool", + type: "publicKey", + index: false, + }, + { + name: "sourceQuoteMint", + type: "publicKey", + index: false, + }, + { + name: "relaunchSigner", + type: "publicKey", + index: false, + }, + { + name: "relaunchSignerBump", + type: "u8", + index: false, + }, + { + name: "oldTokenVault", + type: "publicKey", + index: false, + }, + { + name: "newTokenVault", + type: "publicKey", + index: false, + }, + { + name: "sourceQuoteVault", + type: "publicKey", + index: false, + }, + { + name: "usdcVault", + type: "publicKey", + index: false, + }, + { + name: "thresholdBps", + type: "u16", + index: false, + }, + { + name: "oldSupplySnapshot", + type: "u64", + index: false, + }, + { + name: "secondsForDeposits", + type: "u32", + index: false, + }, + { + name: "gracePeriodSeconds", + type: "u32", + index: false, + }, + { + name: "monthlySpendingLimitAmount", + type: "u64", + index: false, + }, + { + name: "monthlySpendingLimitMembers", + type: { + vec: "publicKey", + }, + index: false, + }, + { + name: "teamAddress", + type: "publicKey", + index: false, + }, + { + name: "pdaBump", + type: "u8", + index: false, + }, + ], + }, + ], + errors: [ + { + code: 6000, + name: "SupplyNonZero", + msg: "New mint supply must be zero", + }, + { + code: 6001, + name: "FreezeAuthoritySet", + msg: "New mint must not have a freeze authority", + }, + { + code: 6002, + name: "SourcePoolNotCanonical", + msg: "Source pool is not the canonical PumpSwap pool for the old mint", + }, + { + code: 6003, + name: "SourcePoolQuoteMintMismatch", + msg: "Source quote mint does not match the source pool's quote mint", + }, + { + code: 6004, + name: "InvalidQuoteMint", + msg: "Source quote mint must be WSOL or USDC", + }, + { + code: 6005, + name: "ForbiddenOldMintExtension", + msg: "Old mint carries a Token-2022 extension outside the metadata allowlist", + }, + { + code: 6006, + name: "InvalidThresholdBps", + msg: "Threshold must be between 1 and 10000 bps", + }, + { + code: 6007, + name: "InvalidSecondsForDeposits", + msg: "Deposit period must be at most 1 year", + }, + { + code: 6008, + name: "InvalidMonthlySpendingLimit", + msg: "Monthly spending limit amount and members must both be set or both be empty", + }, + { + code: 6009, + name: "InvalidMonthlySpendingLimitMembers", + msg: "There can be at most 10 monthly spending limit members, without duplicates", + }, + ], }; diff --git a/tasks.md b/tasks.md index 9f5b426e8..b323f942b 100644 --- a/tasks.md +++ b/tasks.md @@ -27,13 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 1 — initialize_relaunch + start_deposits" -- [NEXT] 1.1 `initialize_relaunch` (instruction + SDK + tests) - - Accounts/validations per plan §1: `mint_authority` Signer proof-of-control, new-mint checks, source-pool canonicality, quote mint ∈ {WSOL, USDC}, old-mint extension allowlist, threshold/window/spending-limit validations; USDC-quoted sources share one ATA for `source_quote_vault`/`usdc_vault` - - Handler order: `set_authority` → metadata CPI → `mint_to` 12M → snapshot old supply → `set_inner` → event - - SDK: `initializeRelaunch()` with create-mint-to-self pre-instructions - - Tests: full list in plan §1 (happy path, decoupled mint creation, authority footguns, extension allowlist, canonicality failures, arg bounds, PDA squat + rival relaunch) - -- [ ] 1.2 `start_deposits` (instruction + SDK + tests) +- [NEXT] 1.2 `start_deposits` (instruction + SDK + tests) - `has_one = admin` signer gate, `Initialized → Live`, stamp `unix_timestamp_started`, event - Tests: happy path, non-admin fails, double-start fails (compute-budget trick for unique sigs) diff --git a/tests/futarchy/unit/adminEnqueueMultisigProposalApproval.test.ts b/tests/futarchy/unit/adminEnqueueMultisigProposalApproval.test.ts index 2bcdb77af..3ff8e6ff2 100644 --- a/tests/futarchy/unit/adminEnqueueMultisigProposalApproval.test.ts +++ b/tests/futarchy/unit/adminEnqueueMultisigProposalApproval.test.ts @@ -23,12 +23,13 @@ export default function suite() { await this.createTokenAccount(META, this.payer.publicKey); await this.createTokenAccount(USDC, this.payer.publicKey); - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9, 1); await this.mintTo( USDC, this.payer.publicKey, this.payer, 100_000 * 1_000_000, + 1, ); dao = await this.setupBasicDaoWithLiquidity({ diff --git a/tests/futarchy/unit/executeMultisigProposalApproval.test.ts b/tests/futarchy/unit/executeMultisigProposalApproval.test.ts index 233118d18..ee582cc7a 100644 --- a/tests/futarchy/unit/executeMultisigProposalApproval.test.ts +++ b/tests/futarchy/unit/executeMultisigProposalApproval.test.ts @@ -23,12 +23,13 @@ export default function suite() { await this.createTokenAccount(META, this.payer.publicKey); await this.createTokenAccount(USDC, this.payer.publicKey); - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9, 1); await this.mintTo( USDC, this.payer.publicKey, this.payer, 100_000 * 1_000_000, + 1, ); dao = await this.setupBasicDaoWithLiquidity({ diff --git a/tests/futarchy/unit/executeSpendingLimitChange.test.ts b/tests/futarchy/unit/executeSpendingLimitChange.test.ts index da68fc85e..aa32d7f82 100644 --- a/tests/futarchy/unit/executeSpendingLimitChange.test.ts +++ b/tests/futarchy/unit/executeSpendingLimitChange.test.ts @@ -33,7 +33,7 @@ export default function suite() { await this.createTokenAccount(META, this.payer.publicKey); await this.createTokenAccount(USDC, this.payer.publicKey); - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9, 1); await this.mintTo( USDC, this.payer.publicKey, diff --git a/tests/main.test.ts b/tests/main.test.ts index 89880f219..71aa773d4 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -127,6 +127,7 @@ export interface TestContext { to: PublicKey, mintAuthority: Keypair, amount: number, + computeUnitPrice?: number, ) => Promise; getTokenBalance: (mint: PublicKey, owner: PublicKey) => Promise; getMint: (mint: PublicKey) => Promise; @@ -455,16 +456,29 @@ before(async function () { ); }; + // computeUnitPrice, when set, prepends a ComputeBudget instruction so an + // otherwise byte-identical mint transaction gets a unique hash — bankrun + // rejects duplicate hashes within a blockhash window with "This transaction + // has already been processed". this.mintTo = async ( mint: PublicKey, to: PublicKey, mintAuthority: Keypair, amount: number, + computeUnitPrice?: number, ) => { const tokenAccount = token.getAssociatedTokenAddressSync(mint, to, true); const tx = new Transaction(); + if (computeUnitPrice !== undefined) { + tx.add( + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: computeUnitPrice, + }), + ); + } + tx.add( token.createAssociatedTokenAccountIdempotentInstruction( this.payer.publicKey, diff --git a/tests/performancePackageV2/unit/completeUnlock.test.ts b/tests/performancePackageV2/unit/completeUnlock.test.ts index 45d1b40d8..dcf23871e 100644 --- a/tests/performancePackageV2/unit/completeUnlock.test.ts +++ b/tests/performancePackageV2/unit/completeUnlock.test.ts @@ -543,6 +543,9 @@ export default function suite() { recipient: recipient.publicKey, signer: authority.publicKey, }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) .signers([authority]) .rpc(); @@ -657,6 +660,9 @@ export default function suite() { signer: recipient.publicKey, dao, }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) .signers([recipient]) .rpc(); @@ -822,6 +828,9 @@ export default function suite() { recipient: recipient.publicKey, signer: authority.publicKey, }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) .signers([authority]) .rpc(); diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index eaf1fce38..2b17dca86 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -1,5 +1,6 @@ import scaffold from "./unit/scaffold.test.js"; import venues from "./unit/venues.test.js"; +import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -13,4 +14,5 @@ export default function suite() { describe("scaffold", scaffold); describe("venues", venues); + describe("#initialize_relaunch", initializeRelaunch); } diff --git a/tests/relaunch/unit/initializeRelaunch.test.ts b/tests/relaunch/unit/initializeRelaunch.test.ts new file mode 100644 index 000000000..0cd6fcf62 --- /dev/null +++ b/tests/relaunch/unit/initializeRelaunch.test.ts @@ -0,0 +1,869 @@ +import { + Keypair, + PublicKey, + SystemProgram, + Transaction, + TransactionMessage, + VersionedTransaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { + getMetadataAddr, + MAINNET_USDC, + RelaunchClient, +} from "@metadaoproject/programs"; +import { BanksClient } from "solana-bankrun"; +import { createLookupTableForTransaction } from "../../utils.js"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { + getPumpPoolAuthorityAddr, + writePumpPool, + PumpPool, +} from "../pumpAmm.js"; + +type InitializeRelaunchParams = Parameters< + RelaunchClient["initializeRelaunchIx"] +>[0]; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +// TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY +const TOTAL_MINTED = 12_000_000n * 10n ** 6n; + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; +const MAX_SECONDS_FOR_DEPOSITS = 60 * 60 * 24 * 365; + +async function createRawMint( + banksClient: BanksClient, + payer: Keypair, + { + decimals = 6, + mintAuthority, + freezeAuthority = null, + }: { + decimals?: number; + mintAuthority: PublicKey; + freezeAuthority?: PublicKey | null; + }, +): Promise { + const mintKeypair = Keypair.generate(); + const rent = await banksClient.getRent(); + + const tx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mintKeypair.publicKey, + lamports: Number(rent.minimumBalance(BigInt(token.MINT_SIZE))), + space: token.MINT_SIZE, + programId: token.TOKEN_PROGRAM_ID, + }), + token.createInitializeMint2Instruction( + mintKeypair.publicKey, + decimals, + mintAuthority, + freezeAuthority, + ), + ); + + tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; + tx.feePayer = payer.publicKey; + tx.sign(payer, mintKeypair); + await banksClient.processTransaction(tx); + + return mintKeypair.publicKey; +} + +// A pump-style Token-2022 mint: metadata pointer + mint-embedded token +// metadata, nothing else. +async function createT22MintWithMetadata( + banksClient: BanksClient, + payer: Keypair, +): Promise { + const mintKeypair = Keypair.generate(); + const mint = mintKeypair.publicKey; + const rent = await banksClient.getRent(); + const mintLen = token.getMintLen([token.ExtensionType.MetadataPointer]); + + const tx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + // Overfund so the token metadata TLV realloc stays rent-exempt. + lamports: Number(rent.minimumBalance(BigInt(mintLen + 500))), + space: mintLen, + programId: token.TOKEN_2022_PROGRAM_ID, + }), + token.createInitializeMetadataPointerInstruction( + mint, + payer.publicKey, + mint, + token.TOKEN_2022_PROGRAM_ID, + ), + token.createInitializeMint2Instruction( + mint, + 6, + payer.publicKey, + null, + token.TOKEN_2022_PROGRAM_ID, + ), + token.createInitializeInstruction({ + programId: token.TOKEN_2022_PROGRAM_ID, + mint, + metadata: mint, + name: "Old Token", + symbol: "OLD", + uri: "https://example.com/old.json", + mintAuthority: payer.publicKey, + updateAuthority: payer.publicKey, + }), + ); + + tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; + tx.feePayer = payer.publicKey; + tx.sign(payer, mintKeypair); + await banksClient.processTransaction(tx); + + return mint; +} + +async function createT22MintWithTransferFee( + banksClient: BanksClient, + payer: Keypair, +): Promise { + const mintKeypair = Keypair.generate(); + const mint = mintKeypair.publicKey; + const rent = await banksClient.getRent(); + const mintLen = token.getMintLen([token.ExtensionType.TransferFeeConfig]); + + const tx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: Number(rent.minimumBalance(BigInt(mintLen))), + space: mintLen, + programId: token.TOKEN_2022_PROGRAM_ID, + }), + token.createInitializeTransferFeeConfigInstruction( + mint, + payer.publicKey, + payer.publicKey, + 100, + 1_000_000n, + token.TOKEN_2022_PROGRAM_ID, + ), + token.createInitializeMint2Instruction( + mint, + 6, + payer.publicKey, + null, + token.TOKEN_2022_PROGRAM_ID, + ), + ); + + tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; + tx.feePayer = payer.publicKey; + tx.sign(payer, mintKeypair); + await banksClient.processTransaction(tx); + + return mint; +} + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let pool: PumpPool; + + before(function () { + client = this.relaunch; + }); + + beforeEach(async function () { + ({ oldMint, oldTokenProgram } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + })); + pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + }); + + const defaultParams = function (this: Mocha.Context) { + return { + oldMint, + oldTokenProgram, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + monthlySpendingLimitAmount: new BN(10_000_000_000), // 10k USDC + monthlySpendingLimitMembers: [this.payer.publicKey], + teamAddress: this.payer.publicKey, + }; + }; + + const initializeWithParams = async function ( + this: Mocha.Context, + overrides: Partial = {}, + ): Promise { + const { newMint, instructions } = await client.createNewMintIxs(); + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + ...overrides, + }) + .preInstructions(instructions) + .rpc(); + return newMint; + }; + + const expectInitializeToFail = async function ( + this: Mocha.Context, + overrides: Partial, + expectedError: string, + ) { + try { + await initializeWithParams.call(this, overrides); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, expectedError); + } + }; + + it("initializes a relaunch with valid parameters", async function () { + const params = defaultParams.call(this); + const { newMint, relaunch, txSignature } = + await client.initializeRelaunch(params); + assert.isString(txSignature); + + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const storedRelaunch = await client.fetchRelaunch(relaunch); + + assert.isTrue(storedRelaunch.admin.equals(this.payer.publicKey)); + assert.isTrue(storedRelaunch.newMint.equals(newMint)); + assert.isTrue(storedRelaunch.oldMint.equals(oldMint)); + assert.isTrue(storedRelaunch.sourcePool.equals(pool.pool)); + assert.isTrue(storedRelaunch.sourceQuoteMint.equals(token.NATIVE_MINT)); + assert.isTrue(storedRelaunch.relaunchSigner.equals(relaunchSigner)); + assert.isTrue( + storedRelaunch.oldTokenVault.equals( + token.getAssociatedTokenAddressSync(oldMint, relaunchSigner, true), + ), + ); + assert.isTrue( + storedRelaunch.newTokenVault.equals( + token.getAssociatedTokenAddressSync(newMint, relaunchSigner, true), + ), + ); + assert.isTrue( + storedRelaunch.sourceQuoteVault.equals( + token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + relaunchSigner, + true, + ), + ), + ); + assert.isTrue( + storedRelaunch.usdcVault.equals( + token.getAssociatedTokenAddressSync(MAINNET_USDC, relaunchSigner, true), + ), + ); + assert.equal(storedRelaunch.thresholdBps, params.thresholdBps); + assert.equal( + storedRelaunch.oldSupplySnapshot.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + assert.equal(storedRelaunch.secondsForDeposits, params.secondsForDeposits); + assert.equal(storedRelaunch.gracePeriodSeconds, params.gracePeriodSeconds); + assert.equal( + storedRelaunch.monthlySpendingLimitAmount.toString(), + params.monthlySpendingLimitAmount.toString(), + ); + assert.equal(storedRelaunch.monthlySpendingLimitMembers.length, 1); + assert.isTrue( + storedRelaunch.monthlySpendingLimitMembers[0].equals( + this.payer.publicKey, + ), + ); + assert.isTrue(storedRelaunch.teamAddress.equals(this.payer.publicKey)); + assert.isDefined(storedRelaunch.state.initialized); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + assert.equal(storedRelaunch.quoteRecovered.toString(), "0"); + assert.equal(storedRelaunch.usdcRecovered.toString(), "0"); + assert.isNull(storedRelaunch.unixTimestampStarted); + assert.isNull(storedRelaunch.unixTimestampClosed); + assert.isNull(storedRelaunch.unixTimestampCompleted); + assert.isNull(storedRelaunch.dao); + assert.isNull(storedRelaunch.daoVault); + assert.equal(storedRelaunch.seqNum.toString(), "0"); + + const [expectedRelaunch, pdaBump] = PublicKey.findProgramAddressSync( + [Buffer.from("relaunch"), newMint.toBuffer()], + client.getProgramId(), + ); + assert.isTrue(relaunch.equals(expectedRelaunch)); + assert.equal(storedRelaunch.pdaBump, pdaBump); + const [, signerBump] = PublicKey.findProgramAddressSync( + [Buffer.from("relaunch_signer"), relaunch.toBuffer()], + client.getProgramId(), + ); + assert.equal(storedRelaunch.relaunchSignerBump, signerBump); + + const rawNewMint = await this.banksClient.getAccount(newMint); + const newMintState = token.unpackMint(newMint, { + ...rawNewMint, + data: Buffer.from(rawNewMint.data), + } as any); + assert.isTrue(newMintState.mintAuthority.equals(relaunchSigner)); + assert.equal(newMintState.supply.toString(), TOTAL_MINTED.toString()); + + const rawVault = await this.banksClient.getAccount( + storedRelaunch.newTokenVault, + ); + const vault = token.unpackAccount(storedRelaunch.newTokenVault, { + ...rawVault, + data: Buffer.from(rawVault.data), + } as any); + assert.equal(vault.amount.toString(), TOTAL_MINTED.toString()); + + const metadata = await this.banksClient.getAccount( + getMetadataAddr(newMint)[0], + ); + assert.isNotNull(metadata); + }); + + it("stores one shared vault for USDC-quoted sources", async function () { + const usdcPool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: MAINNET_USDC, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: USDC_POOL_QUOTE_RESERVE, + }); + + const newMint = await initializeWithParams.call(this, { + sourcePool: usdcPool.pool, + sourceQuoteMint: MAINNET_USDC, + }); + + const relaunch = client.getRelaunchAddress({ newMint }); + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const storedRelaunch = await client.fetchRelaunch(relaunch); + + assert.isTrue(storedRelaunch.sourceQuoteMint.equals(MAINNET_USDC)); + assert.isTrue( + storedRelaunch.sourceQuoteVault.equals(storedRelaunch.usdcVault), + ); + assert.isTrue( + storedRelaunch.usdcVault.equals( + token.getAssociatedTokenAddressSync(MAINNET_USDC, relaunchSigner, true), + ), + ); + }); + + it("initializes with a mint created in an earlier transaction", async function () { + const { newMint, instructions } = await client.createNewMintIxs(); + + const tx = new Transaction().add(...instructions); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + + const storedRelaunch = await client.getRelaunch({ newMint }); + assert.isTrue(storedRelaunch.newMint.equals(newMint)); + }); + + it("fails when the mint authority does not sign", async function () { + const mintAuthority = Keypair.generate(); + const newMint = await createRawMint(this.banksClient, this.payer, { + mintAuthority: mintAuthority.publicKey, + }); + + const ix = await client + .initializeRelaunchIx({ + newMint, + mintAuthority: mintAuthority.publicKey, + ...defaultParams.call(this), + }) + .instruction(); + for (const key of ix.keys) { + if (key.pubkey.equals(mintAuthority.publicKey)) { + key.isSigner = false; + } + } + + const tx = new Transaction().add(ix); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + + const result = await this.banksClient.tryProcessTransaction(tx); + assert.isNotNull(result.result); + assert.isTrue( + result.meta.logMessages.some((log) => log.includes("AccountNotSigner")), + ); + }); + + it("fails when the wrong key signs as mint authority", async function () { + const newMint = await createRawMint(this.banksClient, this.payer, { + mintAuthority: this.payer.publicKey, + }); + const wrongAuthority = Keypair.generate(); + + try { + await client + .initializeRelaunchIx({ + newMint, + mintAuthority: wrongAuthority.publicKey, + ...defaultParams.call(this), + }) + .signers([wrongAuthority]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintMintMintAuthority"); + } + }); + + it("fails when the mint is pre-pointed at the relaunch signer", async function () { + const { newMint, instructions } = await client.createNewMintIxs(); + const relaunch = client.getRelaunchAddress({ newMint }); + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + + // Recreate launchpad's convention: mint born with authority already set + // to the program PDA. A PDA can't sign, so this mint is uninitializable. + const createAccountIx = instructions[0]; + const tx = new Transaction().add( + createAccountIx, + token.createInitializeMint2Instruction(newMint, 6, relaunchSigner, null), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + try { + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintMintMintAuthority"); + } + }); + + it("fails when the new mint has non-zero supply", async function () { + const newMint = await createRawMint(this.banksClient, this.payer, { + mintAuthority: this.payer.publicKey, + }); + + const ata = token.getAssociatedTokenAddressSync( + newMint, + this.payer.publicKey, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + this.payer.publicKey, + ata, + this.payer.publicKey, + newMint, + ), + token.createMintToInstruction( + newMint, + ata, + this.payer.publicKey, + 100_000_000, + ), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + try { + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "SupplyNonZero"); + } + }); + + it("fails when the new mint has a freeze authority", async function () { + const newMint = await createRawMint(this.banksClient, this.payer, { + mintAuthority: this.payer.publicKey, + freezeAuthority: this.payer.publicKey, + }); + + try { + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "FreezeAuthoritySet"); + } + }); + + it("fails when the new mint has wrong decimals", async function () { + const newMint = await createRawMint(this.banksClient, this.payer, { + decimals: 9, + mintAuthority: this.payer.publicKey, + }); + + try { + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintMintDecimals"); + } + }); + + it("initializes with a Token-2022 old mint carrying only metadata extensions", async function () { + const oldMint22 = await createT22MintWithMetadata( + this.banksClient, + this.payer, + ); + const pool22 = await writePumpPool({ + context: this.context, + baseMint: oldMint22, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + + const newMint = await initializeWithParams.call(this, { + oldMint: oldMint22, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + sourcePool: pool22.pool, + }); + + const relaunch = client.getRelaunchAddress({ newMint }); + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const storedRelaunch = await client.fetchRelaunch(relaunch); + + assert.isTrue(storedRelaunch.oldMint.equals(oldMint22)); + assert.isTrue( + storedRelaunch.oldTokenVault.equals( + token.getAssociatedTokenAddressSync( + oldMint22, + relaunchSigner, + true, + token.TOKEN_2022_PROGRAM_ID, + ), + ), + ); + assert.equal(storedRelaunch.oldSupplySnapshot.toString(), "0"); + }); + + it("fails closed on a Token-2022 old mint with a transfer-fee extension", async function () { + const oldMint22 = await createT22MintWithTransferFee( + this.banksClient, + this.payer, + ); + const pool22 = await writePumpPool({ + context: this.context, + baseMint: oldMint22, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + + await expectInitializeToFail.call( + this, + { + oldMint: oldMint22, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + sourcePool: pool22.pool, + }, + "ForbiddenOldMintExtension", + ); + }); + + it("fails when the source pool is not owned by pump_amm", async function () { + const foreignPool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + owner: token.TOKEN_PROGRAM_ID, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: foreignPool.pool }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the source pool index is not 0", async function () { + const indexedPool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + index: 1, + creator: getPumpPoolAuthorityAddr(oldMint), + }); + + await expectInitializeToFail.call( + this, + { sourcePool: indexedPool.pool }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the source pool has a different base mint", async function () { + const otherMint = Keypair.generate().publicKey; + const otherPool = await writePumpPool({ + context: this.context, + baseMint: otherMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: otherPool.pool }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the source pool creator is not the pool-authority PDA", async function () { + const squattedPool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + creator: Keypair.generate().publicKey, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: squattedPool.pool }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when a canonical-looking pool sits at the wrong address", async function () { + // A byte-for-byte copy of the canonical pool at a different address + // passes every field check — only the derived-address check catches it. + const impostor = Keypair.generate().publicKey; + const canonicalPool = await this.banksClient.getAccount(pool.pool); + this.context.setAccount(impostor, { + data: Buffer.from(canonicalPool.data), + owner: canonicalPool.owner, + lamports: Number(canonicalPool.lamports), + executable: false, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: impostor }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the quote mint is neither WSOL nor USDC", async function () { + const bogusQuoteMint = await createRawMint(this.banksClient, this.payer, { + mintAuthority: this.payer.publicKey, + }); + const bogusPool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: bogusQuoteMint, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: USDC_POOL_QUOTE_RESERVE, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: bogusPool.pool, sourceQuoteMint: bogusQuoteMint }, + "InvalidQuoteMint", + ); + }); + + it("fails when the quote mint does not match the source pool's quote mint", async function () { + await expectInitializeToFail.call( + this, + { sourceQuoteMint: MAINNET_USDC }, + "SourcePoolQuoteMintMismatch", + ); + }); + + it("validates the threshold bounds", async function () { + await expectInitializeToFail.call( + this, + { thresholdBps: 0 }, + "InvalidThresholdBps", + ); + await expectInitializeToFail.call( + this, + { thresholdBps: 10_001 }, + "InvalidThresholdBps", + ); + + const newMint = await initializeWithParams.call(this, { + thresholdBps: 10_000, + }); + const storedRelaunch = await client.getRelaunch({ newMint }); + assert.equal(storedRelaunch.thresholdBps, 10_000); + }); + + it("fails when the deposit period exceeds the cap", async function () { + await expectInitializeToFail.call( + this, + { secondsForDeposits: MAX_SECONDS_FOR_DEPOSITS + 1 }, + "InvalidSecondsForDeposits", + ); + }); + + it("initializes without a spending limit", async function () { + const newMint = await initializeWithParams.call(this, { + monthlySpendingLimitAmount: new BN(0), + monthlySpendingLimitMembers: [], + }); + + const storedRelaunch = await client.getRelaunch({ newMint }); + assert.equal(storedRelaunch.monthlySpendingLimitAmount.toString(), "0"); + assert.equal(storedRelaunch.monthlySpendingLimitMembers.length, 0); + }); + + it("validates the spending limit config", async function () { + // A half-set config fails in either direction: an amount nobody can + // spend, or members with nothing to spend. + await expectInitializeToFail.call( + this, + { monthlySpendingLimitAmount: new BN(0) }, + "InvalidMonthlySpendingLimit", + ); + await expectInitializeToFail.call( + this, + { monthlySpendingLimitMembers: [] }, + "InvalidMonthlySpendingLimit", + ); + await expectInitializeToFail.call( + this, + { + monthlySpendingLimitMembers: [ + this.payer.publicKey, + this.payer.publicKey, + ], + }, + "InvalidMonthlySpendingLimitMembers", + ); + // 11 member pubkeys push the transaction past the packet size limit, so + // this one goes through a v0 transaction with a lookup table. + const { newMint, instructions } = await client.createNewMintIxs(); + const initTx = await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + monthlySpendingLimitMembers: Array.from( + { length: 11 }, + () => Keypair.generate().publicKey, + ), + }) + .preInstructions(instructions) + .transaction(); + + const lookupTable = await createLookupTableForTransaction(initTx, this); + const message = new TransactionMessage({ + payerKey: this.payer.publicKey, + recentBlockhash: (await this.banksClient.getLatestBlockhash())[0], + instructions: initTx.instructions, + }).compileToV0Message([lookupTable]); + const tx = new VersionedTransaction(message); + tx.sign([this.payer]); + + const result = await this.banksClient.tryProcessTransaction(tx); + assert.isNotNull(result.result); + assert.isTrue( + result.meta.logMessages.some((log) => + log.includes("InvalidMonthlySpendingLimitMembers"), + ), + ); + }); + + it("fails to initialize the same new mint twice", async function () { + const { newMint, instructions } = await client.createNewMintIxs(); + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .preInstructions(instructions) + .rpc(); + + try { + await client + .initializeRelaunchIx({ + newMint, + ...defaultParams.call(this), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + const details = [e.message, ...(e.logs ?? [])].join(" "); + assert.include(details, "already in use"); + } + }); + + it("allows a rival relaunch for the same old mint under a different new mint", async function () { + const firstNewMint = await initializeWithParams.call(this); + const secondNewMint = await initializeWithParams.call(this); + + const first = await client.getRelaunch({ newMint: firstNewMint }); + const second = await client.getRelaunch({ newMint: secondNewMint }); + + assert.isTrue(first.oldMint.equals(oldMint)); + assert.isTrue(second.oldMint.equals(oldMint)); + assert.isTrue(first.sourcePool.equals(second.sourcePool)); + assert.isFalse(first.relaunchSigner.equals(second.relaunchSigner)); + assert.isFalse(first.oldTokenVault.equals(second.oldTokenVault)); + }); +} From b984f3807e56b4344d450ee581172a9a34d29f20 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 5 Aug 2026 22:55:48 +0200 Subject: [PATCH 08/31] start_deposits ix, sdk, tests --- programs/relaunch/src/error.rs | 2 + programs/relaunch/src/events.rs | 7 ++ programs/relaunch/src/instructions/mod.rs | 2 + .../src/instructions/start_deposits.rs | 46 ++++++++ programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 13 +++ sdk/src/relaunch/v0.1/types/relaunch.ts | 106 ++++++++++++++++++ tasks.md | 10 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/startDeposits.test.ts | 101 +++++++++++++++++ 10 files changed, 285 insertions(+), 9 deletions(-) create mode 100644 programs/relaunch/src/instructions/start_deposits.rs create mode 100644 tests/relaunch/unit/startDeposits.test.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 94ca9dcc3..0d6b98a93 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -22,4 +22,6 @@ pub enum RelaunchError { InvalidMonthlySpendingLimit, #[msg("There can be at most 10 monthly spending limit members, without duplicates")] InvalidMonthlySpendingLimitMembers, + #[msg("Relaunch must be in the Initialized state")] + RelaunchNotInitialized, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index e8b105b8e..8748518a3 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -41,3 +41,10 @@ pub struct RelaunchInitializedEvent { pub team_address: Pubkey, pub pda_bump: u8, } + +#[event] +pub struct DepositsStartedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub admin: Pubkey, +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 1d616aa77..5668526c2 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,3 +1,5 @@ pub mod initialize_relaunch; +pub mod start_deposits; pub use initialize_relaunch::*; +pub use start_deposits::*; diff --git a/programs/relaunch/src/instructions/start_deposits.rs b/programs/relaunch/src/instructions/start_deposits.rs new file mode 100644 index 000000000..9e6a71f16 --- /dev/null +++ b/programs/relaunch/src/instructions/start_deposits.rs @@ -0,0 +1,46 @@ +use anchor_lang::prelude::*; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, DepositsStartedEvent}; +use crate::state::{Relaunch, RelaunchState}; + +#[event_cpi] +#[derive(Accounts)] +pub struct StartDeposits<'info> { + #[account( + mut, + has_one = admin, + )] + pub relaunch: Account<'info, Relaunch>, + + pub admin: Signer<'info>, +} + +impl StartDeposits<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Initialized, + RelaunchError::RelaunchNotInitialized + ); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch = &mut ctx.accounts.relaunch; + let clock = Clock::get()?; + + relaunch.state = RelaunchState::Live; + relaunch.unix_timestamp_started = Some(clock.unix_timestamp); + + relaunch.seq_num += 1; + + emit_cpi!(DepositsStartedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: ctx.accounts.relaunch.key(), + admin: ctx.accounts.admin.key(), + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index ef8f164ea..1952f9484 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -40,4 +40,9 @@ pub mod relaunch { ) -> Result<()> { InitializeRelaunch::handle(ctx, args) } + + #[access_control(ctx.accounts.validate())] + pub fn start_deposits(ctx: Context) -> Result<()> { + StartDeposits::handle(ctx) + } } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index ecb074990..676c0f4ca 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -168,6 +168,19 @@ export class RelaunchClient { ]); } + startDepositsIx({ + relaunch, + admin = this.provider.publicKey, + }: { + relaunch: PublicKey; + admin?: PublicKey; + }) { + return this.relaunchProgram.methods.startDeposits().accounts({ + relaunch, + admin, + }); + } + // Builds the create-mint-to-self pre-instructions: a `createAccountWithSeed` // + `initializeMint2` pair with the payer as mint authority, so // `initialize_relaunch` can take the authority from a mint the payer diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index caf7bcec4..9d2e2a6b2 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -141,6 +141,32 @@ export type Relaunch = { }, ]; }, + { + name: "startDeposits"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "admin"; + isMut: false; + isSigner: true; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, ]; accounts: [ { @@ -597,6 +623,28 @@ export type Relaunch = { }, ]; }, + { + name: "DepositsStartedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "admin"; + type: "publicKey"; + index: false; + }, + ]; + }, ]; errors: [ { @@ -649,6 +697,11 @@ export type Relaunch = { name: "InvalidMonthlySpendingLimitMembers"; msg: "There can be at most 10 monthly spending limit members, without duplicates"; }, + { + code: 6010; + name: "RelaunchNotInitialized"; + msg: "Relaunch must be in the Initialized state"; + }, ]; }; @@ -795,6 +848,32 @@ export const IDL: Relaunch = { }, ], }, + { + name: "startDeposits", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "admin", + isMut: false, + isSigner: true, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, ], accounts: [ { @@ -1251,6 +1330,28 @@ export const IDL: Relaunch = { }, ], }, + { + name: "DepositsStartedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "admin", + type: "publicKey", + index: false, + }, + ], + }, ], errors: [ { @@ -1303,5 +1404,10 @@ export const IDL: Relaunch = { name: "InvalidMonthlySpendingLimitMembers", msg: "There can be at most 10 monthly spending limit members, without duplicates", }, + { + code: 6010, + name: "RelaunchNotInitialized", + msg: "Relaunch must be in the Initialized state", + }, ], }; diff --git a/tasks.md b/tasks.md index b323f942b..95e30516a 100644 --- a/tasks.md +++ b/tasks.md @@ -23,19 +23,11 @@ ## Tasks -### Phase 1: initialize_relaunch + start_deposits - -> Reference: `relaunch-implementation-plan.md` → "Stage 1 — initialize_relaunch + start_deposits" - -- [NEXT] 1.2 `start_deposits` (instruction + SDK + tests) - - `has_one = admin` signer gate, `Initialized → Live`, stamp `unix_timestamp_started`, event - - Tests: happy path, non-admin fails, double-start fails (compute-budget trick for unique sigs) - ### Phase 2: deposit + failure loop > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" -- [ ] 2.1 Token-2022 test availability check +- [NEXT] 2.1 Token-2022 test availability check - ~~Confirm bankrun ships Token-2022 as a builtin~~ — confirmed during 0.4: it's a builtin (`tests/relaunch/unit/scaffold.test.ts` runs a plain Token-2022 old mint), no fixture needed - Extend `setupRelaunch()` to produce Token-2022 old mints (with allowed metadata extensions) diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 2b17dca86..835e88ac8 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -1,6 +1,7 @@ import scaffold from "./unit/scaffold.test.js"; import venues from "./unit/venues.test.js"; import initializeRelaunch from "./unit/initializeRelaunch.test.js"; +import startDeposits from "./unit/startDeposits.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -15,4 +16,5 @@ export default function suite() { describe("scaffold", scaffold); describe("venues", venues); describe("#initialize_relaunch", initializeRelaunch); + describe("#start_deposits", startDeposits); } diff --git a/tests/relaunch/unit/startDeposits.test.ts b/tests/relaunch/unit/startDeposits.test.ts new file mode 100644 index 000000000..36caff3db --- /dev/null +++ b/tests/relaunch/unit/startDeposits.test.ts @@ -0,0 +1,101 @@ +import { ComputeBudgetProgram, Keypair, PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { RelaunchClient } from "@metadaoproject/programs"; +import { setupRelaunch } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +export default function suite() { + let client: RelaunchClient; + let relaunch: PublicKey; + + before(function () { + client = this.relaunch; + }); + + beforeEach(async function () { + const { oldMint } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + ({ relaunch } = await client.initializeRelaunch({ + oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + })); + }); + + it("starts deposits", async function () { + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.initialized); + assert.isNull(storedRelaunch.unixTimestampStarted); + + const clock = await this.banksClient.getClock(); + + await client.startDepositsIx({ relaunch }).rpc(); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.live); + assert.equal( + storedRelaunch.unixTimestampStarted.toString(), + clock.unixTimestamp.toString(), + ); + assert.equal(storedRelaunch.seqNum.toString(), "1"); + }); + + it("fails when a non-admin starts deposits", async function () { + const nonAdmin = Keypair.generate(); + + try { + await client + .startDepositsIx({ relaunch, admin: nonAdmin.publicKey }) + .signers([nonAdmin]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintHasOne"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.initialized); + }); + + it("fails when deposits have already been started", async function () { + await client.startDepositsIx({ relaunch }).rpc(); + + try { + // The compute-unit-limit instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first call. + await client + .startDepositsIx({ relaunch }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotInitialized"); + } + }); +} From eaebc8992044bd76c97324611c7e5c67ae8c00bd Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 5 Aug 2026 23:14:46 +0200 Subject: [PATCH 09/31] adjust utils --- tasks.md | 6 +- .../relaunch/unit/initializeRelaunch.test.ts | 58 +------------- tests/relaunch/unit/scaffold.test.ts | 12 ++- tests/relaunch/utils.ts | 77 ++++++++++++++----- 4 files changed, 74 insertions(+), 79 deletions(-) diff --git a/tasks.md b/tasks.md index 95e30516a..fd0671953 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" -- [NEXT] 2.1 Token-2022 test availability check - - ~~Confirm bankrun ships Token-2022 as a builtin~~ — confirmed during 0.4: it's a builtin (`tests/relaunch/unit/scaffold.test.ts` runs a plain Token-2022 old mint), no fixture needed - - Extend `setupRelaunch()` to produce Token-2022 old mints (with allowed metadata extensions) - -- [ ] 2.2 `deposit` (instruction + SDK + tests) +- [NEXT] 2.2 `deposit` (instruction + SDK + tests) - `Live` + window gate, `transfer_checked` under old mint's owner program, `init_if_needed` DepositRecord, accumulate record + total, event - Tests per plan §3: both token programs, accumulation, multiple depositors, zero amount, window/state gates diff --git a/tests/relaunch/unit/initializeRelaunch.test.ts b/tests/relaunch/unit/initializeRelaunch.test.ts index 0cd6fcf62..e0a946530 100644 --- a/tests/relaunch/unit/initializeRelaunch.test.ts +++ b/tests/relaunch/unit/initializeRelaunch.test.ts @@ -16,7 +16,7 @@ import { } from "@metadaoproject/programs"; import { BanksClient } from "solana-bankrun"; import { createLookupTableForTransaction } from "../../utils.js"; -import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { setupRelaunch, createOldMint, DEFAULT_OLD_SUPPLY } from "../utils.js"; import { getPumpPoolAuthorityAddr, writePumpPool, @@ -78,59 +78,6 @@ async function createRawMint( return mintKeypair.publicKey; } -// A pump-style Token-2022 mint: metadata pointer + mint-embedded token -// metadata, nothing else. -async function createT22MintWithMetadata( - banksClient: BanksClient, - payer: Keypair, -): Promise { - const mintKeypair = Keypair.generate(); - const mint = mintKeypair.publicKey; - const rent = await banksClient.getRent(); - const mintLen = token.getMintLen([token.ExtensionType.MetadataPointer]); - - const tx = new Transaction().add( - SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: mint, - // Overfund so the token metadata TLV realloc stays rent-exempt. - lamports: Number(rent.minimumBalance(BigInt(mintLen + 500))), - space: mintLen, - programId: token.TOKEN_2022_PROGRAM_ID, - }), - token.createInitializeMetadataPointerInstruction( - mint, - payer.publicKey, - mint, - token.TOKEN_2022_PROGRAM_ID, - ), - token.createInitializeMint2Instruction( - mint, - 6, - payer.publicKey, - null, - token.TOKEN_2022_PROGRAM_ID, - ), - token.createInitializeInstruction({ - programId: token.TOKEN_2022_PROGRAM_ID, - mint, - metadata: mint, - name: "Old Token", - symbol: "OLD", - uri: "https://example.com/old.json", - mintAuthority: payer.publicKey, - updateAuthority: payer.publicKey, - }), - ); - - tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; - tx.feePayer = payer.publicKey; - tx.sign(payer, mintKeypair); - await banksClient.processTransaction(tx); - - return mint; -} - async function createT22MintWithTransferFee( banksClient: BanksClient, payer: Keypair, @@ -558,9 +505,10 @@ export default function suite() { }); it("initializes with a Token-2022 old mint carrying only metadata extensions", async function () { - const oldMint22 = await createT22MintWithMetadata( + const oldMint22 = await createOldMint( this.banksClient, this.payer, + token.TOKEN_2022_PROGRAM_ID, ); const pool22 = await writePumpPool({ context: this.context, diff --git a/tests/relaunch/unit/scaffold.test.ts b/tests/relaunch/unit/scaffold.test.ts index bdd86f91b..c342f4adf 100644 --- a/tests/relaunch/unit/scaffold.test.ts +++ b/tests/relaunch/unit/scaffold.test.ts @@ -33,6 +33,16 @@ export default function suite() { assert.equal(mint.decimals, 6); assert.equal(mint.supply.toString(), DEFAULT_OLD_SUPPLY.toString()); + const extensions = token.getExtensionTypes(mint.tlvData); + if (oldTokenProgram.equals(token.TOKEN_2022_PROGRAM_ID)) { + assert.sameMembers(extensions, [ + token.ExtensionType.MetadataPointer, + token.ExtensionType.TokenMetadata, + ]); + } else { + assert.isEmpty(extensions); + } + const rawTokenAccount = await this.banksClient.getAccount(payerOldTokenAccount); const tokenAccount = token.unpackAccount( @@ -47,7 +57,7 @@ export default function suite() { await assertOldMintSetup.call(this, token.TOKEN_PROGRAM_ID); }); - it("setupRelaunch creates a Token-2022 old mint with supply", async function () { + it("setupRelaunch creates a Token-2022 old mint with metadata extensions and supply", async function () { await assertOldMintSetup.call(this, token.TOKEN_2022_PROGRAM_ID); }); } diff --git a/tests/relaunch/utils.ts b/tests/relaunch/utils.ts index fd95c5ff3..f4d20413b 100644 --- a/tests/relaunch/utils.ts +++ b/tests/relaunch/utils.ts @@ -11,6 +11,9 @@ import { BanksClient } from "solana-bankrun"; // 1B tokens at 6 decimals — the supply of a pump token. export const DEFAULT_OLD_SUPPLY = 1_000_000_000n * 10n ** 6n; +// Classic SPL mints are plain; Token-2022 mints get the pump-style shape: +// metadata pointer + mint-embedded token metadata, the only extensions the +// program's allowlist accepts. export async function createOldMint( banksClient: BanksClient, payer: Signer, @@ -18,25 +21,63 @@ export async function createOldMint( decimals: number = 6, ): Promise { const mintKeypair = Keypair.generate(); + const mint = mintKeypair.publicKey; const rent = await banksClient.getRent(); - const lamports = Number(rent.minimumBalance(BigInt(token.MINT_SIZE))); - const tx = new Transaction().add( - SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: mintKeypair.publicKey, - lamports, - space: token.MINT_SIZE, - programId: tokenProgram, - }), - token.createInitializeMint2Instruction( - mintKeypair.publicKey, - decimals, - payer.publicKey, - null, - tokenProgram, - ), - ); + const tx = new Transaction(); + if (tokenProgram.equals(token.TOKEN_2022_PROGRAM_ID)) { + const mintLen = token.getMintLen([token.ExtensionType.MetadataPointer]); + tx.add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + // Overfund so the token metadata TLV realloc stays rent-exempt. + lamports: Number(rent.minimumBalance(BigInt(mintLen + 500))), + space: mintLen, + programId: tokenProgram, + }), + token.createInitializeMetadataPointerInstruction( + mint, + payer.publicKey, + mint, + tokenProgram, + ), + token.createInitializeMint2Instruction( + mint, + decimals, + payer.publicKey, + null, + tokenProgram, + ), + token.createInitializeInstruction({ + programId: tokenProgram, + mint, + metadata: mint, + name: "Old Token", + symbol: "OLD", + uri: "https://example.com/old.json", + mintAuthority: payer.publicKey, + updateAuthority: payer.publicKey, + }), + ); + } else { + tx.add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: Number(rent.minimumBalance(BigInt(token.MINT_SIZE))), + space: token.MINT_SIZE, + programId: tokenProgram, + }), + token.createInitializeMint2Instruction( + mint, + decimals, + payer.publicKey, + null, + tokenProgram, + ), + ); + } tx.recentBlockhash = (await banksClient.getLatestBlockhash())[0]; tx.feePayer = payer.publicKey; @@ -44,7 +85,7 @@ export async function createOldMint( await banksClient.processTransaction(tx); - return mintKeypair.publicKey; + return mint; } export type SetupRelaunchParams = { From 2fc01938afcbc8bd3cc519c863f1e1eec6593838 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 5 Aug 2026 23:45:52 +0200 Subject: [PATCH 10/31] deposit ix, sdk, tests --- programs/relaunch/src/error.rs | 8 + programs/relaunch/src/events.rs | 12 + programs/relaunch/src/instructions/deposit.rs | 132 ++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 77 ++++ sdk/src/relaunch/v0.1/types/relaunch.ts | 294 +++++++++++++ tasks.md | 6 +- .../unit/removeWhitelistedUser.test.ts | 19 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/deposit.test.ts | 402 ++++++++++++++++++ 11 files changed, 951 insertions(+), 8 deletions(-) create mode 100644 programs/relaunch/src/instructions/deposit.rs create mode 100644 tests/relaunch/unit/deposit.test.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 0d6b98a93..5865c0a68 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -24,4 +24,12 @@ pub enum RelaunchError { InvalidMonthlySpendingLimitMembers, #[msg("Relaunch must be in the Initialized state")] RelaunchNotInitialized, + #[msg("Relaunch must be in the Live state")] + RelaunchNotLive, + #[msg("Deposit window has closed")] + DepositWindowClosed, + #[msg("Amount must be greater than zero")] + InvalidAmount, + #[msg("Insufficient balance")] + InsufficientFunds, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 8748518a3..f149d6f16 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -48,3 +48,15 @@ pub struct DepositsStartedEvent { pub relaunch: Pubkey, pub admin: Pubkey, } + +#[event] +pub struct TokensDepositedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub depositor: Pubkey, + pub deposit_record: Pubkey, + pub amount: u64, + pub total_deposited: u64, + pub total_deposited_by_depositor: u64, + pub deposit_record_seq_num: u64, +} diff --git a/programs/relaunch/src/instructions/deposit.rs b/programs/relaunch/src/instructions/deposit.rs new file mode 100644 index 000000000..1f495ad9c --- /dev/null +++ b/programs/relaunch/src/instructions/deposit.rs @@ -0,0 +1,132 @@ +use anchor_lang::prelude::*; +use anchor_spl::token_interface; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, TokensDepositedEvent}; +use crate::state::{DepositRecord, Relaunch, RelaunchState}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct DepositArgs { + pub amount: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct Deposit<'info> { + #[account( + mut, + has_one = old_mint, + has_one = old_token_vault, + )] + pub relaunch: Box>, + + #[account( + init_if_needed, + payer = payer, + space = 8 + DepositRecord::INIT_SPACE, + seeds = [b"deposit_record", relaunch.key().as_ref(), depositor.key().as_ref()], + bump + )] + pub deposit_record: Box>, + + #[account(mint::token_program = old_token_program)] + pub old_mint: Box>, + + #[account(mut)] + pub old_token_vault: Box>, + + pub depositor: Signer<'info>, + + #[account( + mut, + associated_token::mint = old_mint, + associated_token::authority = depositor, + associated_token::token_program = old_token_program, + )] + pub depositor_token_account: Box>, + + #[account(mut)] + pub payer: Signer<'info>, + + pub old_token_program: Interface<'info, token_interface::TokenInterface>, + pub system_program: Program<'info, System>, +} + +impl Deposit<'_> { + pub fn validate(&self, args: &DepositArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Live, + RelaunchError::RelaunchNotLive + ); + + let clock = Clock::get()?; + require_gt!( + self.relaunch.unix_timestamp_started.unwrap() + + self.relaunch.seconds_for_deposits as i64, + clock.unix_timestamp, + RelaunchError::DepositWindowClosed + ); + + require_gt!(args.amount, 0, RelaunchError::InvalidAmount); + + require_gte!( + self.depositor_token_account.amount, + args.amount, + RelaunchError::InsufficientFunds + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: DepositArgs) -> Result<()> { + token_interface::transfer_checked( + CpiContext::new( + ctx.accounts.old_token_program.to_account_info(), + token_interface::TransferChecked { + from: ctx.accounts.depositor_token_account.to_account_info(), + mint: ctx.accounts.old_mint.to_account_info(), + to: ctx.accounts.old_token_vault.to_account_info(), + authority: ctx.accounts.depositor.to_account_info(), + }, + ), + args.amount, + ctx.accounts.old_mint.decimals, + )?; + + let deposit_record = &mut ctx.accounts.deposit_record; + + // A fresh record holds the default pubkey, an existing one + // holds the depositor's pubkey. + if deposit_record.depositor == ctx.accounts.depositor.key() { + deposit_record.amount_deposited += args.amount; + deposit_record.seq_num += 1; + } else { + deposit_record.set_inner(DepositRecord { + relaunch: ctx.accounts.relaunch.key(), + depositor: ctx.accounts.depositor.key(), + amount_deposited: args.amount, + claimed: false, + seq_num: 0, + pda_bump: ctx.bumps.deposit_record, + }); + } + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.total_deposited += args.amount; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(TokensDepositedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: ctx.accounts.relaunch.key(), + depositor: ctx.accounts.depositor.key(), + deposit_record: ctx.accounts.deposit_record.key(), + amount: args.amount, + total_deposited: ctx.accounts.relaunch.total_deposited, + total_deposited_by_depositor: ctx.accounts.deposit_record.amount_deposited, + deposit_record_seq_num: ctx.accounts.deposit_record.seq_num, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 5668526c2..761163b51 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,5 +1,7 @@ +pub mod deposit; pub mod initialize_relaunch; pub mod start_deposits; +pub use deposit::*; pub use initialize_relaunch::*; pub use start_deposits::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 1952f9484..0fbd83e8a 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -45,4 +45,9 @@ pub mod relaunch { pub fn start_deposits(ctx: Context) -> Result<()> { StartDeposits::handle(ctx) } + + #[access_control(ctx.accounts.validate(&args))] + pub fn deposit(ctx: Context, args: DepositArgs) -> Result<()> { + Deposit::handle(ctx, args) + } } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 676c0f4ca..141ce4a32 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -181,6 +181,83 @@ export class RelaunchClient { }); } + depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount, + depositor = this.provider.publicKey, + payer = this.provider.publicKey, + }: { + relaunch: PublicKey; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + amount: BN; + depositor?: PublicKey; + payer?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + oldTokenProgram, + ); + const depositorTokenAccount = getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + + return this.relaunchProgram.methods.deposit({ amount }).accounts({ + relaunch, + depositRecord, + oldMint, + oldTokenVault, + depositor, + depositorTokenAccount, + payer, + oldTokenProgram, + }); + } + + // Deposits from the provider wallet, reading the old mint and its owner + // program from the stored relaunch. + async deposit({ + relaunch, + amount, + }: { + relaunch: PublicKey; + amount: BN; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + const oldMintAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.oldMint, + ); + if (oldMintAccount === null) { + throw new Error( + `old mint ${storedRelaunch.oldMint.toBase58()} does not exist`, + ); + } + + return this.depositIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + oldTokenProgram: oldMintAccount.owner, + amount, + }).rpc(); + } + // Builds the create-mint-to-self pre-instructions: a `createAccountWithSeed` // + `initializeMint2` pair with the payer as mint authority, so // `initialize_relaunch` can take the authority from a mint the payer diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 9d2e2a6b2..ff6d2c618 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -167,6 +167,74 @@ export type Relaunch = { ]; args: []; }, + { + name: "deposit"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "depositRecord"; + isMut: true; + isSigner: false; + }, + { + name: "oldMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "depositor"; + isMut: false; + isSigner: true; + }, + { + name: "depositorTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "oldTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "DepositArgs"; + }; + }, + ]; + }, ]; accounts: [ { @@ -432,6 +500,18 @@ export type Relaunch = { ]; }; }, + { + name: "DepositArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "amount"; + type: "u64"; + }, + ]; + }; + }, { name: "InitializeRelaunchArgs"; type: { @@ -645,6 +725,53 @@ export type Relaunch = { }, ]; }, + { + name: "TokensDepositedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "depositor"; + type: "publicKey"; + index: false; + }, + { + name: "depositRecord"; + type: "publicKey"; + index: false; + }, + { + name: "amount"; + type: "u64"; + index: false; + }, + { + name: "totalDeposited"; + type: "u64"; + index: false; + }, + { + name: "totalDepositedByDepositor"; + type: "u64"; + index: false; + }, + { + name: "depositRecordSeqNum"; + type: "u64"; + index: false; + }, + ]; + }, ]; errors: [ { @@ -702,6 +829,26 @@ export type Relaunch = { name: "RelaunchNotInitialized"; msg: "Relaunch must be in the Initialized state"; }, + { + code: 6011; + name: "RelaunchNotLive"; + msg: "Relaunch must be in the Live state"; + }, + { + code: 6012; + name: "DepositWindowClosed"; + msg: "Deposit window has closed"; + }, + { + code: 6013; + name: "InvalidAmount"; + msg: "Amount must be greater than zero"; + }, + { + code: 6014; + name: "InsufficientFunds"; + msg: "Insufficient balance"; + }, ]; }; @@ -874,6 +1021,74 @@ export const IDL: Relaunch = { ], args: [], }, + { + name: "deposit", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "depositRecord", + isMut: true, + isSigner: false, + }, + { + name: "oldMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "depositor", + isMut: false, + isSigner: true, + }, + { + name: "depositorTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "oldTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "DepositArgs", + }, + }, + ], + }, ], accounts: [ { @@ -1139,6 +1354,18 @@ export const IDL: Relaunch = { ], }, }, + { + name: "DepositArgs", + type: { + kind: "struct", + fields: [ + { + name: "amount", + type: "u64", + }, + ], + }, + }, { name: "InitializeRelaunchArgs", type: { @@ -1352,6 +1579,53 @@ export const IDL: Relaunch = { }, ], }, + { + name: "TokensDepositedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "depositor", + type: "publicKey", + index: false, + }, + { + name: "depositRecord", + type: "publicKey", + index: false, + }, + { + name: "amount", + type: "u64", + index: false, + }, + { + name: "totalDeposited", + type: "u64", + index: false, + }, + { + name: "totalDepositedByDepositor", + type: "u64", + index: false, + }, + { + name: "depositRecordSeqNum", + type: "u64", + index: false, + }, + ], + }, ], errors: [ { @@ -1409,5 +1683,25 @@ export const IDL: Relaunch = { name: "RelaunchNotInitialized", msg: "Relaunch must be in the Initialized state", }, + { + code: 6011, + name: "RelaunchNotLive", + msg: "Relaunch must be in the Live state", + }, + { + code: 6012, + name: "DepositWindowClosed", + msg: "Deposit window has closed", + }, + { + code: 6013, + name: "InvalidAmount", + msg: "Amount must be greater than zero", + }, + { + code: 6014, + name: "InsufficientFunds", + msg: "Insufficient balance", + }, ], }; diff --git a/tasks.md b/tasks.md index fd0671953..d150993e5 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" -- [NEXT] 2.2 `deposit` (instruction + SDK + tests) - - `Live` + window gate, `transfer_checked` under old mint's owner program, `init_if_needed` DepositRecord, accumulate record + total, event - - Tests per plan §3: both token programs, accumulation, multiple depositors, zero amount, window/state gates - -- [ ] 2.3 `close_deposits` (instruction + SDK + tests) +- [NEXT] 2.3 `close_deposits` (instruction + SDK + tests) - Window elapsed; u128 threshold math → `SellPending` | `Failed`; stamp `unix_timestamp_closed`, event - Tests per plan §5: exact-threshold boundary, one-short → Failed, early close fails, 10^15-supply overflow case, permissionless crank diff --git a/tests/gatedMint/unit/removeWhitelistedUser.test.ts b/tests/gatedMint/unit/removeWhitelistedUser.test.ts index 713743f1d..1dd5e9ba0 100644 --- a/tests/gatedMint/unit/removeWhitelistedUser.test.ts +++ b/tests/gatedMint/unit/removeWhitelistedUser.test.ts @@ -1,4 +1,4 @@ -import { Keypair, PublicKey } from "@solana/web3.js"; +import { ComputeBudgetProgram, Keypair, PublicKey } from "@solana/web3.js"; import * as token from "@solana/spl-token"; import { assert } from "chai"; import { mintTo } from "spl-token-bankrun"; @@ -191,8 +191,21 @@ export default function suite() { .rpc(); assert.isNull(await this.banksClient.getAccount(addr)); - // The PDA was freed, so the same user can be whitelisted again. - await whitelistUser(gatedMintClient, mint, admin, user, this.payer); + // The PDA was freed, so the same user can be whitelisted again. The + // compute-unit-limit instruction makes the transaction hash unique so + // the re-add isn't rejected as a duplicate of the first add. + await gatedMintClient + .addWhitelistedUserIx({ + mint, + authority: admin.publicKey, + user, + payer: this.payer.publicKey, + }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 200_001 }), + ]) + .signers([admin]) + .rpc(); assert.isNotNull(await this.banksClient.getAccount(addr)); }); diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 835e88ac8..e562d3394 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -2,6 +2,7 @@ import scaffold from "./unit/scaffold.test.js"; import venues from "./unit/venues.test.js"; import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; +import deposit from "./unit/deposit.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -17,4 +18,5 @@ export default function suite() { describe("venues", venues); describe("#initialize_relaunch", initializeRelaunch); describe("#start_deposits", startDeposits); + describe("#deposit", deposit); } diff --git a/tests/relaunch/unit/deposit.test.ts b/tests/relaunch/unit/deposit.test.ts new file mode 100644 index 000000000..33e914357 --- /dev/null +++ b/tests/relaunch/unit/deposit.test.ts @@ -0,0 +1,402 @@ +import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { getDepositRecordAddr, RelaunchClient } from "@metadaoproject/programs"; +import { getAccount } from "spl-token-bankrun"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let payerOldTokenAccount: PublicKey; + let relaunch: PublicKey; + let oldTokenVault: PublicKey; + + before(function () { + client = this.relaunch; + }); + + const setupLiveRelaunch = async function ( + this: Mocha.Context, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, + { start = true }: { start?: boolean } = {}, + ) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram: tokenProgram, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: tokenProgram, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + + if (start) { + await client.startDepositsIx({ relaunch }).rpc(); + } + + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const oldTokenVault = token.getAssociatedTokenAddressSync( + setup.oldMint, + relaunchSigner, + true, + tokenProgram, + ); + + return { ...setup, relaunch, oldTokenVault }; + }; + + // Creates the depositor's ATA and funds it with old tokens from the payer. + const fundDepositor = async function ( + this: Mocha.Context, + depositor: PublicKey, + amount: bigint, + ): Promise { + const ata = token.getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + this.payer.publicKey, + ata, + depositor, + oldMint, + oldTokenProgram, + ), + token.createTransferCheckedInstruction( + payerOldTokenAccount, + oldMint, + ata, + this.payer.publicKey, + amount, + 6, + [], + oldTokenProgram, + ), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + return ata; + }; + + beforeEach(async function () { + ({ + oldMint, + oldTokenProgram, + payerOldTokenAccount, + relaunch, + oldTokenVault, + } = await setupLiveRelaunch.call(this)); + }); + + it("deposits old tokens", async function () { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), // 100 tokens + }) + .rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "100000000"); + assert.equal(storedRelaunch.seqNum.toString(), "2"); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.relaunch.equals(relaunch)); + assert.isTrue(record.depositor.equals(this.payer.publicKey)); + assert.equal(record.amountDeposited.toString(), "100000000"); + assert.isFalse(record.claimed); + assert.equal(record.seqNum.toString(), "0"); + const [, recordBump] = getDepositRecordAddr({ + programId: client.getProgramId(), + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.pdaBump, recordBump); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "100000000"); + + const depositorAccount = await getAccount( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + depositorAccount.amount.toString(), + (DEFAULT_OLD_SUPPLY - 100_000_000n).toString(), + ); + }); + + it("deposits old tokens under Token-2022", async function () { + const setup = await setupLiveRelaunch.call( + this, + token.TOKEN_2022_PROGRAM_ID, + ); + + await client + .depositIx({ + relaunch: setup.relaunch, + oldMint: setup.oldMint, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + amount: new BN(100_000_000), + }) + .rpc(); + + const storedRelaunch = await client.fetchRelaunch(setup.relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "100000000"); + + const record = await client.getDepositRecord({ + relaunch: setup.relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), "100000000"); + + const vault = await getAccount( + this.banksClient, + setup.oldTokenVault, + undefined, + token.TOKEN_2022_PROGRAM_ID, + ); + assert.equal(vault.amount.toString(), "100000000"); + }); + + it("accumulates repeat deposits in the same record", async function () { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), + }) + .rpc(); + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(200_000_000), + }) + .rpc(); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), "300000000"); + assert.equal(record.seqNum.toString(), "1"); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "300000000"); + assert.equal(storedRelaunch.seqNum.toString(), "3"); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "300000000"); + }); + + it("tracks multiple depositors independently", async function () { + const alice = Keypair.generate(); + const bob = Keypair.generate(); + await fundDepositor.call(this, alice.publicKey, 1_000_000_000n); + await fundDepositor.call(this, bob.publicKey, 1_000_000_000n); + + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), + depositor: alice.publicKey, + }) + .signers([alice]) + .rpc(); + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(200_000_000), + depositor: bob.publicKey, + }) + .signers([bob]) + .rpc(); + + const aliceRecord = await client.getDepositRecord({ + relaunch, + depositor: alice.publicKey, + }); + assert.isTrue(aliceRecord.depositor.equals(alice.publicKey)); + assert.equal(aliceRecord.amountDeposited.toString(), "100000000"); + + const bobRecord = await client.getDepositRecord({ + relaunch, + depositor: bob.publicKey, + }); + assert.isTrue(bobRecord.depositor.equals(bob.publicKey)); + assert.equal(bobRecord.amountDeposited.toString(), "200000000"); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "300000000"); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "300000000"); + }); + + it("fails to deposit zero tokens", async function () { + try { + await client + .depositIx({ relaunch, oldMint, oldTokenProgram, amount: new BN(0) }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InvalidAmount"); + } + }); + + it("fails when the depositor balance is insufficient", async function () { + const depositor = Keypair.generate(); + await fundDepositor.call(this, depositor.publicKey, 100_000_000n); + + try { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_001), + depositor: depositor.publicKey, + }) + .signers([depositor]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InsufficientFunds"); + } + + const depositRecord = client.getDepositRecordAddress({ + relaunch, + depositor: depositor.publicKey, + }); + assert.isNull(await this.banksClient.getAccount(depositRecord)); + }); + + it("fails before deposits start", async function () { + const setup = await setupLiveRelaunch.call(this, token.TOKEN_PROGRAM_ID, { + start: false, + }); + + try { + await client + .depositIx({ + relaunch: setup.relaunch, + oldMint: setup.oldMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + amount: new BN(100_000_000), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotLive"); + } + }); + + it("fails after the deposit window closes", async function () { + await this.advanceBySeconds(ONE_WEEK - 10); + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), + }) + .rpc(); + + await this.advanceBySeconds(10); + try { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(200_000_000), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "DepositWindowClosed"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "100000000"); + }); + + it("fails with the wrong old-token program", async function () { + // The vault ATA re-derived under the wrong token program is an address + // that doesn't exist, so deserialization fails before the has_one check. + try { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + amount: new BN(100_000_000), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "AccountNotInitialized"); + } + }); + + it("fails when the destination is not the old token vault", async function () { + try { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), + }) + .accounts({ oldTokenVault: payerOldTokenAccount }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintHasOne"); + } + }); +} From f8116097d99014996c98112a72a14496f1101d05 Mon Sep 17 00:00:00 2001 From: Pileks Date: Thu, 6 Aug 2026 00:47:07 +0200 Subject: [PATCH 11/31] close_deposits ix, sdk, tests --- programs/relaunch/src/error.rs | 2 + programs/relaunch/src/events.rs | 9 + .../src/instructions/close_deposits.rs | 58 +++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 6 + sdk/src/relaunch/v0.1/types/relaunch.ts | 100 ++++++++ tasks.md | 6 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/closeDeposits.test.ts | 235 ++++++++++++++++++ 10 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 programs/relaunch/src/instructions/close_deposits.rs create mode 100644 tests/relaunch/unit/closeDeposits.test.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 5865c0a68..bd5c2422d 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -32,4 +32,6 @@ pub enum RelaunchError { InvalidAmount, #[msg("Insufficient balance")] InsufficientFunds, + #[msg("Deposit window is still open")] + DepositWindowStillOpen, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index f149d6f16..2a09e6a35 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -1,5 +1,7 @@ use anchor_lang::prelude::*; +use crate::state::RelaunchState; + #[derive(AnchorSerialize, AnchorDeserialize)] pub struct CommonFields { pub slot: u64, @@ -60,3 +62,10 @@ pub struct TokensDepositedEvent { pub total_deposited_by_depositor: u64, pub deposit_record_seq_num: u64, } + +#[event] +pub struct DepositsClosedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub new_state: RelaunchState, +} diff --git a/programs/relaunch/src/instructions/close_deposits.rs b/programs/relaunch/src/instructions/close_deposits.rs new file mode 100644 index 000000000..6a440a73d --- /dev/null +++ b/programs/relaunch/src/instructions/close_deposits.rs @@ -0,0 +1,58 @@ +use anchor_lang::prelude::*; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, DepositsClosedEvent}; +use crate::state::{Relaunch, RelaunchState}; + +#[event_cpi] +#[derive(Accounts)] +pub struct CloseDeposits<'info> { + #[account(mut)] + pub relaunch: Account<'info, Relaunch>, +} + +impl CloseDeposits<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Live, + RelaunchError::RelaunchNotLive + ); + + let clock = Clock::get()?; + require_gte!( + clock.unix_timestamp, + self.relaunch.unix_timestamp_started.unwrap() + + self.relaunch.seconds_for_deposits as i64, + RelaunchError::DepositWindowStillOpen + ); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch = &mut ctx.accounts.relaunch; + let clock = Clock::get()?; + + // threshold_bps × old_supply_snapshot overflows u64 for supplies + // above ~1.8e15 raw units, so the threshold math runs in u128. + let threshold = + relaunch.threshold_bps as u128 * relaunch.old_supply_snapshot as u128 / 10_000; + + relaunch.state = if relaunch.total_deposited as u128 >= threshold { + RelaunchState::SellPending + } else { + RelaunchState::Failed + }; + relaunch.unix_timestamp_closed = Some(clock.unix_timestamp); + + relaunch.seq_num += 1; + + emit_cpi!(DepositsClosedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: ctx.accounts.relaunch.key(), + new_state: ctx.accounts.relaunch.state, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 761163b51..c8d3f2e5f 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,7 +1,9 @@ +pub mod close_deposits; pub mod deposit; pub mod initialize_relaunch; pub mod start_deposits; +pub use close_deposits::*; pub use deposit::*; pub use initialize_relaunch::*; pub use start_deposits::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 0fbd83e8a..da1221edb 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -50,4 +50,9 @@ pub mod relaunch { pub fn deposit(ctx: Context, args: DepositArgs) -> Result<()> { Deposit::handle(ctx, args) } + + #[access_control(ctx.accounts.validate())] + pub fn close_deposits(ctx: Context) -> Result<()> { + CloseDeposits::handle(ctx) + } } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 141ce4a32..8e4b00587 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -227,6 +227,12 @@ export class RelaunchClient { }); } + closeDepositsIx({ relaunch }: { relaunch: PublicKey }) { + return this.relaunchProgram.methods.closeDeposits().accounts({ + relaunch, + }); + } + // Deposits from the provider wallet, reading the old mint and its owner // program from the stored relaunch. async deposit({ diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index ff6d2c618..0d0a74f9f 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -235,6 +235,27 @@ export type Relaunch = { }, ]; }, + { + name: "closeDeposits"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, ]; accounts: [ { @@ -772,6 +793,30 @@ export type Relaunch = { }, ]; }, + { + name: "DepositsClosedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "newState"; + type: { + defined: "RelaunchState"; + }; + index: false; + }, + ]; + }, ]; errors: [ { @@ -849,6 +894,11 @@ export type Relaunch = { name: "InsufficientFunds"; msg: "Insufficient balance"; }, + { + code: 6015; + name: "DepositWindowStillOpen"; + msg: "Deposit window is still open"; + }, ]; }; @@ -1089,6 +1139,27 @@ export const IDL: Relaunch = { }, ], }, + { + name: "closeDeposits", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, ], accounts: [ { @@ -1626,6 +1697,30 @@ export const IDL: Relaunch = { }, ], }, + { + name: "DepositsClosedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "newState", + type: { + defined: "RelaunchState", + }, + index: false, + }, + ], + }, ], errors: [ { @@ -1703,5 +1798,10 @@ export const IDL: Relaunch = { name: "InsufficientFunds", msg: "Insufficient balance", }, + { + code: 6015, + name: "DepositWindowStillOpen", + msg: "Deposit window is still open", + }, ], }; diff --git a/tasks.md b/tasks.md index d150993e5..36af87da2 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" -- [NEXT] 2.3 `close_deposits` (instruction + SDK + tests) - - Window elapsed; u128 threshold math → `SellPending` | `Failed`; stamp `unix_timestamp_closed`, event - - Tests per plan §5: exact-threshold boundary, one-short → Failed, early close fails, 10^15-supply overflow case, permissionless crank - -- [ ] 2.4 `mark_failed` (instruction + SDK + tests) +- [NEXT] 2.4 `mark_failed` (instruction + SDK + tests) - `SellPending → Failed` once `now > closed + grace_period_seconds`, event - Tests per plan §11: before/after grace, wrong states, permissionless diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index e562d3394..33c136b8a 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -3,6 +3,7 @@ import venues from "./unit/venues.test.js"; import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; +import closeDeposits from "./unit/closeDeposits.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -19,4 +20,5 @@ export default function suite() { describe("#initialize_relaunch", initializeRelaunch); describe("#start_deposits", startDeposits); describe("#deposit", deposit); + describe("#close_deposits", closeDeposits); } diff --git a/tests/relaunch/unit/closeDeposits.test.ts b/tests/relaunch/unit/closeDeposits.test.ts new file mode 100644 index 000000000..f28e6e748 --- /dev/null +++ b/tests/relaunch/unit/closeDeposits.test.ts @@ -0,0 +1,235 @@ +import { + ComputeBudgetProgram, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { RelaunchClient } from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +// 10% of the 1B-token default supply = 100M tokens. +const DEFAULT_THRESHOLD_BPS = 1000; +const DEFAULT_THRESHOLD_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let relaunch: PublicKey; + + before(function () { + client = this.relaunch; + }); + + const setupLiveRelaunch = async function ( + this: Mocha.Context, + { + thresholdBps = DEFAULT_THRESHOLD_BPS, + oldSupply, + start = true, + }: { thresholdBps?: number; oldSupply?: bigint; start?: boolean } = {}, + ) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldSupply, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps, + teamAddress: this.payer.publicKey, + }); + + if (start) { + await client.startDepositsIx({ relaunch }).rpc(); + } + + return { ...setup, relaunch }; + }; + + const deposit = async function (this: Mocha.Context, amount: bigint) { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(amount.toString()), + }) + .rpc(); + }; + + beforeEach(async function () { + ({ oldMint, oldTokenProgram, relaunch } = + await setupLiveRelaunch.call(this)); + }); + + it("closes into SellPending when deposits exactly meet the threshold", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await this.advanceBySeconds(ONE_WEEK); + + const clock = await this.banksClient.getClock(); + await client.closeDepositsIx({ relaunch }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + assert.equal( + storedRelaunch.unixTimestampClosed.toString(), + clock.unixTimestamp.toString(), + ); + assert.equal(storedRelaunch.seqNum.toString(), "3"); + }); + + it("closes into Failed when deposits are one base unit short", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT - 1n); + await this.advanceBySeconds(ONE_WEEK); + + await client.closeDepositsIx({ relaunch }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + assert.isNotNull(storedRelaunch.unixTimestampClosed); + }); + + it("fails before the deposit window elapses", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await this.advanceBySeconds(ONE_WEEK - 10); + + try { + // The compute-unit-limit instruction makes the transaction hash unique + // so the later successful close isn't rejected as a duplicate. + await client + .closeDepositsIx({ relaunch }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "DepositWindowStillOpen"); + } + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.live); + assert.isNull(storedRelaunch.unixTimestampClosed); + + // The window closes exactly at started + seconds_for_deposits. + await this.advanceBySeconds(10); + await client.closeDepositsIx({ relaunch }).rpc(); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails when deposits are already closed", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await this.advanceBySeconds(ONE_WEEK); + + await client.closeDepositsIx({ relaunch }).rpc(); + + try { + // The compute-unit-limit instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first call. + await client + .closeDepositsIx({ relaunch }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotLive"); + } + }); + + it("fails before deposits start", async function () { + const setup = await setupLiveRelaunch.call(this, { start: false }); + await this.advanceBySeconds(ONE_WEEK); + + try { + await client.closeDepositsIx({ relaunch: setup.relaunch }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotLive"); + } + }); + + it("computes the threshold in u128 when the multiplication overflows u64", async function () { + // 10_000 bps × a 10^16 raw supply is 10^20, above u64::MAX (~1.8 × 10^19). + const hugeSupply = 10n ** 16n; + const setup = await setupLiveRelaunch.call(this, { + thresholdBps: 10_000, + oldSupply: hugeSupply, + }); + + await client + .depositIx({ + relaunch: setup.relaunch, + oldMint: setup.oldMint, + oldTokenProgram: setup.oldTokenProgram, + amount: new BN(hugeSupply.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + + await client.closeDepositsIx({ relaunch: setup.relaunch }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(setup.relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("lets any keypair crank the close", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await this.advanceBySeconds(ONE_WEEK); + + const cranker = Keypair.generate(); + const fund = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: this.payer.publicKey, + toPubkey: cranker.publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ); + fund.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + fund.feePayer = this.payer.publicKey; + fund.sign(this.payer); + await this.banksClient.processTransaction(fund); + + const tx = new Transaction().add( + await client.closeDepositsIx({ relaunch }).instruction(), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = cranker.publicKey; + tx.sign(cranker); + await this.banksClient.processTransaction(tx); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); +} From c8a387ed9601534ea4d74bad90f3e09294370495 Mon Sep 17 00:00:00 2001 From: Pileks Date: Thu, 6 Aug 2026 01:05:28 +0200 Subject: [PATCH 12/31] mark_failed ix, sdk, tests --- CLAUDE.md | 8 +- programs/relaunch/src/error.rs | 4 + programs/relaunch/src/events.rs | 6 + .../relaunch/src/instructions/mark_failed.rs | 47 +++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 6 + sdk/src/relaunch/v0.1/types/relaunch.ts | 96 +++++++++ tasks.md | 6 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/closeDeposits.test.ts | 8 +- tests/relaunch/unit/markFailed.test.ts | 185 ++++++++++++++++++ tests/relaunch/unit/startDeposits.test.ts | 4 +- 13 files changed, 364 insertions(+), 15 deletions(-) create mode 100644 programs/relaunch/src/instructions/mark_failed.rs create mode 100644 tests/relaunch/unit/markFailed.test.ts diff --git a/CLAUDE.md b/CLAUDE.md index 13a1b7caf..89b460498 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -191,20 +191,20 @@ Tests use `solana-bankrun` for deterministic testing without external RPC: - `advanceBySlots()` - Simulate time progression - Time constants: `TEN_SECONDS_IN_SLOTS`, `ONE_MINUTE_IN_SLOTS`, `HOUR_IN_SLOTS`, `DAY_IN_SLOTS` -**Getting unique transaction signatures:** When testing error cases that call the same instruction multiple times (e.g., verifying an action fails after state changes), add a `ComputeBudgetProgram.setComputeUnitLimit()` instruction with incrementing values to produce different transaction signatures: +**Getting unique transaction signatures:** When testing error cases that call the same instruction multiple times (e.g., verifying an action fails after state changes), add a `ComputeBudgetProgram.setComputeUnitPrice()` instruction to make the transaction hash unique, so the retry isn't rejected as a duplicate of the earlier byte-identical transaction: ```typescript -// First call (200_000), second call (200_001), etc. +// If the same call site needs several unique retries, increment microLamports (1, 2, ...). await client .someIx({ ... }) .postInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), ]) .signers([signer]) .rpc(); ``` -Do NOT use `advanceBySlots()` for this purpose - it changes the clock which may affect time-dependent tests. +Do NOT use `setComputeUnitLimit()` for this — reserve it for genuinely raising a transaction's compute budget. Do NOT use `advanceBySlots()` either - it changes the clock which may affect time-dependent tests. **Isolating tests during development:** When writing or editing tests, ALWAYS add `.only` to the `describe`/`it` block you're working on before running. This keeps feedback fast and output clean. Once your changes pass, remove `.only` and run the full suite (`anchor test --skip-build`) to confirm nothing else broke. diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index bd5c2422d..4cfdcba22 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -34,4 +34,8 @@ pub enum RelaunchError { InsufficientFunds, #[msg("Deposit window is still open")] DepositWindowStillOpen, + #[msg("Relaunch must be in the SellPending state")] + RelaunchNotSellPending, + #[msg("Grace period has not elapsed")] + GracePeriodStillActive, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 2a09e6a35..e3d2cefd1 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -69,3 +69,9 @@ pub struct DepositsClosedEvent { pub relaunch: Pubkey, pub new_state: RelaunchState, } + +#[event] +pub struct RelaunchMarkedFailedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, +} diff --git a/programs/relaunch/src/instructions/mark_failed.rs b/programs/relaunch/src/instructions/mark_failed.rs new file mode 100644 index 000000000..a3274f4e2 --- /dev/null +++ b/programs/relaunch/src/instructions/mark_failed.rs @@ -0,0 +1,47 @@ +use anchor_lang::prelude::*; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, RelaunchMarkedFailedEvent}; +use crate::state::{Relaunch, RelaunchState}; + +#[event_cpi] +#[derive(Accounts)] +pub struct MarkFailed<'info> { + #[account(mut)] + pub relaunch: Account<'info, Relaunch>, +} + +impl MarkFailed<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::SellPending, + RelaunchError::RelaunchNotSellPending + ); + + let clock = Clock::get()?; + require_gt!( + clock.unix_timestamp, + self.relaunch.unix_timestamp_closed.unwrap() + + self.relaunch.grace_period_seconds as i64, + RelaunchError::GracePeriodStillActive + ); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch = &mut ctx.accounts.relaunch; + let clock = Clock::get()?; + + relaunch.state = RelaunchState::Failed; + + relaunch.seq_num += 1; + + emit_cpi!(RelaunchMarkedFailedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: ctx.accounts.relaunch.key(), + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index c8d3f2e5f..24ad11930 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,9 +1,11 @@ pub mod close_deposits; pub mod deposit; pub mod initialize_relaunch; +pub mod mark_failed; pub mod start_deposits; pub use close_deposits::*; pub use deposit::*; pub use initialize_relaunch::*; +pub use mark_failed::*; pub use start_deposits::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index da1221edb..e585ebfee 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -55,4 +55,9 @@ pub mod relaunch { pub fn close_deposits(ctx: Context) -> Result<()> { CloseDeposits::handle(ctx) } + + #[access_control(ctx.accounts.validate())] + pub fn mark_failed(ctx: Context) -> Result<()> { + MarkFailed::handle(ctx) + } } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 8e4b00587..1b0224147 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -233,6 +233,12 @@ export class RelaunchClient { }); } + markFailedIx({ relaunch }: { relaunch: PublicKey }) { + return this.relaunchProgram.methods.markFailed().accounts({ + relaunch, + }); + } + // Deposits from the provider wallet, reading the old mint and its owner // program from the stored relaunch. async deposit({ diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 0d0a74f9f..43ce2fa05 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -256,6 +256,27 @@ export type Relaunch = { ]; args: []; }, + { + name: "markFailed"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, ]; accounts: [ { @@ -817,6 +838,23 @@ export type Relaunch = { }, ]; }, + { + name: "RelaunchMarkedFailedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + ]; + }, ]; errors: [ { @@ -899,6 +937,16 @@ export type Relaunch = { name: "DepositWindowStillOpen"; msg: "Deposit window is still open"; }, + { + code: 6016; + name: "RelaunchNotSellPending"; + msg: "Relaunch must be in the SellPending state"; + }, + { + code: 6017; + name: "GracePeriodStillActive"; + msg: "Grace period has not elapsed"; + }, ]; }; @@ -1160,6 +1208,27 @@ export const IDL: Relaunch = { ], args: [], }, + { + name: "markFailed", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, ], accounts: [ { @@ -1721,6 +1790,23 @@ export const IDL: Relaunch = { }, ], }, + { + name: "RelaunchMarkedFailedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + ], + }, ], errors: [ { @@ -1803,5 +1889,15 @@ export const IDL: Relaunch = { name: "DepositWindowStillOpen", msg: "Deposit window is still open", }, + { + code: 6016, + name: "RelaunchNotSellPending", + msg: "Relaunch must be in the SellPending state", + }, + { + code: 6017, + name: "GracePeriodStillActive", + msg: "Grace period has not elapsed", + }, ], }; diff --git a/tasks.md b/tasks.md index 36af87da2..83161f4d2 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" -- [NEXT] 2.4 `mark_failed` (instruction + SDK + tests) - - `SellPending → Failed` once `now > closed + grace_period_seconds`, event - - Tests per plan §11: before/after grace, wrong states, permissionless - -- [ ] 2.5 `claim_refund` (instruction + SDK + tests) +- [NEXT] 2.5 `claim_refund` (instruction + SDK + tests) - `Failed`, returns exact `amount_deposited`, sets `claimed`, event - Tests per plan §10: both token programs, double-refund, wrong state, vault-empty conservation, both end-to-end failure paths diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 33c136b8a..d5ea3b760 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -4,6 +4,7 @@ import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; +import markFailed from "./unit/markFailed.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -21,4 +22,5 @@ export default function suite() { describe("#start_deposits", startDeposits); describe("#deposit", deposit); describe("#close_deposits", closeDeposits); + describe("#mark_failed", markFailed); } diff --git a/tests/relaunch/unit/closeDeposits.test.ts b/tests/relaunch/unit/closeDeposits.test.ts index f28e6e748..1347a0809 100644 --- a/tests/relaunch/unit/closeDeposits.test.ts +++ b/tests/relaunch/unit/closeDeposits.test.ts @@ -122,12 +122,12 @@ export default function suite() { await this.advanceBySeconds(ONE_WEEK - 10); try { - // The compute-unit-limit instruction makes the transaction hash unique + // The compute-unit-price instruction makes the transaction hash unique // so the later successful close isn't rejected as a duplicate. await client .closeDepositsIx({ relaunch }) .postInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), ]) .rpc(); assert.fail("Should have thrown error"); @@ -154,12 +154,12 @@ export default function suite() { await client.closeDepositsIx({ relaunch }).rpc(); try { - // The compute-unit-limit instruction makes the transaction hash unique + // The compute-unit-price instruction makes the transaction hash unique // so the retry isn't rejected as a duplicate of the first call. await client .closeDepositsIx({ relaunch }) .postInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), ]) .rpc(); assert.fail("Should have thrown error"); diff --git a/tests/relaunch/unit/markFailed.test.ts b/tests/relaunch/unit/markFailed.test.ts new file mode 100644 index 000000000..1940a488a --- /dev/null +++ b/tests/relaunch/unit/markFailed.test.ts @@ -0,0 +1,185 @@ +import { + ComputeBudgetProgram, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { RelaunchClient } from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +// 10% of the 1B-token default supply = 100M tokens. +const DEFAULT_THRESHOLD_BPS = 1000; +const DEFAULT_THRESHOLD_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let relaunch: PublicKey; + + before(function () { + client = this.relaunch; + }); + + beforeEach(async function () { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + oldMint = setup.oldMint; + oldTokenProgram = setup.oldTokenProgram; + + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + ({ relaunch } = await client.initializeRelaunch({ + oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: DEFAULT_THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + })); + + await client.startDepositsIx({ relaunch }).rpc(); + }); + + const closeIntoSellPending = async function (this: Mocha.Context) { + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(DEFAULT_THRESHOLD_AMOUNT.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + }; + + it("marks the relaunch failed once the grace period has elapsed", async function () { + await closeIntoSellPending.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + + await client.markFailedIx({ relaunch }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + assert.equal(storedRelaunch.seqNum.toString(), "4"); + }); + + it("fails until the grace period fully elapses", async function () { + await closeIntoSellPending.call(this); + + // The last second of the grace period still belongs to the admin's sell + // window, so exactly closed + grace must fail. + await this.advanceBySeconds(ONE_DAY); + + try { + // The compute-unit-price instruction makes the transaction hash unique + // so the later successful call isn't rejected as a duplicate. + await client + .markFailedIx({ relaunch }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "GracePeriodStillActive"); + } + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + + await this.advanceBySeconds(1); + await client.markFailedIx({ relaunch }).rpc(); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + }); + + it("fails when the relaunch is Live", async function () { + await this.advanceBySeconds(ONE_WEEK + ONE_DAY + 1); + + try { + await client.markFailedIx({ relaunch }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.live); + }); + + it("fails when the relaunch is already Failed", async function () { + await closeIntoSellPending.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + await client.markFailedIx({ relaunch }).rpc(); + + try { + // The compute-unit-price instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first call. + await client + .markFailedIx({ relaunch }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + }); + + it("lets any keypair crank mark_failed", async function () { + await closeIntoSellPending.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + + const cranker = Keypair.generate(); + const fund = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: this.payer.publicKey, + toPubkey: cranker.publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ); + fund.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + fund.feePayer = this.payer.publicKey; + fund.sign(this.payer); + await this.banksClient.processTransaction(fund); + + const tx = new Transaction().add( + await client.markFailedIx({ relaunch }).instruction(), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = cranker.publicKey; + tx.sign(cranker); + await this.banksClient.processTransaction(tx); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + }); +} diff --git a/tests/relaunch/unit/startDeposits.test.ts b/tests/relaunch/unit/startDeposits.test.ts index 36caff3db..0efafddb9 100644 --- a/tests/relaunch/unit/startDeposits.test.ts +++ b/tests/relaunch/unit/startDeposits.test.ts @@ -85,12 +85,12 @@ export default function suite() { await client.startDepositsIx({ relaunch }).rpc(); try { - // The compute-unit-limit instruction makes the transaction hash unique + // The compute-unit-price instruction makes the transaction hash unique // so the retry isn't rejected as a duplicate of the first call. await client .startDepositsIx({ relaunch }) .postInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), ]) .rpc(); assert.fail("Should have thrown error"); From 4449ee2a8406e9ee49b76a3bc576d61fc8f040ef Mon Sep 17 00:00:00 2001 From: Pileks Date: Thu, 6 Aug 2026 01:17:39 +0200 Subject: [PATCH 13/31] claim_refund ix, sdk, tests --- programs/relaunch/src/error.rs | 4 + programs/relaunch/src/events.rs | 10 + .../relaunch/src/instructions/claim_refund.rs | 108 +++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 73 +++ sdk/src/relaunch/v0.1/types/relaunch.ts | 208 ++++++++ tasks.md | 10 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/claimRefund.test.ts | 447 ++++++++++++++++++ 10 files changed, 860 insertions(+), 9 deletions(-) create mode 100644 programs/relaunch/src/instructions/claim_refund.rs create mode 100644 tests/relaunch/unit/claimRefund.test.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 4cfdcba22..68cb04ba3 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -38,4 +38,8 @@ pub enum RelaunchError { RelaunchNotSellPending, #[msg("Grace period has not elapsed")] GracePeriodStillActive, + #[msg("Relaunch must be in the Failed state")] + RelaunchNotFailed, + #[msg("Deposit record has already been claimed")] + AlreadyClaimed, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index e3d2cefd1..f08d87935 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -75,3 +75,13 @@ pub struct RelaunchMarkedFailedEvent { pub common: CommonFields, pub relaunch: Pubkey, } + +#[event] +pub struct RefundClaimedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub depositor: Pubkey, + pub deposit_record: Pubkey, + pub amount_refunded: u64, + pub deposit_record_seq_num: u64, +} diff --git a/programs/relaunch/src/instructions/claim_refund.rs b/programs/relaunch/src/instructions/claim_refund.rs new file mode 100644 index 000000000..c683583df --- /dev/null +++ b/programs/relaunch/src/instructions/claim_refund.rs @@ -0,0 +1,108 @@ +use anchor_lang::prelude::*; +use anchor_spl::token_interface; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, RefundClaimedEvent}; +use crate::state::{DepositRecord, Relaunch, RelaunchState}; + +#[event_cpi] +#[derive(Accounts)] +pub struct ClaimRefund<'info> { + #[account( + mut, + has_one = old_mint, + has_one = old_token_vault, + has_one = relaunch_signer, + )] + pub relaunch: Box>, + + #[account( + mut, + has_one = relaunch, + has_one = depositor, + seeds = [b"deposit_record", relaunch.key().as_ref(), depositor.key().as_ref()], + bump = deposit_record.pda_bump + )] + pub deposit_record: Box>, + + #[account(mint::token_program = old_token_program)] + pub old_mint: Box>, + + #[account(mut)] + pub old_token_vault: Box>, + + /// CHECK: just a signer + pub relaunch_signer: UncheckedAccount<'info>, + + /// CHECK: the refund recipient; not required to sign, so anyone can crank + /// refunds for any depositor. + pub depositor: UncheckedAccount<'info>, + + #[account( + mut, + associated_token::mint = old_mint, + associated_token::authority = depositor, + associated_token::token_program = old_token_program, + )] + pub depositor_token_account: Box>, + + pub old_token_program: Interface<'info, token_interface::TokenInterface>, +} + +impl ClaimRefund<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Failed, + RelaunchError::RelaunchNotFailed + ); + + require!(!self.deposit_record.claimed, RelaunchError::AlreadyClaimed); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let deposit_record = &mut ctx.accounts.deposit_record; + deposit_record.claimed = true; + deposit_record.seq_num += 1; + + token_interface::transfer_checked( + CpiContext::new_with_signer( + ctx.accounts.old_token_program.to_account_info(), + token_interface::TransferChecked { + from: ctx.accounts.old_token_vault.to_account_info(), + mint: ctx.accounts.old_mint.to_account_info(), + to: ctx.accounts.depositor_token_account.to_account_info(), + authority: ctx.accounts.relaunch_signer.to_account_info(), + }, + signer, + ), + ctx.accounts.deposit_record.amount_deposited, + ctx.accounts.old_mint.decimals, + )?; + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(RefundClaimedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + depositor: ctx.accounts.depositor.key(), + deposit_record: ctx.accounts.deposit_record.key(), + amount_refunded: ctx.accounts.deposit_record.amount_deposited, + deposit_record_seq_num: ctx.accounts.deposit_record.seq_num, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 24ad11930..bc2236395 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,9 +1,11 @@ +pub mod claim_refund; pub mod close_deposits; pub mod deposit; pub mod initialize_relaunch; pub mod mark_failed; pub mod start_deposits; +pub use claim_refund::*; pub use close_deposits::*; pub use deposit::*; pub use initialize_relaunch::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index e585ebfee..98e7ccdcb 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -60,4 +60,9 @@ pub mod relaunch { pub fn mark_failed(ctx: Context) -> Result<()> { MarkFailed::handle(ctx) } + + #[access_control(ctx.accounts.validate())] + pub fn claim_refund(ctx: Context) -> Result<()> { + ClaimRefund::handle(ctx) + } } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 1b0224147..fa28692f6 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -239,6 +239,48 @@ export class RelaunchClient { }); } + claimRefundIx({ + relaunch, + oldMint, + oldTokenProgram, + depositor = this.provider.publicKey, + }: { + relaunch: PublicKey; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + depositor?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + oldTokenProgram, + ); + const depositorTokenAccount = getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + + return this.relaunchProgram.methods.claimRefund().accounts({ + relaunch, + depositRecord, + oldMint, + oldTokenVault, + relaunchSigner, + depositor, + depositorTokenAccount, + oldTokenProgram, + }); + } + // Deposits from the provider wallet, reading the old mint and its owner // program from the stored relaunch. async deposit({ @@ -270,6 +312,37 @@ export class RelaunchClient { }).rpc(); } + // Claims a refund for the given depositor (the provider wallet by default), + // reading the old mint and its owner program from the stored relaunch. + async claimRefund({ + relaunch, + depositor = this.provider.publicKey, + }: { + relaunch: PublicKey; + depositor?: PublicKey; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + const oldMintAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.oldMint, + ); + if (oldMintAccount === null) { + throw new Error( + `old mint ${storedRelaunch.oldMint.toBase58()} does not exist`, + ); + } + + return this.claimRefundIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + oldTokenProgram: oldMintAccount.owner, + depositor, + }).rpc(); + } + // Builds the create-mint-to-self pre-instructions: a `createAccountWithSeed` // + `initializeMint2` pair with the payer as mint authority, so // `initialize_relaunch` can take the authority from a mint the payer diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 43ce2fa05..e6dc5b2a6 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -277,6 +277,63 @@ export type Relaunch = { ]; args: []; }, + { + name: "claimRefund"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "depositRecord"; + isMut: true; + isSigner: false; + }, + { + name: "oldMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "depositor"; + isMut: false; + isSigner: false; + docs: ["refunds for any depositor."]; + }, + { + name: "depositorTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "oldTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, ]; accounts: [ { @@ -855,6 +912,43 @@ export type Relaunch = { }, ]; }, + { + name: "RefundClaimedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "depositor"; + type: "publicKey"; + index: false; + }, + { + name: "depositRecord"; + type: "publicKey"; + index: false; + }, + { + name: "amountRefunded"; + type: "u64"; + index: false; + }, + { + name: "depositRecordSeqNum"; + type: "u64"; + index: false; + }, + ]; + }, ]; errors: [ { @@ -947,6 +1041,16 @@ export type Relaunch = { name: "GracePeriodStillActive"; msg: "Grace period has not elapsed"; }, + { + code: 6018; + name: "RelaunchNotFailed"; + msg: "Relaunch must be in the Failed state"; + }, + { + code: 6019; + name: "AlreadyClaimed"; + msg: "Deposit record has already been claimed"; + }, ]; }; @@ -1229,6 +1333,63 @@ export const IDL: Relaunch = { ], args: [], }, + { + name: "claimRefund", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "depositRecord", + isMut: true, + isSigner: false, + }, + { + name: "oldMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "depositor", + isMut: false, + isSigner: false, + docs: ["refunds for any depositor."], + }, + { + name: "depositorTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "oldTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, ], accounts: [ { @@ -1807,6 +1968,43 @@ export const IDL: Relaunch = { }, ], }, + { + name: "RefundClaimedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "depositor", + type: "publicKey", + index: false, + }, + { + name: "depositRecord", + type: "publicKey", + index: false, + }, + { + name: "amountRefunded", + type: "u64", + index: false, + }, + { + name: "depositRecordSeqNum", + type: "u64", + index: false, + }, + ], + }, ], errors: [ { @@ -1899,5 +2097,15 @@ export const IDL: Relaunch = { name: "GracePeriodStillActive", msg: "Grace period has not elapsed", }, + { + code: 6018, + name: "RelaunchNotFailed", + msg: "Relaunch must be in the Failed state", + }, + { + code: 6019, + name: "AlreadyClaimed", + msg: "Deposit record has already been claimed", + }, ], }; diff --git a/tasks.md b/tasks.md index 83161f4d2..670d11c2f 100644 --- a/tasks.md +++ b/tasks.md @@ -23,19 +23,11 @@ ## Tasks -### Phase 2: deposit + failure loop - -> Reference: `relaunch-implementation-plan.md` → "Stage 2 — deposit, close_deposits, mark_failed, claim_refund" - -- [NEXT] 2.5 `claim_refund` (instruction + SDK + tests) - - `Failed`, returns exact `amount_deposited`, sets `claimed`, event - - Tests per plan §10: both token programs, double-refund, wrong state, vault-empty conservation, both end-to-end failure paths - ### Phase 3: PumpSwap + Whirlpool legs > Reference: `relaunch-implementation-plan.md` → "Stage 3 — execute_sell, execute_usdc_swap, deposit_via_buy" -- [ ] 3.1 `execute_sell` (instruction + SDK + tests) +- [NEXT] 3.1 `execute_sell` (instruction + SDK + tests) - Hand-built `cpi/pump_amm.rs` sell builder (discriminator + borsh args + verified 21-account list from design §06) - Admin + `SellPending` + grace gate; sell full vault; `quote_recovered` from measured delta; WSOL → `Sold`, USDC → `Swapped` - Tests per plan §6: both quote variants, slippage failure leaves state unchanged, non-admin/wrong-state/after-grace, Token-2022 base pool diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index d5ea3b760..7c01402d2 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -5,6 +5,7 @@ import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import markFailed from "./unit/markFailed.test.js"; +import claimRefund from "./unit/claimRefund.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; import { BankrunProvider } from "anchor-bankrun"; @@ -23,4 +24,5 @@ export default function suite() { describe("#deposit", deposit); describe("#close_deposits", closeDeposits); describe("#mark_failed", markFailed); + describe("#claim_refund", claimRefund); } diff --git a/tests/relaunch/unit/claimRefund.test.ts b/tests/relaunch/unit/claimRefund.test.ts new file mode 100644 index 000000000..5ba0ea538 --- /dev/null +++ b/tests/relaunch/unit/claimRefund.test.ts @@ -0,0 +1,447 @@ +import { + ComputeBudgetProgram, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { RelaunchClient } from "@metadaoproject/programs"; +import { getAccount } from "spl-token-bankrun"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +// 10% of the 1B-token default supply = 100M tokens. +const DEFAULT_THRESHOLD_BPS = 1000; +const DEFAULT_THRESHOLD_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let payerOldTokenAccount: PublicKey; + let relaunch: PublicKey; + let oldTokenVault: PublicKey; + + before(function () { + client = this.relaunch; + }); + + const setupLiveRelaunch = async function ( + this: Mocha.Context, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, + ) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram: tokenProgram, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: tokenProgram, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: DEFAULT_THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const oldTokenVault = token.getAssociatedTokenAddressSync( + setup.oldMint, + relaunchSigner, + true, + tokenProgram, + ); + + return { ...setup, relaunch, oldTokenVault }; + }; + + // Creates the depositor's ATA and funds it with old tokens from the payer. + const fundDepositor = async function ( + this: Mocha.Context, + depositor: PublicKey, + amount: bigint, + ): Promise { + const ata = token.getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + this.payer.publicKey, + ata, + depositor, + oldMint, + oldTokenProgram, + ), + token.createTransferCheckedInstruction( + payerOldTokenAccount, + oldMint, + ata, + this.payer.publicKey, + amount, + 6, + [], + oldTokenProgram, + ), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + return ata; + }; + + const deposit = async function ( + this: Mocha.Context, + amount: bigint, + depositor?: Keypair, + ) { + const builder = client.depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(amount.toString()), + depositor: depositor?.publicKey, + }); + if (depositor !== undefined) { + builder.signers([depositor]); + } + await builder.rpc(); + }; + + const closeDeposits = async function (this: Mocha.Context) { + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + }; + + beforeEach(async function () { + ({ + oldMint, + oldTokenProgram, + payerOldTokenAccount, + relaunch, + oldTokenVault, + } = await setupLiveRelaunch.call(this)); + }); + + it("refunds the exact accumulated deposit after a threshold miss", async function () { + await deposit.call(this, 100_000_000n); // 100 tokens + await deposit.call(this, 200_000_000n); // 200 tokens + await closeDeposits.call(this); + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + assert.equal(record.amountDeposited.toString(), "300000000"); + assert.equal(record.seqNum.toString(), "2"); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.seqNum.toString(), "5"); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "0"); + + const depositorAccount = await getAccount( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + depositorAccount.amount.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + }); + + it("refunds under Token-2022", async function () { + const setup = await setupLiveRelaunch.call( + this, + token.TOKEN_2022_PROGRAM_ID, + ); + + await client + .depositIx({ + relaunch: setup.relaunch, + oldMint: setup.oldMint, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + amount: new BN(100_000_000), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch: setup.relaunch }).rpc(); + + await client + .claimRefundIx({ + relaunch: setup.relaunch, + oldMint: setup.oldMint, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }) + .rpc(); + + const record = await client.getDepositRecord({ + relaunch: setup.relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + + const vault = await getAccount( + this.banksClient, + setup.oldTokenVault, + undefined, + token.TOKEN_2022_PROGRAM_ID, + ); + assert.equal(vault.amount.toString(), "0"); + + const depositorAccount = await getAccount( + this.banksClient, + setup.payerOldTokenAccount, + undefined, + token.TOKEN_2022_PROGRAM_ID, + ); + assert.equal( + depositorAccount.amount.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + }); + + it("refunds every depositor exactly and empties the vault", async function () { + const alice = Keypair.generate(); + const bob = Keypair.generate(); + const aliceAta = await fundDepositor.call( + this, + alice.publicKey, + 1_000_000_000n, // 1,000 tokens + ); + const bobAta = await fundDepositor.call( + this, + bob.publicKey, + 1_000_000_000n, + ); + + await deposit.call(this, 100_000_000n, alice); + await deposit.call(this, 200_000_000n, bob); + await deposit.call(this, 300_000_000n); + await closeDeposits.call(this); + + await client + .claimRefundIx({ + relaunch, + oldMint, + oldTokenProgram, + depositor: alice.publicKey, + }) + .rpc(); + await client + .claimRefundIx({ + relaunch, + oldMint, + oldTokenProgram, + depositor: bob.publicKey, + }) + .rpc(); + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + + const aliceAccount = await getAccount(this.banksClient, aliceAta); + assert.equal(aliceAccount.amount.toString(), "1000000000"); + + const bobAccount = await getAccount(this.banksClient, bobAta); + assert.equal(bobAccount.amount.toString(), "1000000000"); + + const payerAccount = await getAccount( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + payerAccount.amount.toString(), + (DEFAULT_OLD_SUPPLY - 2_000_000_000n).toString(), + ); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "0"); + }); + + it("fails to refund the same record twice", async function () { + await deposit.call(this, 100_000_000n); + await closeDeposits.call(this); + + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + + try { + // The compute-unit-price instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first claim. + await client + .claimRefundIx({ relaunch, oldMint, oldTokenProgram }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "AlreadyClaimed"); + } + + const depositorAccount = await getAccount( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + depositorAccount.amount.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + }); + + it("fails without a deposit record", async function () { + await deposit.call(this, 100_000_000n); + await closeDeposits.call(this); + + const rando = Keypair.generate(); + await fundDepositor.call(this, rando.publicKey, 0n); + + try { + await client + .claimRefundIx({ + relaunch, + oldMint, + oldTokenProgram, + depositor: rando.publicKey, + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "AccountNotInitialized"); + } + }); + + it("fails while the relaunch is Live", async function () { + await deposit.call(this, 100_000_000n); + + try { + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotFailed"); + } + }); + + it("fails while the relaunch is SellPending", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await closeDeposits.call(this); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + + try { + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotFailed"); + } + }); + + it("refunds after the grace period lapses and mark_failed cranks", async function () { + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await closeDeposits.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + await client.markFailedIx({ relaunch }).rpc(); + + await client.claimRefundIx({ relaunch, oldMint, oldTokenProgram }).rpc(); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + + const vault = await getAccount(this.banksClient, oldTokenVault); + assert.equal(vault.amount.toString(), "0"); + + const depositorAccount = await getAccount( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + depositorAccount.amount.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + }); + + it("lets any keypair crank a refund for a depositor", async function () { + const alice = Keypair.generate(); + const aliceAta = await fundDepositor.call( + this, + alice.publicKey, + 1_000_000_000n, + ); + await deposit.call(this, 100_000_000n, alice); + await closeDeposits.call(this); + + const cranker = Keypair.generate(); + const fund = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: this.payer.publicKey, + toPubkey: cranker.publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ); + fund.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + fund.feePayer = this.payer.publicKey; + fund.sign(this.payer); + await this.banksClient.processTransaction(fund); + + const tx = new Transaction().add( + await client + .claimRefundIx({ + relaunch, + oldMint, + oldTokenProgram, + depositor: alice.publicKey, + }) + .instruction(), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = cranker.publicKey; + tx.sign(cranker); + await this.banksClient.processTransaction(tx); + + const aliceAccount = await getAccount(this.banksClient, aliceAta); + assert.equal(aliceAccount.amount.toString(), "1000000000"); + + const record = await client.getDepositRecord({ + relaunch, + depositor: alice.publicKey, + }); + assert.isTrue(record.claimed); + }); +} From 4a1f988d0b57d91650c277d14f9bde1d5716d766 Mon Sep 17 00:00:00 2001 From: Pileks Date: Thu, 6 Aug 2026 18:21:51 +0200 Subject: [PATCH 14/31] execute_sell ix, sdk, tests --- programs/relaunch/src/constants.rs | 22 + programs/relaunch/src/error.rs | 2 + programs/relaunch/src/events.rs | 9 + .../relaunch/src/instructions/execute_sell.rs | 213 +++++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + programs/relaunch/src/pump_amm.rs | 119 ++++- sdk/src/relaunch/v0.1/RelaunchClient.ts | 180 ++++++++ sdk/src/relaunch/v0.1/index.ts | 1 + sdk/src/relaunch/v0.1/pumpAmm.ts | 112 +++++ sdk/src/relaunch/v0.1/types/relaunch.ts | 418 ++++++++++++++++++ tasks.md | 7 +- tests/main.test.ts | 12 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/pumpAmm.ts | 112 ++--- tests/relaunch/unit/executeSell.test.ts | 338 ++++++++++++++ tests/relaunch/unit/venues.test.ts | 8 +- 17 files changed, 1458 insertions(+), 104 deletions(-) create mode 100644 programs/relaunch/src/instructions/execute_sell.rs create mode 100644 sdk/src/relaunch/v0.1/pumpAmm.ts create mode 100644 tests/relaunch/unit/executeSell.test.ts diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index 047099d2d..dff77b9fd 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -55,6 +55,28 @@ pub mod pump_fees_program { declare_id!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"); } +/// pump_amm's global config: the `["global_config"]` PDA under pump_amm. +pub mod pump_amm_global_config { + use anchor_lang::prelude::declare_id; + + declare_id!("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw"); +} + +/// pump_amm's event authority: the `["__event_authority"]` PDA under pump_amm. +pub mod pump_amm_event_authority { + use anchor_lang::prelude::declare_id; + + declare_id!("GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR"); +} + +/// The fee config the pump fee program keeps for pump_amm: the +/// `["fee_config", pump_amm program id]` PDA under pump_fees. +pub mod pump_amm_fee_config { + use anchor_lang::prelude::declare_id; + + declare_id!("5PHirr8joyTMp9JMm6nW7hNDVyEYdkzDqazxPD7RaTjx"); +} + // Orca pub mod whirlpool_program { diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 68cb04ba3..ae19abc6e 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -42,4 +42,6 @@ pub enum RelaunchError { RelaunchNotFailed, #[msg("Deposit record has already been claimed")] AlreadyClaimed, + #[msg("Grace period has elapsed")] + GracePeriodElapsed, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index f08d87935..102f2e219 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -76,6 +76,15 @@ pub struct RelaunchMarkedFailedEvent { pub relaunch: Pubkey, } +#[event] +pub struct SellExecutedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub base_sold: u64, + pub quote_recovered: u64, + pub new_state: RelaunchState, +} + #[event] pub struct RefundClaimedEvent { pub common: CommonFields, diff --git a/programs/relaunch/src/instructions/execute_sell.rs b/programs/relaunch/src/instructions/execute_sell.rs new file mode 100644 index 000000000..601baf9d0 --- /dev/null +++ b/programs/relaunch/src/instructions/execute_sell.rs @@ -0,0 +1,213 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::token::{Mint, Token, TokenAccount}; +use anchor_spl::token_interface; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, SellExecutedEvent}; +use crate::pump_amm; +use crate::state::{Relaunch, RelaunchState}; +use crate::{ + pump_amm_event_authority, pump_amm_fee_config, pump_amm_global_config, pump_amm_program, + pump_fees_program, usdc_mint, +}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct ExecuteSellArgs { + /// The admin's live, client-computed slippage floor on the sell proceeds. + pub min_quote_out: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct ExecuteSell<'info> { + #[account( + mut, + has_one = admin, + has_one = old_mint, + has_one = source_quote_mint, + has_one = source_pool, + has_one = relaunch_signer, + has_one = old_token_vault, + has_one = source_quote_vault, + )] + pub relaunch: Box>, + + pub admin: Signer<'info>, + + /// CHECK: the vault authority that signs the sell; pump_amm requires the + /// user account writable. + #[account(mut)] + pub relaunch_signer: UncheckedAccount<'info>, + + #[account(mint::token_program = base_token_program)] + pub old_mint: Box>, + + pub source_quote_mint: Box>, + + #[account(mut)] + pub old_token_vault: Box>, + + #[account(mut)] + pub source_quote_vault: Box>, + + /// CHECK: fingerprint-validated at init and pinned by has_one; pump_amm + /// rechecks its internal consistency. + #[account(mut)] + pub source_pool: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_global_config::id())] + pub pump_global_config: UncheckedAccount<'info>, + + /// CHECK: pump_amm requires membership in its global config + pub protocol_fee_recipient: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this ATA + #[account(mut)] + pub protocol_fee_recipient_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this against the pool's stored field + #[account(mut)] + pub pool_base_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this against the pool's stored field + #[account(mut)] + pub pool_quote_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm derives this from the pool's coin_creator + #[account(mut)] + pub coin_creator_vault_ata: UncheckedAccount<'info>, + + /// CHECK: pump_amm derives this from the pool's coin_creator + pub coin_creator_vault_authority: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_fee_config::id())] + pub pump_fee_config: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_fees_program::id())] + pub pump_fee_program: UncheckedAccount<'info>, + + /// CHECK: pump_amm address-checks this PDA (it need not exist) + pub pool_v2: UncheckedAccount<'info>, + + /// CHECK: pump_amm requires membership in its global config's buyback list + pub buyback_fee_recipient: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this ATA + #[account(mut)] + pub buyback_fee_recipient_token_account: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_event_authority::id())] + pub pump_event_authority: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_program::id())] + pub pump_amm_program: UncheckedAccount<'info>, + + pub base_token_program: Interface<'info, token_interface::TokenInterface>, + pub quote_token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl ExecuteSell<'_> { + pub fn validate(&self, _args: &ExecuteSellArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::SellPending, + RelaunchError::RelaunchNotSellPending + ); + + let clock = Clock::get()?; + require_gte!( + self.relaunch.unix_timestamp_closed.unwrap() + + self.relaunch.grace_period_seconds as i64, + clock.unix_timestamp, + RelaunchError::GracePeriodElapsed + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: ExecuteSellArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let base_sold = ctx.accounts.old_token_vault.amount; + let quote_before = ctx.accounts.source_quote_vault.amount; + + pump_amm::sell( + pump_amm::Sell { + pool: ctx.accounts.source_pool.to_account_info(), + user: ctx.accounts.relaunch_signer.to_account_info(), + global_config: ctx.accounts.pump_global_config.to_account_info(), + base_mint: ctx.accounts.old_mint.to_account_info(), + quote_mint: ctx.accounts.source_quote_mint.to_account_info(), + user_base_token_account: ctx.accounts.old_token_vault.to_account_info(), + user_quote_token_account: ctx.accounts.source_quote_vault.to_account_info(), + pool_base_token_account: ctx.accounts.pool_base_token_account.to_account_info(), + pool_quote_token_account: ctx.accounts.pool_quote_token_account.to_account_info(), + protocol_fee_recipient: ctx.accounts.protocol_fee_recipient.to_account_info(), + protocol_fee_recipient_token_account: ctx + .accounts + .protocol_fee_recipient_token_account + .to_account_info(), + base_token_program: ctx.accounts.base_token_program.to_account_info(), + quote_token_program: ctx.accounts.quote_token_program.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + associated_token_program: ctx.accounts.associated_token_program.to_account_info(), + event_authority: ctx.accounts.pump_event_authority.to_account_info(), + program: ctx.accounts.pump_amm_program.to_account_info(), + coin_creator_vault_ata: ctx.accounts.coin_creator_vault_ata.to_account_info(), + coin_creator_vault_authority: ctx + .accounts + .coin_creator_vault_authority + .to_account_info(), + fee_config: ctx.accounts.pump_fee_config.to_account_info(), + fee_program: ctx.accounts.pump_fee_program.to_account_info(), + pool_v2: ctx.accounts.pool_v2.to_account_info(), + buyback_fee_recipient: ctx.accounts.buyback_fee_recipient.to_account_info(), + buyback_fee_recipient_token_account: ctx + .accounts + .buyback_fee_recipient_token_account + .to_account_info(), + }, + base_sold, + args.min_quote_out, + signer, + )?; + + ctx.accounts.source_quote_vault.reload()?; + let quote_recovered = ctx.accounts.source_quote_vault.amount - quote_before; + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.quote_recovered = quote_recovered; + if relaunch.source_quote_mint == usdc_mint::id() { + relaunch.usdc_recovered = quote_recovered; + relaunch.state = RelaunchState::Swapped; + } else { + relaunch.state = RelaunchState::Sold; + } + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(SellExecutedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + base_sold, + quote_recovered, + new_state: ctx.accounts.relaunch.state, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index bc2236395..149689da4 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,6 +1,7 @@ pub mod claim_refund; pub mod close_deposits; pub mod deposit; +pub mod execute_sell; pub mod initialize_relaunch; pub mod mark_failed; pub mod start_deposits; @@ -8,6 +9,7 @@ pub mod start_deposits; pub use claim_refund::*; pub use close_deposits::*; pub use deposit::*; +pub use execute_sell::*; pub use initialize_relaunch::*; pub use mark_failed::*; pub use start_deposits::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 98e7ccdcb..ec33e5298 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -56,6 +56,11 @@ pub mod relaunch { CloseDeposits::handle(ctx) } + #[access_control(ctx.accounts.validate(&args))] + pub fn execute_sell(ctx: Context, args: ExecuteSellArgs) -> Result<()> { + ExecuteSell::handle(ctx, args) + } + #[access_control(ctx.accounts.validate())] pub fn mark_failed(ctx: Context) -> Result<()> { MarkFailed::handle(ctx) diff --git a/programs/relaunch/src/pump_amm.rs b/programs/relaunch/src/pump_amm.rs index f8d46193f..5f0941b92 100644 --- a/programs/relaunch/src/pump_amm.rs +++ b/programs/relaunch/src/pump_amm.rs @@ -1,10 +1,20 @@ -//! Types for reading pump_amm accounts. +//! Types for reading pump_amm accounts, plus hand-built CPI builders — +//! pump_amm ships no Anchor-0.29 crate. Discriminators and args come from +//! pump_amm's IDL; the deployed program is newer than that IDL and requires a +//! remaining-accounts tail past the published list — the `pool_v2` PDA plus +//! one buyback fee recipient with its quote ATA (verified against live +//! mainnet swaps 2026-08-04). use anchor_lang::prelude::*; +use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; +use anchor_lang::solana_program::program::invoke_signed; use crate::error::RelaunchError; +use crate::pump_amm_program; pub const POOL_DISCRIMINATOR: [u8; 8] = [241, 154, 109, 4, 17, 177, 109, 188]; +pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173]; + /// The prefix of pump_amm's `Pool` account that canonicality validation /// reads; trailing fields are ignored. #[derive(AnchorDeserialize)] @@ -26,3 +36,110 @@ impl PumpSwapPool { .map_err(|_| error!(RelaunchError::SourcePoolNotCanonical)) } } + +/// The accounts of pump_amm's `sell`, in instruction order. +pub struct Sell<'info> { + pub pool: AccountInfo<'info>, + pub user: AccountInfo<'info>, + pub global_config: AccountInfo<'info>, + pub base_mint: AccountInfo<'info>, + pub quote_mint: AccountInfo<'info>, + pub user_base_token_account: AccountInfo<'info>, + pub user_quote_token_account: AccountInfo<'info>, + pub pool_base_token_account: AccountInfo<'info>, + pub pool_quote_token_account: AccountInfo<'info>, + pub protocol_fee_recipient: AccountInfo<'info>, + pub protocol_fee_recipient_token_account: AccountInfo<'info>, + pub base_token_program: AccountInfo<'info>, + pub quote_token_program: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, + pub associated_token_program: AccountInfo<'info>, + pub event_authority: AccountInfo<'info>, + pub program: AccountInfo<'info>, + pub coin_creator_vault_ata: AccountInfo<'info>, + pub coin_creator_vault_authority: AccountInfo<'info>, + pub fee_config: AccountInfo<'info>, + pub fee_program: AccountInfo<'info>, + // remaining-accounts tail + pub pool_v2: AccountInfo<'info>, + pub buyback_fee_recipient: AccountInfo<'info>, + pub buyback_fee_recipient_token_account: AccountInfo<'info>, +} + +pub fn sell( + accounts: Sell, + base_amount_in: u64, + min_quote_amount_out: u64, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + let mut data = Vec::with_capacity(24); + data.extend_from_slice(&SELL_DISCRIMINATOR); + data.extend_from_slice(&base_amount_in.to_le_bytes()); + data.extend_from_slice(&min_quote_amount_out.to_le_bytes()); + + let metas = vec![ + AccountMeta::new(accounts.pool.key(), false), + AccountMeta::new(accounts.user.key(), true), + AccountMeta::new_readonly(accounts.global_config.key(), false), + AccountMeta::new_readonly(accounts.base_mint.key(), false), + AccountMeta::new_readonly(accounts.quote_mint.key(), false), + AccountMeta::new(accounts.user_base_token_account.key(), false), + AccountMeta::new(accounts.user_quote_token_account.key(), false), + AccountMeta::new(accounts.pool_base_token_account.key(), false), + AccountMeta::new(accounts.pool_quote_token_account.key(), false), + AccountMeta::new_readonly(accounts.protocol_fee_recipient.key(), false), + AccountMeta::new(accounts.protocol_fee_recipient_token_account.key(), false), + AccountMeta::new_readonly(accounts.base_token_program.key(), false), + AccountMeta::new_readonly(accounts.quote_token_program.key(), false), + AccountMeta::new_readonly(accounts.system_program.key(), false), + AccountMeta::new_readonly(accounts.associated_token_program.key(), false), + AccountMeta::new_readonly(accounts.event_authority.key(), false), + AccountMeta::new_readonly(accounts.program.key(), false), + AccountMeta::new(accounts.coin_creator_vault_ata.key(), false), + AccountMeta::new_readonly(accounts.coin_creator_vault_authority.key(), false), + AccountMeta::new_readonly(accounts.fee_config.key(), false), + AccountMeta::new_readonly(accounts.fee_program.key(), false), + AccountMeta::new_readonly(accounts.pool_v2.key(), false), + AccountMeta::new_readonly(accounts.buyback_fee_recipient.key(), false), + AccountMeta::new(accounts.buyback_fee_recipient_token_account.key(), false), + ]; + + let account_infos = [ + accounts.pool, + accounts.user, + accounts.global_config, + accounts.base_mint, + accounts.quote_mint, + accounts.user_base_token_account, + accounts.user_quote_token_account, + accounts.pool_base_token_account, + accounts.pool_quote_token_account, + accounts.protocol_fee_recipient, + accounts.protocol_fee_recipient_token_account, + accounts.base_token_program, + accounts.quote_token_program, + accounts.system_program, + accounts.associated_token_program, + accounts.event_authority, + accounts.program, + accounts.coin_creator_vault_ata, + accounts.coin_creator_vault_authority, + accounts.fee_config, + accounts.fee_program, + accounts.pool_v2, + accounts.buyback_fee_recipient, + accounts.buyback_fee_recipient_token_account, + ]; + + invoke_signed( + &Instruction { + program_id: pump_amm_program::id(), + accounts: metas, + data, + }, + &account_infos, + signer_seeds, + )?; + + Ok(()) +} diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index fa28692f6..4af471c09 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -8,6 +8,8 @@ import { TransactionSignature, } from "@solana/web3.js"; import { + AccountLayout, + ASSOCIATED_TOKEN_PROGRAM_ID, createInitializeMint2Instruction, getAssociatedTokenAddressSync, MINT_SIZE, @@ -17,6 +19,8 @@ import BN from "bn.js"; import { MAINNET_USDC, MPL_TOKEN_METADATA_PROGRAM_ID, + PUMP_AMM_PROGRAM_ID, + PUMP_FEES_PROGRAM_ID, RELAUNCH_V0_1_PROGRAM_ID, } from "../../constants.js"; import { @@ -30,6 +34,15 @@ import { getRelaunchSignerAddr, getDepositRecordAddr, } from "./pda.js"; +import { + getPumpCreatorVaultAuthorityAddr, + getPumpPoolV2Addr, + parsePumpGlobalConfig, + parsePumpPool, + PUMP_AMM_EVENT_AUTHORITY, + PUMP_AMM_FEE_CONFIG, + PUMP_AMM_GLOBAL_CONFIG, +} from "./pumpAmm.js"; import { getEventAuthorityAddr, getMetadataAddr } from "../../pda.js"; export type CreateRelaunchClientParams = { @@ -233,6 +246,91 @@ export class RelaunchClient { }); } + executeSellIx({ + relaunch, + oldMint, + oldTokenProgram, + sourceQuoteMint, + sourcePool, + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreator, + protocolFeeRecipient, + buybackFeeRecipient, + minQuoteOut, + admin = this.provider.publicKey, + }: { + relaunch: PublicKey; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + sourceQuoteMint: PublicKey; + sourcePool: PublicKey; + poolBaseTokenAccount: PublicKey; + poolQuoteTokenAccount: PublicKey; + coinCreator: PublicKey; + protocolFeeRecipient: PublicKey; + buybackFeeRecipient: PublicKey; + minQuoteOut: BN; + admin?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + oldTokenProgram, + ); + const sourceQuoteVault = getAssociatedTokenAddressSync( + sourceQuoteMint, + relaunchSigner, + true, + ); + const coinCreatorVaultAuthority = + getPumpCreatorVaultAuthorityAddr(coinCreator); + + return this.relaunchProgram.methods.executeSell({ minQuoteOut }).accounts({ + relaunch, + admin, + relaunchSigner, + oldMint, + sourceQuoteMint, + oldTokenVault, + sourceQuoteVault, + sourcePool, + pumpGlobalConfig: PUMP_AMM_GLOBAL_CONFIG, + protocolFeeRecipient, + protocolFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + protocolFeeRecipient, + true, + ), + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreatorVaultAta: getAssociatedTokenAddressSync( + sourceQuoteMint, + coinCreatorVaultAuthority, + true, + ), + coinCreatorVaultAuthority, + pumpFeeConfig: PUMP_AMM_FEE_CONFIG, + pumpFeeProgram: PUMP_FEES_PROGRAM_ID, + poolV2: getPumpPoolV2Addr(oldMint), + buybackFeeRecipient, + buybackFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + buybackFeeRecipient, + true, + ), + pumpEventAuthority: PUMP_AMM_EVENT_AUTHORITY, + pumpAmmProgram: PUMP_AMM_PROGRAM_ID, + baseTokenProgram: oldTokenProgram, + quoteTokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }); + } + markFailedIx({ relaunch }: { relaunch: PublicKey }) { return this.relaunchProgram.methods.markFailed().accounts({ relaunch, @@ -312,6 +410,88 @@ export class RelaunchClient { }).rpc(); } + // Sells the whole old-token vault as the admin (the provider wallet), + // deriving the pump account set from the stored relaunch, its pool, and + // pump's global config. When minQuoteOut is not given, it is computed live + // from the pool reserves: the constant-product output of the sell minus + // slippageBps (which must also cover pump's swap fees). + async executeSell({ + relaunch, + minQuoteOut, + slippageBps = 100, + }: { + relaunch: PublicKey; + minQuoteOut?: BN; + slippageBps?: number; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + const oldMintAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.oldMint, + ); + if (oldMintAccount === null) { + throw new Error( + `old mint ${storedRelaunch.oldMint.toBase58()} does not exist`, + ); + } + + const poolAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.sourcePool, + ); + if (poolAccount === null) { + throw new Error( + `source pool ${storedRelaunch.sourcePool.toBase58()} does not exist`, + ); + } + const pool = parsePumpPool(poolAccount.data); + + const globalConfigAccount = await this.provider.connection.getAccountInfo( + PUMP_AMM_GLOBAL_CONFIG, + ); + if (globalConfigAccount === null) { + throw new Error("pump_amm global config does not exist"); + } + const globalConfig = parsePumpGlobalConfig(globalConfigAccount.data); + + if (minQuoteOut === undefined) { + const [baseIn, baseReserve, quoteReserve] = await Promise.all( + [ + storedRelaunch.oldTokenVault, + pool.poolBaseTokenAccount, + pool.poolQuoteTokenAccount, + ].map((address) => this.fetchTokenBalance(address)), + ); + const grossOut = (quoteReserve * baseIn) / (baseReserve + baseIn); + const floor = (grossOut * (10_000n - BigInt(slippageBps))) / 10_000n; + minQuoteOut = new BN(floor.toString()); + } + + return this.executeSellIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + oldTokenProgram: oldMintAccount.owner, + sourceQuoteMint: storedRelaunch.sourceQuoteMint, + sourcePool: storedRelaunch.sourcePool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient: globalConfig.protocolFeeRecipients[0], + buybackFeeRecipient: globalConfig.buybackFeeRecipients[0], + minQuoteOut, + }).rpc(); + } + + private async fetchTokenBalance(address: PublicKey): Promise { + const accountInfo = await this.provider.connection.getAccountInfo(address); + if (accountInfo === null) { + throw new Error(`token account ${address.toBase58()} does not exist`); + } + return AccountLayout.decode(accountInfo.data).amount; + } + // Claims a refund for the given depositor (the provider wallet by default), // reading the old mint and its owner program from the stored relaunch. async claimRefund({ diff --git a/sdk/src/relaunch/v0.1/index.ts b/sdk/src/relaunch/v0.1/index.ts index aa5f243a4..51360ddbe 100644 --- a/sdk/src/relaunch/v0.1/index.ts +++ b/sdk/src/relaunch/v0.1/index.ts @@ -1,3 +1,4 @@ export * from "./types/index.js"; export * from "./pda.js"; +export * from "./pumpAmm.js"; export * from "./RelaunchClient.js"; diff --git a/sdk/src/relaunch/v0.1/pumpAmm.ts b/sdk/src/relaunch/v0.1/pumpAmm.ts new file mode 100644 index 000000000..346b621a1 --- /dev/null +++ b/sdk/src/relaunch/v0.1/pumpAmm.ts @@ -0,0 +1,112 @@ +import { PublicKey } from "@solana/web3.js"; +import { PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID } from "../../constants.js"; + +export const PUMP_AMM_GLOBAL_CONFIG = PublicKey.findProgramAddressSync( + [Buffer.from("global_config")], + PUMP_AMM_PROGRAM_ID, +)[0]; + +export const PUMP_AMM_EVENT_AUTHORITY = PublicKey.findProgramAddressSync( + [Buffer.from("__event_authority")], + PUMP_AMM_PROGRAM_ID, +)[0]; + +// Per-consumer fee config the pump fee program keeps for pump_amm. +export const PUMP_AMM_FEE_CONFIG = PublicKey.findProgramAddressSync( + [Buffer.from("fee_config"), PUMP_AMM_PROGRAM_ID.toBuffer()], + PUMP_FEES_PROGRAM_ID, +)[0]; + +// The current pump_amm requires this PDA as the first remaining account on +// buys and sells (checked by address only — the account need not exist). +export function getPumpPoolV2Addr(baseMint: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("pool-v2"), baseMint.toBuffer()], + PUMP_AMM_PROGRAM_ID, + )[0]; +} + +export function getPumpCreatorVaultAuthorityAddr( + coinCreator: PublicKey, +): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("creator_vault"), coinCreator.toBuffer()], + PUMP_AMM_PROGRAM_ID, + )[0]; +} + +export type PumpPoolAccount = { + poolBump: number; + index: number; + creator: PublicKey; + baseMint: PublicKey; + quoteMint: PublicKey; + lpMint: PublicKey; + poolBaseTokenAccount: PublicKey; + poolQuoteTokenAccount: PublicKey; + coinCreator: PublicKey; +}; + +// Pool account layout: 8-byte discriminator, pool_bump u8, index u16 LE, +// then creator / base_mint / quote_mint / lp_mint / pool_base_token_account / +// pool_quote_token_account pubkeys, lp_supply u64 LE, coin_creator pubkey. +export function parsePumpPool(data: Buffer): PumpPoolAccount { + return { + poolBump: data.readUInt8(8), + index: data.readUInt16LE(9), + creator: new PublicKey(data.subarray(11, 43)), + baseMint: new PublicKey(data.subarray(43, 75)), + quoteMint: new PublicKey(data.subarray(75, 107)), + lpMint: new PublicKey(data.subarray(107, 139)), + poolBaseTokenAccount: new PublicKey(data.subarray(139, 171)), + poolQuoteTokenAccount: new PublicKey(data.subarray(171, 203)), + coinCreator: new PublicKey(data.subarray(211, 243)), + }; +} + +export type PumpGlobalConfigAccount = { + protocolFeeRecipients: PublicKey[]; + buybackFeeRecipients: PublicKey[]; +}; + +function readRecipients(data: Buffer, offset: number): PublicKey[] { + const recipients: PublicKey[] = []; + for (let i = 0; i < 8; i++) { + const recipient = new PublicKey( + data.subarray(offset + i * 32, offset + (i + 1) * 32), + ); + if (!recipient.equals(PublicKey.default)) { + recipients.push(recipient); + } + } + return recipients; +} + +// GlobalConfig layout (unset recipient slots are the default pubkey and are +// filtered out): +// +// offset size field +// 0 8 discriminator +// 8 32 admin +// 40 8 lp_fee_basis_points +// 48 8 protocol_fee_basis_points +// 56 1 disable_flags +// 57 256 protocol_fee_recipients [Pubkey; 8] +// 313 8 coin_creator_fee_basis_points +// 321 32 admin_set_coin_creator_authority +// 353 32 whitelist_pda +// 385 32 reserved_fee_recipient +// 417 1 mayhem_mode_enabled +// 418 224 reserved_fee_recipients [Pubkey; 7] +// 642 1 is_cashback_enabled +// 643 256 buyback_fee_recipients [Pubkey; 8] +// 899 8 buyback_basis_points +// 907 32 boost_authority +// 939 1 boost_enabled +// 940 total +export function parsePumpGlobalConfig(data: Buffer): PumpGlobalConfigAccount { + return { + protocolFeeRecipients: readRecipients(data, 57), + buybackFeeRecipients: readRecipients(data, 643), + }; +} diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index e6dc5b2a6..db1bfac81 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -256,6 +256,161 @@ export type Relaunch = { ]; args: []; }, + { + name: "executeSell"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "admin"; + isMut: false; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: true; + isSigner: false; + docs: ["user account writable."]; + }, + { + name: "oldMint"; + isMut: false; + isSigner: false; + }, + { + name: "sourceQuoteMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourcePool"; + isMut: true; + isSigner: false; + docs: ["rechecks its internal consistency."]; + }, + { + name: "pumpGlobalConfig"; + isMut: false; + isSigner: false; + }, + { + name: "protocolFeeRecipient"; + isMut: false; + isSigner: false; + }, + { + name: "protocolFeeRecipientTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "poolBaseTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "poolQuoteTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "coinCreatorVaultAta"; + isMut: true; + isSigner: false; + }, + { + name: "coinCreatorVaultAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "pumpFeeConfig"; + isMut: false; + isSigner: false; + }, + { + name: "pumpFeeProgram"; + isMut: false; + isSigner: false; + }, + { + name: "poolV2"; + isMut: false; + isSigner: false; + }, + { + name: "buybackFeeRecipient"; + isMut: false; + isSigner: false; + }, + { + name: "buybackFeeRecipientTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "pumpEventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "pumpAmmProgram"; + isMut: false; + isSigner: false; + }, + { + name: "baseTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "quoteTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "ExecuteSellArgs"; + }; + }, + ]; + }, { name: "markFailed"; accounts: [ @@ -611,6 +766,21 @@ export type Relaunch = { ]; }; }, + { + name: "ExecuteSellArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "minQuoteOut"; + docs: [ + "The admin's live, client-computed slippage floor on the sell proceeds.", + ]; + type: "u64"; + }, + ]; + }; + }, { name: "InitializeRelaunchArgs"; type: { @@ -912,6 +1082,40 @@ export type Relaunch = { }, ]; }, + { + name: "SellExecutedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "baseSold"; + type: "u64"; + index: false; + }, + { + name: "quoteRecovered"; + type: "u64"; + index: false; + }, + { + name: "newState"; + type: { + defined: "RelaunchState"; + }; + index: false; + }, + ]; + }, { name: "RefundClaimedEvent"; fields: [ @@ -1051,6 +1255,11 @@ export type Relaunch = { name: "AlreadyClaimed"; msg: "Deposit record has already been claimed"; }, + { + code: 6020; + name: "GracePeriodElapsed"; + msg: "Grace period has elapsed"; + }, ]; }; @@ -1312,6 +1521,161 @@ export const IDL: Relaunch = { ], args: [], }, + { + name: "executeSell", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "admin", + isMut: false, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: true, + isSigner: false, + docs: ["user account writable."], + }, + { + name: "oldMint", + isMut: false, + isSigner: false, + }, + { + name: "sourceQuoteMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "sourcePool", + isMut: true, + isSigner: false, + docs: ["rechecks its internal consistency."], + }, + { + name: "pumpGlobalConfig", + isMut: false, + isSigner: false, + }, + { + name: "protocolFeeRecipient", + isMut: false, + isSigner: false, + }, + { + name: "protocolFeeRecipientTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "poolBaseTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "poolQuoteTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "coinCreatorVaultAta", + isMut: true, + isSigner: false, + }, + { + name: "coinCreatorVaultAuthority", + isMut: false, + isSigner: false, + }, + { + name: "pumpFeeConfig", + isMut: false, + isSigner: false, + }, + { + name: "pumpFeeProgram", + isMut: false, + isSigner: false, + }, + { + name: "poolV2", + isMut: false, + isSigner: false, + }, + { + name: "buybackFeeRecipient", + isMut: false, + isSigner: false, + }, + { + name: "buybackFeeRecipientTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "pumpEventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "pumpAmmProgram", + isMut: false, + isSigner: false, + }, + { + name: "baseTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "quoteTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "ExecuteSellArgs", + }, + }, + ], + }, { name: "markFailed", accounts: [ @@ -1667,6 +2031,21 @@ export const IDL: Relaunch = { ], }, }, + { + name: "ExecuteSellArgs", + type: { + kind: "struct", + fields: [ + { + name: "minQuoteOut", + docs: [ + "The admin's live, client-computed slippage floor on the sell proceeds.", + ], + type: "u64", + }, + ], + }, + }, { name: "InitializeRelaunchArgs", type: { @@ -1968,6 +2347,40 @@ export const IDL: Relaunch = { }, ], }, + { + name: "SellExecutedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "baseSold", + type: "u64", + index: false, + }, + { + name: "quoteRecovered", + type: "u64", + index: false, + }, + { + name: "newState", + type: { + defined: "RelaunchState", + }, + index: false, + }, + ], + }, { name: "RefundClaimedEvent", fields: [ @@ -2107,5 +2520,10 @@ export const IDL: Relaunch = { name: "AlreadyClaimed", msg: "Deposit record has already been claimed", }, + { + code: 6020, + name: "GracePeriodElapsed", + msg: "Grace period has elapsed", + }, ], }; diff --git a/tasks.md b/tasks.md index 670d11c2f..cce8b0175 100644 --- a/tasks.md +++ b/tasks.md @@ -27,12 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 3 — execute_sell, execute_usdc_swap, deposit_via_buy" -- [NEXT] 3.1 `execute_sell` (instruction + SDK + tests) - - Hand-built `cpi/pump_amm.rs` sell builder (discriminator + borsh args + verified 21-account list from design §06) - - Admin + `SellPending` + grace gate; sell full vault; `quote_recovered` from measured delta; WSOL → `Sold`, USDC → `Swapped` - - Tests per plan §6: both quote variants, slippage failure leaves state unchanged, non-admin/wrong-state/after-grace, Token-2022 base pool - -- [ ] 3.2 `execute_usdc_swap` (instruction + SDK + tests) +- [NEXT] 3.2 `execute_usdc_swap` (instruction + SDK + tests) - Whirlpool `swap_v2` CPI (`orca_whirlpools_client` if clean under 0.29, else hand-built; verified 15-account list incl. `memo_program`); pool pinned `address = USDC_SWAP_POOL` - Admin + `Sold`; full WSOL balance a→b; set `usdc_recovered` - SDK: tick-array derivation helper + `min_usdc_out` diff --git a/tests/main.test.ts b/tests/main.test.ts index 71aa773d4..1b8d43803 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -43,16 +43,14 @@ import { LiquidationClient, RelaunchClient, LOW_FEE_RAYDIUM_CONFIG, + PUMP_AMM_FEE_CONFIG, + PUMP_AMM_GLOBAL_CONFIG, PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID, WHIRLPOOL_PROGRAM_ID, sha256, } from "@metadaoproject/programs"; -import { - PUMP_GLOBAL_CONFIG, - PUMP_FEE_CONFIG, - PUMP_GLOBAL_VOLUME_ACCUMULATOR, -} from "./relaunch/pumpAmm.js"; +import { PUMP_GLOBAL_VOLUME_ACCUMULATOR } from "./relaunch/pumpAmm.js"; import { WHIRLPOOLS_CONFIG, WHIRLPOOL_FEE_TIER } from "./relaunch/whirlpool.js"; import { LaunchpadClient as LaunchpadClientV6 } from "@metadaoproject/programs/launchpad/v0.6"; import { LaunchpadClient as LaunchpadClientV8 } from "@metadaoproject/programs/launchpad/v0.8"; @@ -275,7 +273,7 @@ before(async function () { }, }, { - address: PUMP_GLOBAL_CONFIG, + address: PUMP_AMM_GLOBAL_CONFIG, info: { data: fs.readFileSync("./tests/fixtures/pump-global-config"), executable: false, @@ -284,7 +282,7 @@ before(async function () { }, }, { - address: PUMP_FEE_CONFIG, + address: PUMP_AMM_FEE_CONFIG, info: { data: fs.readFileSync("./tests/fixtures/pump-fee-config"), executable: false, diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 7c01402d2..2129dcc0f 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -4,6 +4,7 @@ import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; +import executeSell from "./unit/executeSell.test.js"; import markFailed from "./unit/markFailed.test.js"; import claimRefund from "./unit/claimRefund.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; @@ -23,6 +24,7 @@ export default function suite() { describe("#start_deposits", startDeposits); describe("#deposit", deposit); describe("#close_deposits", closeDeposits); + describe("#execute_sell", executeSell); describe("#mark_failed", markFailed); describe("#claim_refund", claimRefund); } diff --git a/tests/relaunch/pumpAmm.ts b/tests/relaunch/pumpAmm.ts index 9d72339e6..e6ebf7248 100644 --- a/tests/relaunch/pumpAmm.ts +++ b/tests/relaunch/pumpAmm.ts @@ -7,7 +7,13 @@ import { import * as token from "@solana/spl-token"; import { BanksClient, ProgramTestContext } from "solana-bankrun"; import { - MAINNET_USDC, + getPumpCreatorVaultAuthorityAddr, + getPumpPoolV2Addr, + parsePumpGlobalConfig, + PumpGlobalConfigAccount, + PUMP_AMM_EVENT_AUTHORITY, + PUMP_AMM_FEE_CONFIG, + PUMP_AMM_GLOBAL_CONFIG, PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID, PUMP_PROGRAM_ID, @@ -15,27 +21,11 @@ import { const TOKEN_ACCOUNT_RENT = 2_039_280n; -export const PUMP_GLOBAL_CONFIG = PublicKey.findProgramAddressSync( - [Buffer.from("global_config")], - PUMP_AMM_PROGRAM_ID, -)[0]; - -export const PUMP_EVENT_AUTHORITY = PublicKey.findProgramAddressSync( - [Buffer.from("__event_authority")], - PUMP_AMM_PROGRAM_ID, -)[0]; - export const PUMP_GLOBAL_VOLUME_ACCUMULATOR = PublicKey.findProgramAddressSync( [Buffer.from("global_volume_accumulator")], PUMP_AMM_PROGRAM_ID, )[0]; -// Per-consumer fee config the pump fee program keeps for pump_amm. -export const PUMP_FEE_CONFIG = PublicKey.findProgramAddressSync( - [Buffer.from("fee_config"), PUMP_AMM_PROGRAM_ID.toBuffer()], - PUMP_FEES_PROGRAM_ID, -)[0]; - // Stable coin creator used by fabricated pools so creator-vault ATAs are // deterministic across tests. export const PUMP_TEST_COIN_CREATOR = Keypair.fromSeed( @@ -86,15 +76,6 @@ export function getCanonicalPumpPoolAddr( }); } -export function getCreatorVaultAuthorityAddr( - coinCreator: PublicKey, -): PublicKey { - return PublicKey.findProgramAddressSync( - [Buffer.from("creator_vault"), coinCreator.toBuffer()], - PUMP_AMM_PROGRAM_ID, - )[0]; -} - export function getUserVolumeAccumulatorAddr(user: PublicKey): PublicKey { return PublicKey.findProgramAddressSync( [Buffer.from("user_volume_accumulator"), user.toBuffer()], @@ -102,70 +83,27 @@ export function getUserVolumeAccumulatorAddr(user: PublicKey): PublicKey { )[0]; } -// The current pump_amm requires this PDA as the first remaining account on -// buys and sells (checked by address only — the account need not exist; -// verified against live mainnet swaps 2026-08-04). Newer than the published -// IDL's account list. -export function getPoolV2Addr(baseMint: PublicKey): PublicKey { - return PublicKey.findProgramAddressSync( - [Buffer.from("pool-v2"), baseMint.toBuffer()], - PUMP_AMM_PROGRAM_ID, - )[0]; -} - -async function globalConfigRecipients( +async function fetchGlobalConfig( banksClient: BanksClient, - offset: number, -): Promise { - const globalConfig = await banksClient.getAccount(PUMP_GLOBAL_CONFIG); - const data = Buffer.from(globalConfig!.data); - const recipients: PublicKey[] = []; - for (let i = 0; i < 8; i++) { - const recipient = new PublicKey( - data.subarray(offset + i * 32, offset + (i + 1) * 32), - ); - if (!recipient.equals(PublicKey.default)) { - recipients.push(recipient); - } - } - return recipients; +): Promise { + const globalConfig = await banksClient.getAccount(PUMP_AMM_GLOBAL_CONFIG); + return parsePumpGlobalConfig(Buffer.from(globalConfig!.data)); } -// GlobalConfig layout -// -// offset size field -// 0 8 discriminator -// 8 32 admin -// 40 8 lp_fee_basis_points -// 48 8 protocol_fee_basis_points -// 56 1 disable_flags -// 57 256 protocol_fee_recipients [Pubkey; 8] <- getProtocolFeeRecipient -// 313 8 coin_creator_fee_basis_points -// 321 32 admin_set_coin_creator_authority -// 353 32 whitelist_pda -// 385 32 reserved_fee_recipient -// 417 1 mayhem_mode_enabled -// 418 224 reserved_fee_recipients [Pubkey; 7] -// 642 1 is_cashback_enabled -// 643 256 buyback_fee_recipients [Pubkey; 8] <- getBuybackFeeRecipients -// 899 8 buyback_basis_points -// 907 32 boost_authority -// 939 1 boost_enabled -// 940 total export async function getProtocolFeeRecipient( banksClient: BanksClient, ): Promise { - const recipients = await globalConfigRecipients(banksClient, 57); - if (recipients.length === 0) { + const { protocolFeeRecipients } = await fetchGlobalConfig(banksClient); + if (protocolFeeRecipients.length === 0) { throw new Error("no protocol fee recipient in global config"); } - return recipients[0]; + return protocolFeeRecipients[0]; } export async function getBuybackFeeRecipients( banksClient: BanksClient, ): Promise { - return globalConfigRecipients(banksClient, 643); + return (await fetchGlobalConfig(banksClient)).buybackFeeRecipients; } function packTokenAccount({ @@ -362,14 +300,14 @@ export async function writePumpPool({ // fabricated pool already wrote so accrued balances survive. const creatorVaultAta = token.getAssociatedTokenAddressSync( quoteMint, - getCreatorVaultAuthorityAddr(coinCreator), + getPumpCreatorVaultAuthorityAddr(coinCreator), true, ); if (!(await context.banksClient.getAccount(creatorVaultAta))) { writeTokenAccount(context, { address: creatorVaultAta, mint: quoteMint, - owner: getCreatorVaultAuthorityAddr(coinCreator), + owner: getPumpCreatorVaultAuthorityAddr(coinCreator), amount: 0n, }); } @@ -424,7 +362,7 @@ function swapAccountMetas( protocolFeeRecipient, true, ); - const coinCreatorVaultAuthority = getCreatorVaultAuthorityAddr( + const coinCreatorVaultAuthority = getPumpCreatorVaultAuthorityAddr( pool.coinCreator, ); const coinCreatorVaultAta = token.getAssociatedTokenAddressSync( @@ -436,7 +374,7 @@ function swapAccountMetas( return [ { pubkey: pool.pool, isSigner: false, isWritable: true }, { pubkey: user, isSigner: true, isWritable: true }, - { pubkey: PUMP_GLOBAL_CONFIG, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_GLOBAL_CONFIG, isSigner: false, isWritable: false }, { pubkey: pool.baseMint, isSigner: false, isWritable: false }, { pubkey: pool.quoteMint, isSigner: false, isWritable: false }, { pubkey: userBaseTokenAccount, isSigner: false, isWritable: true }, @@ -457,7 +395,7 @@ function swapAccountMetas( isSigner: false, isWritable: false, }, - { pubkey: PUMP_EVENT_AUTHORITY, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_EVENT_AUTHORITY, isSigner: false, isWritable: false }, { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false }, { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true }, { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: false }, @@ -490,10 +428,10 @@ export function pumpSellIx({ const keys = [ ...swapAccountMetas(pool, user, protocolFeeRecipient), - { pubkey: PUMP_FEE_CONFIG, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_FEE_CONFIG, isSigner: false, isWritable: false }, { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false }, { - pubkey: getPoolV2Addr(pool.baseMint), + pubkey: getPumpPoolV2Addr(pool.baseMint), isSigner: false, isWritable: false, }, @@ -555,10 +493,10 @@ export function pumpBuyIx({ isSigner: false, isWritable: true, }, - { pubkey: PUMP_FEE_CONFIG, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_FEE_CONFIG, isSigner: false, isWritable: false }, { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false }, { - pubkey: getPoolV2Addr(pool.baseMint), + pubkey: getPumpPoolV2Addr(pool.baseMint), isSigner: false, isWritable: false, }, @@ -599,7 +537,7 @@ export function pumpInitUserVolumeAccumulatorIx({ isWritable: true, }, { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: PUMP_EVENT_AUTHORITY, isSigner: false, isWritable: false }, + { pubkey: PUMP_AMM_EVENT_AUTHORITY, isSigner: false, isWritable: false }, { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false }, ], data: Buffer.from([94, 6, 202, 115, 255, 96, 232, 183]), diff --git a/tests/relaunch/unit/executeSell.test.ts b/tests/relaunch/unit/executeSell.test.ts new file mode 100644 index 000000000..98f126a47 --- /dev/null +++ b/tests/relaunch/unit/executeSell.test.ts @@ -0,0 +1,338 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BanksClient } from "solana-bankrun"; +import { MAINNET_USDC, RelaunchClient } from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { + getBuybackFeeRecipients, + getProtocolFeeRecipient, + writePumpPool, + PumpPool, +} from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const DEFAULT_THRESHOLD_BPS = 1000; +// 10% of the 1B-token default supply = 100M tokens. +const DEPOSIT_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount( + address, + { ...raw, data: Buffer.from(raw.data) } as any, + tokenProgram, + ).amount; +} + +// The constant-product output of selling the deposited amount, before fees. +function grossQuoteOut(quoteReserve: bigint): bigint { + return (quoteReserve * DEPOSIT_AMOUNT) / (POOL_BASE_RESERVE + DEPOSIT_AMOUNT); +} + +export default function suite() { + let client: RelaunchClient; + + before(function () { + client = this.relaunch; + }); + + const setupSellPendingRelaunch = async function ( + this: Mocha.Context, + { + quoteMint = token.NATIVE_MINT, + oldTokenProgram = token.TOKEN_PROGRAM_ID, + close = true, + }: { + quoteMint?: PublicKey; + oldTokenProgram?: PublicKey; + close?: boolean; + } = {}, + ): Promise<{ + relaunch: PublicKey; + pool: PumpPool; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: quoteMint.equals(token.NATIVE_MINT) + ? WSOL_POOL_QUOTE_RESERVE + : USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: quoteMint, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: DEFAULT_THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + await client + .depositIx({ + relaunch, + oldMint: setup.oldMint, + oldTokenProgram, + amount: new BN(DEPOSIT_AMOUNT.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + if (close) { + await client.closeDepositsIx({ relaunch }).rpc(); + } + + return { ...setup, pool, relaunch }; + }; + + it("sells the old-token vault into a WSOL-quoted pool and lands in Sold", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupSellPendingRelaunch.call(this); + + await client.executeSell({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(oldVaultBalance.toString(), "0"); + + const poolBaseBalance = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ); + assert.equal( + poolBaseBalance.toString(), + (POOL_BASE_RESERVE + DEPOSIT_AMOUNT).toString(), + ); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal( + storedRelaunch.quoteRecovered.toString(), + quoteVaultBalance.toString(), + ); + + // The proceeds are the constant-product output minus pump's fees. + const gross = grossQuoteOut(WSOL_POOL_QUOTE_RESERVE); + const quoteRecovered = BigInt(storedRelaunch.quoteRecovered.toString()); + assert.isTrue(quoteRecovered > (gross * 97n) / 100n); + assert.isTrue(quoteRecovered <= gross); + + assert.equal(storedRelaunch.usdcRecovered.toString(), "0"); + assert.equal(storedRelaunch.seqNum.toString(), "4"); + }); + + it("jumps straight to Swapped for a USDC-quoted pool", async function () { + const { relaunch, oldTokenProgram } = await setupSellPendingRelaunch.call( + this, + { quoteMint: MAINNET_USDC }, + ); + + await client.executeSell({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.swapped); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(oldVaultBalance.toString(), "0"); + + assert.equal( + storedRelaunch.usdcRecovered.toString(), + storedRelaunch.quoteRecovered.toString(), + ); + + const usdcVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.usdcVault, + ); + assert.equal( + storedRelaunch.usdcRecovered.toString(), + usdcVaultBalance.toString(), + ); + + const gross = grossQuoteOut(USDC_POOL_QUOTE_RESERVE); + const usdcRecovered = BigInt(storedRelaunch.usdcRecovered.toString()); + assert.isTrue(usdcRecovered > (gross * 97n) / 100n); + assert.isTrue(usdcRecovered <= gross); + }); + + it("sells a Token-2022 old token correctly", async function () { + const { relaunch, oldTokenProgram } = await setupSellPendingRelaunch.call( + this, + { oldTokenProgram: token.TOKEN_2022_PROGRAM_ID }, + ); + + await client.executeSell({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(oldVaultBalance.toString(), "0"); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal( + storedRelaunch.quoteRecovered.toString(), + quoteVaultBalance.toString(), + ); + }); + + it("fails when min_quote_out is above the achievable proceeds, leaving state unchanged", async function () { + const { relaunch, oldTokenProgram } = + await setupSellPendingRelaunch.call(this); + + try { + // The whole quote reserve is unreachable output for any sell. + await client.executeSell({ + relaunch, + minQuoteOut: new BN(WSOL_POOL_QUOTE_RESERVE.toString()), + }); + assert.fail("Should have thrown error"); + } catch (e) {} + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + assert.equal(storedRelaunch.quoteRecovered.toString(), "0"); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(oldVaultBalance.toString(), DEPOSIT_AMOUNT.toString()); + + // The same sell with a live floor succeeds, so only the floor differed. + await client.executeSell({ relaunch }); + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + }); + + it("fails when a non-admin executes the sell", async function () { + const { relaunch, pool, oldMint, oldTokenProgram } = + await setupSellPendingRelaunch.call(this); + const nonAdmin = Keypair.generate(); + + try { + await client + .executeSellIx({ + relaunch, + oldMint, + oldTokenProgram, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient: await getProtocolFeeRecipient(this.banksClient), + buybackFeeRecipient: ( + await getBuybackFeeRecipients(this.banksClient) + )[0], + minQuoteOut: new BN(0), + admin: nonAdmin.publicKey, + }) + .signers([nonAdmin]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintHasOne"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails before deposits close", async function () { + const { relaunch } = await setupSellPendingRelaunch.call(this, { + close: false, + }); + + try { + await client.executeSell({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + }); + + it("fails when the sell has already been executed", async function () { + const { relaunch } = await setupSellPendingRelaunch.call(this); + + await client.executeSell({ relaunch }); + + try { + await client.executeSell({ relaunch, minQuoteOut: new BN(1) }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + }); + + it("fails after the grace period elapses", async function () { + const { relaunch, oldTokenProgram } = + await setupSellPendingRelaunch.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + + try { + await client.executeSell({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "GracePeriodElapsed"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(oldVaultBalance.toString(), DEPOSIT_AMOUNT.toString()); + }); +} diff --git a/tests/relaunch/unit/venues.test.ts b/tests/relaunch/unit/venues.test.ts index f3496d6fc..9d698bebb 100644 --- a/tests/relaunch/unit/venues.test.ts +++ b/tests/relaunch/unit/venues.test.ts @@ -7,7 +7,6 @@ import { setupRelaunch } from "../utils.js"; import { getBuybackFeeRecipients, getCanonicalPumpPoolAddr, - getCreatorVaultAuthorityAddr, getProtocolFeeRecipient, getUserVolumeAccumulatorAddr, pumpBuyIx, @@ -22,7 +21,10 @@ import { whirlpoolSwapV2Ix, wrapSol, } from "../whirlpool.js"; -import { MAINNET_USDC } from "@metadaoproject/programs"; +import { + getPumpCreatorVaultAuthorityAddr, + MAINNET_USDC, +} from "@metadaoproject/programs"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL @@ -62,7 +64,7 @@ export default function suite() { const feeAtas = [ protocolFeeRecipient, buybackFeeRecipient, - getCreatorVaultAuthorityAddr(pool.coinCreator), + getPumpCreatorVaultAuthorityAddr(pool.coinCreator), ].map((owner) => token.getAssociatedTokenAddressSync(pool.quoteMint, owner, true), ); From 4f31835eef997117b28e62df785e5e1c2a6d6898 Mon Sep 17 00:00:00 2001 From: Pileks Date: Mon, 10 Aug 2026 12:08:56 -0700 Subject: [PATCH 15/31] execute_usdc_swap ix, sdk, tests --- programs/relaunch/src/constants.rs | 7 + programs/relaunch/src/error.rs | 4 + programs/relaunch/src/events.rs | 8 + .../src/instructions/execute_usdc_swap.rs | 164 +++++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 9 + programs/relaunch/src/whirlpool.rs | 101 ++++++ sdk/src/relaunch/v0.1/RelaunchClient.ts | 188 ++++++++-- sdk/src/relaunch/v0.1/index.ts | 1 + sdk/src/relaunch/v0.1/types/relaunch.ts | 330 ++++++++++++++++++ sdk/src/relaunch/v0.1/whirlpool.ts | 94 +++++ tasks.md | 8 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/executeUsdcSwap.test.ts | 288 +++++++++++++++ tests/relaunch/whirlpool.ts | 33 ++ 15 files changed, 1198 insertions(+), 41 deletions(-) create mode 100644 programs/relaunch/src/instructions/execute_usdc_swap.rs create mode 100644 programs/relaunch/src/whirlpool.rs create mode 100644 sdk/src/relaunch/v0.1/whirlpool.ts create mode 100644 tests/relaunch/unit/executeUsdcSwap.test.ts diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index dff77b9fd..d9faa3f64 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -91,3 +91,10 @@ pub mod usdc_swap_pool { declare_id!("Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE"); } + +/// SPL Memo, required by whirlpool's v2 instructions. +pub mod memo_program { + use anchor_lang::prelude::declare_id; + + declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); +} diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index ae19abc6e..a82336e19 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -44,4 +44,8 @@ pub enum RelaunchError { AlreadyClaimed, #[msg("Grace period has elapsed")] GracePeriodElapsed, + #[msg("Relaunch must be in the Sold state")] + RelaunchNotSold, + #[msg("Swap output is below the minimum output amount")] + SlippageExceeded, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 102f2e219..ed117c022 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -85,6 +85,14 @@ pub struct SellExecutedEvent { pub new_state: RelaunchState, } +#[event] +pub struct UsdcSwapExecutedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub wsol_sold: u64, + pub usdc_recovered: u64, +} + #[event] pub struct RefundClaimedEvent { pub common: CommonFields, diff --git a/programs/relaunch/src/instructions/execute_usdc_swap.rs b/programs/relaunch/src/instructions/execute_usdc_swap.rs new file mode 100644 index 000000000..1aa41a069 --- /dev/null +++ b/programs/relaunch/src/instructions/execute_usdc_swap.rs @@ -0,0 +1,164 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Token, TokenAccount}; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, UsdcSwapExecutedEvent}; +use crate::state::{Relaunch, RelaunchState}; +use crate::whirlpool; +use crate::{memo_program, usdc_mint, usdc_swap_pool, whirlpool_program, wsol_mint}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct ExecuteUsdcSwapArgs { + /// The admin's live, client-computed slippage floor on the swap output. + pub min_usdc_out: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct ExecuteUsdcSwap<'info> { + #[account( + mut, + has_one = admin, + has_one = relaunch_signer, + has_one = source_quote_vault, + has_one = usdc_vault, + )] + pub relaunch: Box>, + + pub admin: Signer<'info>, + + /// CHECK: the vault authority that signs the swap. + pub relaunch_signer: UncheckedAccount<'info>, + + /// The WSOL vault holding the sell proceeds; `Sold` only occurs for + /// WSOL-quoted sources. + #[account(mut)] + pub source_quote_vault: Box>, + + #[account(mut)] + pub usdc_vault: Box>, + + /// CHECK: pinned to the program's swap-venue constant; whirlpool rechecks + /// its internal consistency. + #[account(mut, address = usdc_swap_pool::id())] + pub whirlpool: UncheckedAccount<'info>, + + /// CHECK: fixed address; the pinned pool's token A. + #[account(address = wsol_mint::id())] + pub wsol_mint: UncheckedAccount<'info>, + + /// CHECK: fixed address; the pinned pool's token B. + #[account(address = usdc_mint::id())] + pub usdc_mint: UncheckedAccount<'info>, + + /// CHECK: whirlpool checks this against the pool's stored field + #[account(mut)] + pub whirlpool_wsol_vault: UncheckedAccount<'info>, + + /// CHECK: whirlpool checks this against the pool's stored field + #[account(mut)] + pub whirlpool_usdc_vault: UncheckedAccount<'info>, + + /// CHECK: whirlpool validates the tick-array sequence + #[account(mut)] + pub tick_array_0: UncheckedAccount<'info>, + + /// CHECK: whirlpool validates the tick-array sequence + #[account(mut)] + pub tick_array_1: UncheckedAccount<'info>, + + /// CHECK: whirlpool validates the tick-array sequence + #[account(mut)] + pub tick_array_2: UncheckedAccount<'info>, + + /// CHECK: whirlpool checks this PDA + #[account(mut)] + pub oracle: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = memo_program::id())] + pub memo_program: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = whirlpool_program::id())] + pub whirlpool_program: UncheckedAccount<'info>, + + pub token_program: Program<'info, Token>, +} + +impl ExecuteUsdcSwap<'_> { + pub fn validate(&self, _args: &ExecuteUsdcSwapArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Sold, + RelaunchError::RelaunchNotSold + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: ExecuteUsdcSwapArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let wsol_sold = ctx.accounts.source_quote_vault.amount; + let usdc_before = ctx.accounts.usdc_vault.amount; + + whirlpool::swap_v2( + whirlpool::SwapV2 { + token_program_a: ctx.accounts.token_program.to_account_info(), + token_program_b: ctx.accounts.token_program.to_account_info(), + memo_program: ctx.accounts.memo_program.to_account_info(), + token_authority: ctx.accounts.relaunch_signer.to_account_info(), + whirlpool: ctx.accounts.whirlpool.to_account_info(), + token_mint_a: ctx.accounts.wsol_mint.to_account_info(), + token_mint_b: ctx.accounts.usdc_mint.to_account_info(), + token_owner_account_a: ctx.accounts.source_quote_vault.to_account_info(), + token_vault_a: ctx.accounts.whirlpool_wsol_vault.to_account_info(), + token_owner_account_b: ctx.accounts.usdc_vault.to_account_info(), + token_vault_b: ctx.accounts.whirlpool_usdc_vault.to_account_info(), + tick_array_0: ctx.accounts.tick_array_0.to_account_info(), + tick_array_1: ctx.accounts.tick_array_1.to_account_info(), + tick_array_2: ctx.accounts.tick_array_2.to_account_info(), + oracle: ctx.accounts.oracle.to_account_info(), + }, + wsol_sold, + args.min_usdc_out, + whirlpool::MIN_SQRT_PRICE, + true, // amount specified is input + true, // a→b: WSOL → USDC + signer, + )?; + + ctx.accounts.usdc_vault.reload()?; + let usdc_recovered = ctx.accounts.usdc_vault.amount - usdc_before; + + // Whirlpool enforces min_usdc_out internally; re-check the measured + // delta so the floor doesn't rest on the external program. + require_gte!( + usdc_recovered, + args.min_usdc_out, + RelaunchError::SlippageExceeded + ); + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.usdc_recovered = usdc_recovered; + relaunch.state = RelaunchState::Swapped; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(UsdcSwapExecutedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + wsol_sold, + usdc_recovered, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 149689da4..4be39068e 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -2,6 +2,7 @@ pub mod claim_refund; pub mod close_deposits; pub mod deposit; pub mod execute_sell; +pub mod execute_usdc_swap; pub mod initialize_relaunch; pub mod mark_failed; pub mod start_deposits; @@ -10,6 +11,7 @@ pub use claim_refund::*; pub use close_deposits::*; pub use deposit::*; pub use execute_sell::*; +pub use execute_usdc_swap::*; pub use initialize_relaunch::*; pub use mark_failed::*; pub use start_deposits::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index ec33e5298..9706beebb 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -7,6 +7,7 @@ pub mod events; pub mod instructions; pub mod pump_amm; pub mod state; +pub mod whirlpool; pub use constants::*; pub use state::*; @@ -61,6 +62,14 @@ pub mod relaunch { ExecuteSell::handle(ctx, args) } + #[access_control(ctx.accounts.validate(&args))] + pub fn execute_usdc_swap( + ctx: Context, + args: ExecuteUsdcSwapArgs, + ) -> Result<()> { + ExecuteUsdcSwap::handle(ctx, args) + } + #[access_control(ctx.accounts.validate())] pub fn mark_failed(ctx: Context) -> Result<()> { MarkFailed::handle(ctx) diff --git a/programs/relaunch/src/whirlpool.rs b/programs/relaunch/src/whirlpool.rs new file mode 100644 index 000000000..5c61f205d --- /dev/null +++ b/programs/relaunch/src/whirlpool.rs @@ -0,0 +1,101 @@ +//! Hand-built CPI builder for Orca Whirlpool's `swap_v2` — the same pattern +//! as `pump_amm.rs`, since a generated client crate is not worth a new +//! dependency for a single instruction. +use anchor_lang::prelude::*; +use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; +use anchor_lang::solana_program::program::invoke_signed; + +use crate::whirlpool_program; + +pub const SWAP_V2_DISCRIMINATOR: [u8; 8] = [43, 4, 237, 11, 26, 201, 30, 98]; + +/// Whirlpool's global minimum sqrt price; passing it as the price limit of an +/// a→b swap means "no limit beyond the pool's own bounds". +pub const MIN_SQRT_PRICE: u128 = 4_295_048_016; + +/// The accounts of whirlpool's `swap_v2`, in instruction order. +pub struct SwapV2<'info> { + pub token_program_a: AccountInfo<'info>, + pub token_program_b: AccountInfo<'info>, + pub memo_program: AccountInfo<'info>, + pub token_authority: AccountInfo<'info>, + pub whirlpool: AccountInfo<'info>, + pub token_mint_a: AccountInfo<'info>, + pub token_mint_b: AccountInfo<'info>, + pub token_owner_account_a: AccountInfo<'info>, + pub token_vault_a: AccountInfo<'info>, + pub token_owner_account_b: AccountInfo<'info>, + pub token_vault_b: AccountInfo<'info>, + pub tick_array_0: AccountInfo<'info>, + pub tick_array_1: AccountInfo<'info>, + pub tick_array_2: AccountInfo<'info>, + pub oracle: AccountInfo<'info>, +} + +pub fn swap_v2( + accounts: SwapV2, + amount: u64, + other_amount_threshold: u64, + sqrt_price_limit: u128, + amount_specified_is_input: bool, + a_to_b: bool, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + let mut data = Vec::with_capacity(43); + data.extend_from_slice(&SWAP_V2_DISCRIMINATOR); + data.extend_from_slice(&amount.to_le_bytes()); + data.extend_from_slice(&other_amount_threshold.to_le_bytes()); + data.extend_from_slice(&sqrt_price_limit.to_le_bytes()); + data.push(amount_specified_is_input as u8); + data.push(a_to_b as u8); + // remaining_accounts_info: Option = None + data.push(0); + + let metas = vec![ + AccountMeta::new_readonly(accounts.token_program_a.key(), false), + AccountMeta::new_readonly(accounts.token_program_b.key(), false), + AccountMeta::new_readonly(accounts.memo_program.key(), false), + AccountMeta::new_readonly(accounts.token_authority.key(), true), + AccountMeta::new(accounts.whirlpool.key(), false), + AccountMeta::new_readonly(accounts.token_mint_a.key(), false), + AccountMeta::new_readonly(accounts.token_mint_b.key(), false), + AccountMeta::new(accounts.token_owner_account_a.key(), false), + AccountMeta::new(accounts.token_vault_a.key(), false), + AccountMeta::new(accounts.token_owner_account_b.key(), false), + AccountMeta::new(accounts.token_vault_b.key(), false), + AccountMeta::new(accounts.tick_array_0.key(), false), + AccountMeta::new(accounts.tick_array_1.key(), false), + AccountMeta::new(accounts.tick_array_2.key(), false), + AccountMeta::new(accounts.oracle.key(), false), + ]; + + let account_infos = [ + accounts.token_program_a, + accounts.token_program_b, + accounts.memo_program, + accounts.token_authority, + accounts.whirlpool, + accounts.token_mint_a, + accounts.token_mint_b, + accounts.token_owner_account_a, + accounts.token_vault_a, + accounts.token_owner_account_b, + accounts.token_vault_b, + accounts.tick_array_0, + accounts.tick_array_1, + accounts.tick_array_2, + accounts.oracle, + ]; + + invoke_signed( + &Instruction { + program_id: whirlpool_program::id(), + accounts: metas, + data, + }, + &account_infos, + signer_seeds, + )?; + + Ok(()) +} diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 4af471c09..7b26ee0f0 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -13,6 +13,7 @@ import { createInitializeMint2Instruction, getAssociatedTokenAddressSync, MINT_SIZE, + NATIVE_MINT, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import BN from "bn.js"; @@ -22,6 +23,7 @@ import { PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID, RELAUNCH_V0_1_PROGRAM_ID, + WHIRLPOOL_PROGRAM_ID, } from "../../constants.js"; import { RelaunchProgram, @@ -43,6 +45,13 @@ import { PUMP_AMM_FEE_CONFIG, PUMP_AMM_GLOBAL_CONFIG, } from "./pumpAmm.js"; +import { + getWhirlpoolOracleAddr, + getWhirlpoolSwapTickArrayAddrs, + MEMO_PROGRAM_ID, + parseWhirlpool, + USDC_SWAP_POOL, +} from "./whirlpool.js"; import { getEventAuthorityAddr, getMetadataAddr } from "../../pda.js"; export type CreateRelaunchClientParams = { @@ -289,46 +298,157 @@ export class RelaunchClient { const coinCreatorVaultAuthority = getPumpCreatorVaultAuthorityAddr(coinCreator); - return this.relaunchProgram.methods.executeSell({ minQuoteOut }).accounts({ - relaunch, - admin, - relaunchSigner, - oldMint, - sourceQuoteMint, - oldTokenVault, - sourceQuoteVault, - sourcePool, - pumpGlobalConfig: PUMP_AMM_GLOBAL_CONFIG, - protocolFeeRecipient, - protocolFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + return this.relaunchProgram.methods + .executeSell({ minQuoteOut }) + .accounts({ + relaunch, + admin, + relaunchSigner, + oldMint, sourceQuoteMint, + oldTokenVault, + sourceQuoteVault, + sourcePool, + pumpGlobalConfig: PUMP_AMM_GLOBAL_CONFIG, protocolFeeRecipient, - true, - ), - poolBaseTokenAccount, - poolQuoteTokenAccount, - coinCreatorVaultAta: getAssociatedTokenAddressSync( - sourceQuoteMint, + protocolFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + protocolFeeRecipient, + true, + ), + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreatorVaultAta: getAssociatedTokenAddressSync( + sourceQuoteMint, + coinCreatorVaultAuthority, + true, + ), coinCreatorVaultAuthority, - true, - ), - coinCreatorVaultAuthority, - pumpFeeConfig: PUMP_AMM_FEE_CONFIG, - pumpFeeProgram: PUMP_FEES_PROGRAM_ID, - poolV2: getPumpPoolV2Addr(oldMint), - buybackFeeRecipient, - buybackFeeRecipientTokenAccount: getAssociatedTokenAddressSync( - sourceQuoteMint, + pumpFeeConfig: PUMP_AMM_FEE_CONFIG, + pumpFeeProgram: PUMP_FEES_PROGRAM_ID, + poolV2: getPumpPoolV2Addr(oldMint), buybackFeeRecipient, + buybackFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + buybackFeeRecipient, + true, + ), + pumpEventAuthority: PUMP_AMM_EVENT_AUTHORITY, + pumpAmmProgram: PUMP_AMM_PROGRAM_ID, + baseTokenProgram: oldTokenProgram, + quoteTokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .preInstructions([ + // The sell runs close to the 200k default, and PDA derivation costs + // vary with the bump seeds; give it fixed headroom. + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ]); + } + + executeUsdcSwapIx({ + relaunch, + whirlpoolWsolVault, + whirlpoolUsdcVault, + tickArrays, + minUsdcOut, + whirlpool = USDC_SWAP_POOL, + admin = this.provider.publicKey, + }: { + relaunch: PublicKey; + whirlpoolWsolVault: PublicKey; + whirlpoolUsdcVault: PublicKey; + tickArrays: [PublicKey, PublicKey, PublicKey]; + minUsdcOut: BN; + whirlpool?: PublicKey; + admin?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + + const sourceQuoteVault = getAssociatedTokenAddressSync( + NATIVE_MINT, + relaunchSigner, + true, + ); + const usdcVault = getAssociatedTokenAddressSync( + MAINNET_USDC, + relaunchSigner, + true, + ); + + return this.relaunchProgram.methods + .executeUsdcSwap({ minUsdcOut }) + .accounts({ + relaunch, + admin, + relaunchSigner, + sourceQuoteVault, + usdcVault, + whirlpool, + wsolMint: NATIVE_MINT, + usdcMint: MAINNET_USDC, + whirlpoolWsolVault, + whirlpoolUsdcVault, + tickArray0: tickArrays[0], + tickArray1: tickArrays[1], + tickArray2: tickArrays[2], + oracle: getWhirlpoolOracleAddr(whirlpool), + memoProgram: MEMO_PROGRAM_ID, + whirlpoolProgram: WHIRLPOOL_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .preInstructions([ + // The whirlpool swap's cost grows with the initialized ticks it + // crosses; give it fixed headroom over the 200k default. + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ]); + } + + // Swaps the whole WSOL vault to USDC as the admin (the provider wallet), + // deriving the whirlpool account set from the pinned pool's live state. + // When minUsdcOut is not given, it is computed from the pool's spot price + // minus slippageBps (which must also cover the swap fee + price impact). + async executeUsdcSwap({ + relaunch, + minUsdcOut, + slippageBps = 100, + }: { + relaunch: PublicKey; + minUsdcOut?: BN; + slippageBps?: number; + }): Promise { + const whirlpoolAccount = + await this.provider.connection.getAccountInfo(USDC_SWAP_POOL); + if (whirlpoolAccount === null) { + throw new Error(`whirlpool ${USDC_SWAP_POOL.toBase58()} does not exist`); + } + const whirlpool = parseWhirlpool(whirlpoolAccount.data); + + if (minUsdcOut === undefined) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const wsolIn = await this.fetchTokenBalance( + getAssociatedTokenAddressSync(NATIVE_MINT, relaunchSigner, true), + ); + // Spot price in USDC-raw per WSOL-raw is (sqrtPrice / 2^64)^2. + const spotOut = + (wsolIn * whirlpool.sqrtPrice * whirlpool.sqrtPrice) >> 128n; + const floor = (spotOut * (10_000n - BigInt(slippageBps))) / 10_000n; + minUsdcOut = new BN(floor.toString()); + } + + return this.executeUsdcSwapIx({ + relaunch, + whirlpoolWsolVault: whirlpool.tokenVaultA, + whirlpoolUsdcVault: whirlpool.tokenVaultB, + tickArrays: getWhirlpoolSwapTickArrayAddrs( + USDC_SWAP_POOL, + whirlpool.tickCurrentIndex, + whirlpool.tickSpacing, true, ), - pumpEventAuthority: PUMP_AMM_EVENT_AUTHORITY, - pumpAmmProgram: PUMP_AMM_PROGRAM_ID, - baseTokenProgram: oldTokenProgram, - quoteTokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - systemProgram: SystemProgram.programId, - }); + minUsdcOut, + }).rpc(); } markFailedIx({ relaunch }: { relaunch: PublicKey }) { diff --git a/sdk/src/relaunch/v0.1/index.ts b/sdk/src/relaunch/v0.1/index.ts index 51360ddbe..fb2064adb 100644 --- a/sdk/src/relaunch/v0.1/index.ts +++ b/sdk/src/relaunch/v0.1/index.ts @@ -1,4 +1,5 @@ export * from "./types/index.js"; export * from "./pda.js"; export * from "./pumpAmm.js"; +export * from "./whirlpool.js"; export * from "./RelaunchClient.js"; diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index db1bfac81..17a5ba5b9 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -411,6 +411,119 @@ export type Relaunch = { }, ]; }, + { + name: "executeUsdcSwap"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "admin"; + isMut: false; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + docs: [ + "The WSOL vault holding the sell proceeds; `Sold` only occurs for", + "WSOL-quoted sources.", + ]; + }, + { + name: "usdcVault"; + isMut: true; + isSigner: false; + }, + { + name: "whirlpool"; + isMut: true; + isSigner: false; + docs: ["its internal consistency."]; + }, + { + name: "wsolMint"; + isMut: false; + isSigner: false; + }, + { + name: "usdcMint"; + isMut: false; + isSigner: false; + }, + { + name: "whirlpoolWsolVault"; + isMut: true; + isSigner: false; + }, + { + name: "whirlpoolUsdcVault"; + isMut: true; + isSigner: false; + }, + { + name: "tickArray0"; + isMut: true; + isSigner: false; + }, + { + name: "tickArray1"; + isMut: true; + isSigner: false; + }, + { + name: "tickArray2"; + isMut: true; + isSigner: false; + }, + { + name: "oracle"; + isMut: true; + isSigner: false; + }, + { + name: "memoProgram"; + isMut: false; + isSigner: false; + }, + { + name: "whirlpoolProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "ExecuteUsdcSwapArgs"; + }; + }, + ]; + }, { name: "markFailed"; accounts: [ @@ -781,6 +894,21 @@ export type Relaunch = { ]; }; }, + { + name: "ExecuteUsdcSwapArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "minUsdcOut"; + docs: [ + "The admin's live, client-computed slippage floor on the swap output.", + ]; + type: "u64"; + }, + ]; + }; + }, { name: "InitializeRelaunchArgs"; type: { @@ -1116,6 +1244,33 @@ export type Relaunch = { }, ]; }, + { + name: "UsdcSwapExecutedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "wsolSold"; + type: "u64"; + index: false; + }, + { + name: "usdcRecovered"; + type: "u64"; + index: false; + }, + ]; + }, { name: "RefundClaimedEvent"; fields: [ @@ -1260,6 +1415,16 @@ export type Relaunch = { name: "GracePeriodElapsed"; msg: "Grace period has elapsed"; }, + { + code: 6021; + name: "RelaunchNotSold"; + msg: "Relaunch must be in the Sold state"; + }, + { + code: 6022; + name: "SlippageExceeded"; + msg: "Swap output is below the minimum output amount"; + }, ]; }; @@ -1676,6 +1841,119 @@ export const IDL: Relaunch = { }, ], }, + { + name: "executeUsdcSwap", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "admin", + isMut: false, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + docs: [ + "The WSOL vault holding the sell proceeds; `Sold` only occurs for", + "WSOL-quoted sources.", + ], + }, + { + name: "usdcVault", + isMut: true, + isSigner: false, + }, + { + name: "whirlpool", + isMut: true, + isSigner: false, + docs: ["its internal consistency."], + }, + { + name: "wsolMint", + isMut: false, + isSigner: false, + }, + { + name: "usdcMint", + isMut: false, + isSigner: false, + }, + { + name: "whirlpoolWsolVault", + isMut: true, + isSigner: false, + }, + { + name: "whirlpoolUsdcVault", + isMut: true, + isSigner: false, + }, + { + name: "tickArray0", + isMut: true, + isSigner: false, + }, + { + name: "tickArray1", + isMut: true, + isSigner: false, + }, + { + name: "tickArray2", + isMut: true, + isSigner: false, + }, + { + name: "oracle", + isMut: true, + isSigner: false, + }, + { + name: "memoProgram", + isMut: false, + isSigner: false, + }, + { + name: "whirlpoolProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "ExecuteUsdcSwapArgs", + }, + }, + ], + }, { name: "markFailed", accounts: [ @@ -2046,6 +2324,21 @@ export const IDL: Relaunch = { ], }, }, + { + name: "ExecuteUsdcSwapArgs", + type: { + kind: "struct", + fields: [ + { + name: "minUsdcOut", + docs: [ + "The admin's live, client-computed slippage floor on the swap output.", + ], + type: "u64", + }, + ], + }, + }, { name: "InitializeRelaunchArgs", type: { @@ -2381,6 +2674,33 @@ export const IDL: Relaunch = { }, ], }, + { + name: "UsdcSwapExecutedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "wsolSold", + type: "u64", + index: false, + }, + { + name: "usdcRecovered", + type: "u64", + index: false, + }, + ], + }, { name: "RefundClaimedEvent", fields: [ @@ -2525,5 +2845,15 @@ export const IDL: Relaunch = { name: "GracePeriodElapsed", msg: "Grace period has elapsed", }, + { + code: 6021, + name: "RelaunchNotSold", + msg: "Relaunch must be in the Sold state", + }, + { + code: 6022, + name: "SlippageExceeded", + msg: "Swap output is below the minimum output amount", + }, ], }; diff --git a/sdk/src/relaunch/v0.1/whirlpool.ts b/sdk/src/relaunch/v0.1/whirlpool.ts new file mode 100644 index 000000000..23fc8f26a --- /dev/null +++ b/sdk/src/relaunch/v0.1/whirlpool.ts @@ -0,0 +1,94 @@ +import { PublicKey } from "@solana/web3.js"; +import { WHIRLPOOL_PROGRAM_ID } from "../../constants.js"; + +// Orca Whirlpool SOL/USDC 0.04% — the swap venue pinned by the relaunch +// program's `usdc_swap_pool` constant. +export const USDC_SWAP_POOL = new PublicKey( + "Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE", +); + +// SPL Memo, required by whirlpool's v2 instructions. +export const MEMO_PROGRAM_ID = new PublicKey( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", +); + +const TICKS_PER_ARRAY = 88; + +export function getWhirlpoolOracleAddr(whirlpool: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("oracle"), whirlpool.toBuffer()], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +export function getWhirlpoolTickArrayAddr( + whirlpool: PublicKey, + startTickIndex: number, +): PublicKey { + return PublicKey.findProgramAddressSync( + [ + Buffer.from("tick_array"), + whirlpool.toBuffer(), + Buffer.from(startTickIndex.toString()), + ], + WHIRLPOOL_PROGRAM_ID, + )[0]; +} + +// The three tick arrays a swap walks, starting from the array holding the +// current tick and continuing in the swap's direction. +export function getWhirlpoolSwapTickArrayAddrs( + whirlpool: PublicKey, + tickCurrentIndex: number, + tickSpacing: number, + aToB: boolean, +): [PublicKey, PublicKey, PublicKey] { + const span = tickSpacing * TICKS_PER_ARRAY; + const currentStart = Math.floor(tickCurrentIndex / span) * span; + const direction = aToB ? -1 : 1; + return [0, 1, 2].map((k) => + getWhirlpoolTickArrayAddr(whirlpool, currentStart + k * direction * span), + ) as [PublicKey, PublicKey, PublicKey]; +} + +export type WhirlpoolAccount = { + tickSpacing: number; + sqrtPrice: bigint; + tickCurrentIndex: number; + tokenMintA: PublicKey; + tokenVaultA: PublicKey; + tokenMintB: PublicKey; + tokenVaultB: PublicKey; +}; + +// The prefix of whirlpool's `Whirlpool` account that the swap helpers read: +// +// offset size field +// 0 8 discriminator +// 8 32 whirlpools_config +// 40 1 whirlpool_bump +// 41 2 tick_spacing +// 43 2 fee_tier_index_seed +// 45 2 fee_rate +// 47 2 protocol_fee_rate +// 49 16 liquidity +// 65 16 sqrt_price +// 81 4 tick_current_index (i32) +// 85 8 protocol_fee_owed_a +// 93 8 protocol_fee_owed_b +// 101 32 token_mint_a +// 133 32 token_vault_a +// 165 16 fee_growth_global_a +// 181 32 token_mint_b +// 213 32 token_vault_b +export function parseWhirlpool(data: Buffer): WhirlpoolAccount { + return { + tickSpacing: data.readUInt16LE(41), + sqrtPrice: data.readBigUInt64LE(65) + (data.readBigUInt64LE(73) << 64n), + tickCurrentIndex: data.readInt32LE(81), + tokenMintA: new PublicKey(data.subarray(101, 133)), + tokenVaultA: new PublicKey(data.subarray(133, 165)), + tokenMintB: new PublicKey(data.subarray(181, 213)), + tokenVaultB: new PublicKey(data.subarray(213, 245)), + }; +} diff --git a/tasks.md b/tasks.md index cce8b0175..f0fa55791 100644 --- a/tasks.md +++ b/tasks.md @@ -27,13 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 3 — execute_sell, execute_usdc_swap, deposit_via_buy" -- [NEXT] 3.2 `execute_usdc_swap` (instruction + SDK + tests) - - Whirlpool `swap_v2` CPI (`orca_whirlpools_client` if clean under 0.29, else hand-built; verified 15-account list incl. `memo_program`); pool pinned `address = USDC_SWAP_POOL` - - Admin + `Sold`; full WSOL balance a→b; set `usdc_recovered` - - SDK: tick-array derivation helper + `min_usdc_out` - - Tests per plan §7: happy path, wrong pool address, slippage failure, non-admin/wrong-state - -- [ ] 3.3 `deposit_via_buy` (instruction + SDK + tests) +- [NEXT] 3.3 `deposit_via_buy` (instruction + SDK + tests) - `cpi/pump_amm.rs` buy builder (23 accounts = sell + volume accumulators); pull quote → buy → refund unspent → credit measured old-vault delta - Tests per plan §4: both quote variants, mixes with direct deposits, tight `max_quote_in` fails cleanly, window/state gates, credited tokens refund on later failure - SDK: `depositViaBuy()` + SOL→WSOL wrap pre-instructions diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 2129dcc0f..c03138e0d 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -5,6 +5,7 @@ import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; +import executeUsdcSwap from "./unit/executeUsdcSwap.test.js"; import markFailed from "./unit/markFailed.test.js"; import claimRefund from "./unit/claimRefund.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; @@ -25,6 +26,7 @@ export default function suite() { describe("#deposit", deposit); describe("#close_deposits", closeDeposits); describe("#execute_sell", executeSell); + describe("#execute_usdc_swap", executeUsdcSwap); describe("#mark_failed", markFailed); describe("#claim_refund", claimRefund); } diff --git a/tests/relaunch/unit/executeUsdcSwap.test.ts b/tests/relaunch/unit/executeUsdcSwap.test.ts new file mode 100644 index 000000000..b700c2ed3 --- /dev/null +++ b/tests/relaunch/unit/executeUsdcSwap.test.ts @@ -0,0 +1,288 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BankrunProvider } from "anchor-bankrun"; +import { BanksClient } from "solana-bankrun"; +import { + getWhirlpoolSwapTickArrayAddrs, + parseWhirlpool, + RelaunchClient, + USDC_SWAP_POOL, +} from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; +import { ensureWhirlpool, WhirlpoolFixture } from "../whirlpool.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +// Small enough that the sell proceeds (~4.9 SOL) swap through the whirlpool +// fixture (1000 SOL / 100k USDC) with well under 1% price impact. +const WSOL_POOL_QUOTE_RESERVE = 5n * 10n ** 9n; // 5 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const DEFAULT_THRESHOLD_BPS = 1000; +// 10% of the 1B-token default supply = 100M tokens. +const DEPOSIT_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount(address, { + ...raw, + data: Buffer.from(raw.data), + } as any).amount; +} + +export default function suite() { + let client: RelaunchClient; + let whirlpool: WhirlpoolFixture; + + before(async function () { + client = this.relaunch; + whirlpool = await ensureWhirlpool({ + provider: new BankrunProvider(this.context) as any, + payer: this.payer, + banksClient: this.banksClient, + }); + }); + + // The whirlpool account set the low-level ix builder needs, derived from + // the pinned pool's live state. + const swapAccounts = async function (this: Mocha.Context) { + const raw = await this.banksClient.getAccount(USDC_SWAP_POOL); + const pool = parseWhirlpool(Buffer.from(raw!.data)); + return { + whirlpoolWsolVault: pool.tokenVaultA, + whirlpoolUsdcVault: pool.tokenVaultB, + tickArrays: getWhirlpoolSwapTickArrayAddrs( + USDC_SWAP_POOL, + pool.tickCurrentIndex, + pool.tickSpacing, + true, + ), + }; + }; + + const setupSoldRelaunch = async function ( + this: Mocha.Context, + { sell = true }: { sell?: boolean } = {}, + ): Promise<{ relaunch: PublicKey }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: setup.oldTokenProgram, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: DEFAULT_THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + await client + .depositIx({ + relaunch, + oldMint: setup.oldMint, + oldTokenProgram: setup.oldTokenProgram, + amount: new BN(DEPOSIT_AMOUNT.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + if (sell) { + // 100 bps exactly matches pump's fee on these reserves, leaving no + // rounding margin; give the setup sell explicit headroom. + await client.executeSell({ relaunch, slippageBps: 200 }); + } + + return { relaunch }; + }; + + it("swaps the whole WSOL vault to USDC and lands in Swapped", async function () { + const { relaunch } = await setupSoldRelaunch.call(this); + + let storedRelaunch = await client.fetchRelaunch(relaunch); + const wsolSold = BigInt(storedRelaunch.quoteRecovered.toString()); + const whirlpoolWsolBefore = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultA, + ); + const whirlpoolUsdcBefore = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultB, + ); + + await client.executeUsdcSwap({ relaunch }); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.swapped); + + const wsolVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal(wsolVaultBalance.toString(), "0"); + + const usdcVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.usdcVault, + ); + assert.equal( + storedRelaunch.usdcRecovered.toString(), + usdcVaultBalance.toString(), + ); + + // The output is the spot value (100 USDC/SOL, i.e. one tenth in raw + // units) minus the 0.04% fee and a little price impact. + const spotOut = wsolSold / 10n; + const usdcRecovered = BigInt(storedRelaunch.usdcRecovered.toString()); + assert.isTrue(usdcRecovered > (spotOut * 97n) / 100n); + assert.isTrue(usdcRecovered <= spotOut); + + // The full WSOL balance went to the whirlpool, and every USDC the + // whirlpool paid out landed in the relaunch's vault. + const whirlpoolWsolAfter = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultA, + ); + assert.equal( + (whirlpoolWsolAfter - whirlpoolWsolBefore).toString(), + wsolSold.toString(), + ); + const whirlpoolUsdcAfter = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultB, + ); + assert.equal( + (whirlpoolUsdcBefore - whirlpoolUsdcAfter).toString(), + usdcRecovered.toString(), + ); + + assert.equal(storedRelaunch.quoteRecovered.toString(), wsolSold.toString()); + assert.equal(storedRelaunch.seqNum.toString(), "5"); + }); + + it("fails when the whirlpool account is not the pinned pool", async function () { + const { relaunch } = await setupSoldRelaunch.call(this); + const accounts = await swapAccounts.call(this); + + try { + await client + .executeUsdcSwapIx({ + relaunch, + ...accounts, + minUsdcOut: new BN(0), + whirlpool: Keypair.generate().publicKey, + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintAddress"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + }); + + it("fails when min_usdc_out is above the achievable output", async function () { + const { relaunch } = await setupSoldRelaunch.call(this); + + try { + // The whole USDC side of the whirlpool is unreachable output. + await client.executeUsdcSwap({ + relaunch, + minUsdcOut: new BN((100_000n * 10n ** 6n).toString()), + }); + assert.fail("Should have thrown error"); + } catch (e) {} + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + assert.equal(storedRelaunch.usdcRecovered.toString(), "0"); + + const wsolVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal( + wsolVaultBalance.toString(), + storedRelaunch.quoteRecovered.toString(), + ); + + // The same swap with a live floor succeeds, so only the floor differed. + await client.executeUsdcSwap({ relaunch }); + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.swapped); + }); + + it("fails when a non-admin executes the swap", async function () { + const { relaunch } = await setupSoldRelaunch.call(this); + const accounts = await swapAccounts.call(this); + const nonAdmin = Keypair.generate(); + + try { + await client + .executeUsdcSwapIx({ + relaunch, + ...accounts, + minUsdcOut: new BN(0), + admin: nonAdmin.publicKey, + }) + .signers([nonAdmin]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintHasOne"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + }); + + it("fails before the sell has been executed", async function () { + const { relaunch } = await setupSoldRelaunch.call(this, { sell: false }); + + try { + await client.executeUsdcSwap({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSold"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails when the swap has already been executed", async function () { + const { relaunch } = await setupSoldRelaunch.call(this); + + await client.executeUsdcSwap({ relaunch }); + + try { + await client.executeUsdcSwap({ relaunch, minUsdcOut: new BN(1) }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSold"); + } + }); +} diff --git a/tests/relaunch/whirlpool.ts b/tests/relaunch/whirlpool.ts index 50367c557..521054787 100644 --- a/tests/relaunch/whirlpool.ts +++ b/tests/relaunch/whirlpool.ts @@ -11,6 +11,7 @@ import { } from "@solana/web3.js"; import * as token from "@solana/spl-token"; import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; +import { BanksClient } from "solana-bankrun"; import { BN } from "bn.js"; import * as fs from "fs"; import { MAINNET_USDC, WHIRLPOOL_PROGRAM_ID } from "@metadaoproject/programs"; @@ -174,6 +175,38 @@ export type WhirlpoolFixture = { tickArrayStarts: number[]; }; +// Returns the fixture for an already-initialized whirlpool, or builds it via +// setupWhirlpool. The pool PDA is fixed, so whichever suite runs first +// creates it and everyone after reuses it. +export async function ensureWhirlpool({ + provider, + payer, + banksClient, +}: { + provider: anchor.AnchorProvider; + payer: Signer; + banksClient: BanksClient; +}): Promise { + const program = whirlpoolProgram(provider); + const whirlpool = getWhirlpoolAddr(); + + const existing = await banksClient.getAccount(whirlpool); + if (!existing) { + return setupWhirlpool({ provider, payer }); + } + + const pool = await program.account.whirlpool.fetch(whirlpool); + return { + program, + config: WHIRLPOOLS_CONFIG, + whirlpool, + oracle: getOracleAddr(whirlpool), + tokenVaultA: pool.tokenVaultA as PublicKey, + tokenVaultB: pool.tokenVaultB as PublicKey, + tickArrayStarts: [], + }; +} + // Builds a real WSOL/USDC whirlpool through the program's own instructions // under the dumped mainnet config: pool + tick arrays + a full-range // position. The payer funds both sides (WSOL is wrapped here; USDC must From 8b83d38b47ec68fada4be96ef738942b9b1f46a5 Mon Sep 17 00:00:00 2001 From: Pileks Date: Mon, 10 Aug 2026 16:14:40 -0700 Subject: [PATCH 16/31] deposit_via_buy ix, sdk, tests --- programs/relaunch/src/events.rs | 15 + .../src/instructions/deposit_via_buy.rs | 322 +++++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + programs/relaunch/src/pump_amm.rs | 165 ++++- sdk/src/relaunch/v0.1/RelaunchClient.ts | 237 +++++++ sdk/src/relaunch/v0.1/pumpAmm.ts | 13 + sdk/src/relaunch/v0.1/types/relaunch.ts | 514 ++++++++++++++ tasks.md | 11 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/depositViaBuy.test.ts | 653 ++++++++++++++++++ 11 files changed, 1928 insertions(+), 11 deletions(-) create mode 100644 programs/relaunch/src/instructions/deposit_via_buy.rs create mode 100644 tests/relaunch/unit/depositViaBuy.test.ts diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index ed117c022..ed09c22d7 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -102,3 +102,18 @@ pub struct RefundClaimedEvent { pub amount_refunded: u64, pub deposit_record_seq_num: u64, } + +#[event] +pub struct TokensDepositedViaBuyEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub depositor: Pubkey, + pub deposit_record: Pubkey, + /// The old tokens bought and credited (the measured old-vault delta). + pub amount: u64, + /// The quote consumed by the buy, inclusive of pump's fees. + pub quote_spent: u64, + pub total_deposited: u64, + pub total_deposited_by_depositor: u64, + pub deposit_record_seq_num: u64, +} diff --git a/programs/relaunch/src/instructions/deposit_via_buy.rs b/programs/relaunch/src/instructions/deposit_via_buy.rs new file mode 100644 index 000000000..4b7ec5996 --- /dev/null +++ b/programs/relaunch/src/instructions/deposit_via_buy.rs @@ -0,0 +1,322 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::token::{self, Mint, Token, TokenAccount}; +use anchor_spl::token_interface; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, TokensDepositedViaBuyEvent}; +use crate::pump_amm; +use crate::state::{DepositRecord, Relaunch, RelaunchState}; +use crate::{ + pump_amm_event_authority, pump_amm_fee_config, pump_amm_global_config, pump_amm_program, + pump_fees_program, +}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct DepositViaBuyArgs { + /// The exact amount of old tokens to buy off the source pool (pump's buy + /// is exact-output). + pub base_out: u64, + /// The depositor's live slippage cap on the quote spent, inclusive of + /// pump's fees. + pub max_quote_in: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct DepositViaBuy<'info> { + #[account( + mut, + has_one = old_mint, + has_one = source_quote_mint, + has_one = source_pool, + has_one = relaunch_signer, + has_one = old_token_vault, + has_one = source_quote_vault, + )] + pub relaunch: Box>, + + #[account( + init_if_needed, + payer = payer, + space = 8 + DepositRecord::INIT_SPACE, + seeds = [b"deposit_record", relaunch.key().as_ref(), depositor.key().as_ref()], + bump + )] + pub deposit_record: Box>, + + pub depositor: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: the vault authority that signs the buy; pump_amm requires the + /// user account writable. + #[account(mut)] + pub relaunch_signer: UncheckedAccount<'info>, + + #[account(mint::token_program = base_token_program)] + pub old_mint: Box>, + + pub source_quote_mint: Box>, + + #[account(mut)] + pub old_token_vault: Box>, + + #[account(mut)] + pub source_quote_vault: Box>, + + // token::* over associated_token::* deliberately: the depositor may fund + // the buy from any quote account they own (e.g. an ephemeral WSOL + // account); the refund returns to the same account. + #[account( + mut, + token::mint = source_quote_mint, + token::authority = depositor, + )] + pub depositor_quote_account: Box>, + + /// CHECK: fingerprint-validated at init and pinned by has_one; pump_amm + /// rechecks its internal consistency. + #[account(mut)] + pub source_pool: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_global_config::id())] + pub pump_global_config: UncheckedAccount<'info>, + + /// CHECK: pump_amm requires membership in its global config + pub protocol_fee_recipient: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this ATA + #[account(mut)] + pub protocol_fee_recipient_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this against the pool's stored field + #[account(mut)] + pub pool_base_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this against the pool's stored field + #[account(mut)] + pub pool_quote_token_account: UncheckedAccount<'info>, + + /// CHECK: pump_amm derives this from the pool's coin_creator + #[account(mut)] + pub coin_creator_vault_ata: UncheckedAccount<'info>, + + /// CHECK: pump_amm derives this from the pool's coin_creator + pub coin_creator_vault_authority: UncheckedAccount<'info>, + + /// CHECK: pump_amm address-checks this PDA + pub global_volume_accumulator: UncheckedAccount<'info>, + + /// CHECK: pump_amm address-checks this PDA; created for `relaunch_signer` + /// on the first buy + #[account(mut)] + pub user_volume_accumulator: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_fee_config::id())] + pub pump_fee_config: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_fees_program::id())] + pub pump_fee_program: UncheckedAccount<'info>, + + /// CHECK: pump_amm address-checks this PDA (it need not exist) + pub pool_v2: UncheckedAccount<'info>, + + /// CHECK: pump_amm requires membership in its global config's buyback list + pub buyback_fee_recipient: UncheckedAccount<'info>, + + /// CHECK: pump_amm checks this ATA + #[account(mut)] + pub buyback_fee_recipient_token_account: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_event_authority::id())] + pub pump_event_authority: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = pump_amm_program::id())] + pub pump_amm_program: UncheckedAccount<'info>, + + pub base_token_program: Interface<'info, token_interface::TokenInterface>, + pub quote_token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl DepositViaBuy<'_> { + pub fn validate(&self, args: &DepositViaBuyArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Live, + RelaunchError::RelaunchNotLive + ); + + let clock = Clock::get()?; + require_gt!( + self.relaunch.unix_timestamp_started.unwrap() + + self.relaunch.seconds_for_deposits as i64, + clock.unix_timestamp, + RelaunchError::DepositWindowClosed + ); + + require_gt!(args.base_out, 0, RelaunchError::InvalidAmount); + require_gt!(args.max_quote_in, 0, RelaunchError::InvalidAmount); + + require_gte!( + self.depositor_quote_account.amount, + args.max_quote_in, + RelaunchError::InsufficientFunds + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: DepositViaBuyArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let old_before = ctx.accounts.old_token_vault.amount; + let quote_vault_before = ctx.accounts.source_quote_vault.amount; + + token::transfer_checked( + CpiContext::new( + ctx.accounts.quote_token_program.to_account_info(), + token::TransferChecked { + from: ctx.accounts.depositor_quote_account.to_account_info(), + mint: ctx.accounts.source_quote_mint.to_account_info(), + to: ctx.accounts.source_quote_vault.to_account_info(), + authority: ctx.accounts.depositor.to_account_info(), + }, + ), + args.max_quote_in, + ctx.accounts.source_quote_mint.decimals, + )?; + + if ctx.accounts.user_volume_accumulator.data_is_empty() { + pump_amm::init_user_volume_accumulator(pump_amm::InitUserVolumeAccumulator { + payer: ctx.accounts.payer.to_account_info(), + user: ctx.accounts.relaunch_signer.to_account_info(), + user_volume_accumulator: ctx.accounts.user_volume_accumulator.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + event_authority: ctx.accounts.pump_event_authority.to_account_info(), + program: ctx.accounts.pump_amm_program.to_account_info(), + })?; + } + + pump_amm::buy( + pump_amm::Buy { + pool: ctx.accounts.source_pool.to_account_info(), + user: ctx.accounts.relaunch_signer.to_account_info(), + global_config: ctx.accounts.pump_global_config.to_account_info(), + base_mint: ctx.accounts.old_mint.to_account_info(), + quote_mint: ctx.accounts.source_quote_mint.to_account_info(), + user_base_token_account: ctx.accounts.old_token_vault.to_account_info(), + user_quote_token_account: ctx.accounts.source_quote_vault.to_account_info(), + pool_base_token_account: ctx.accounts.pool_base_token_account.to_account_info(), + pool_quote_token_account: ctx.accounts.pool_quote_token_account.to_account_info(), + protocol_fee_recipient: ctx.accounts.protocol_fee_recipient.to_account_info(), + protocol_fee_recipient_token_account: ctx + .accounts + .protocol_fee_recipient_token_account + .to_account_info(), + base_token_program: ctx.accounts.base_token_program.to_account_info(), + quote_token_program: ctx.accounts.quote_token_program.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + associated_token_program: ctx.accounts.associated_token_program.to_account_info(), + event_authority: ctx.accounts.pump_event_authority.to_account_info(), + program: ctx.accounts.pump_amm_program.to_account_info(), + coin_creator_vault_ata: ctx.accounts.coin_creator_vault_ata.to_account_info(), + coin_creator_vault_authority: ctx + .accounts + .coin_creator_vault_authority + .to_account_info(), + global_volume_accumulator: ctx.accounts.global_volume_accumulator.to_account_info(), + user_volume_accumulator: ctx.accounts.user_volume_accumulator.to_account_info(), + fee_config: ctx.accounts.pump_fee_config.to_account_info(), + fee_program: ctx.accounts.pump_fee_program.to_account_info(), + pool_v2: ctx.accounts.pool_v2.to_account_info(), + buyback_fee_recipient: ctx.accounts.buyback_fee_recipient.to_account_info(), + buyback_fee_recipient_token_account: ctx + .accounts + .buyback_fee_recipient_token_account + .to_account_info(), + }, + args.base_out, + args.max_quote_in, + // Volume accumulators feed pump's user-incentive rewards, which + // the relaunch_signer PDA could never claim — don't track. + false, + signer, + )?; + + ctx.accounts.old_token_vault.reload()?; + ctx.accounts.source_quote_vault.reload()?; + + let tokens_bought = ctx.accounts.old_token_vault.amount - old_before; + let quote_refund = ctx.accounts.source_quote_vault.amount - quote_vault_before; + let quote_spent = args.max_quote_in - quote_refund; + + if quote_refund > 0 { + token::transfer_checked( + CpiContext::new_with_signer( + ctx.accounts.quote_token_program.to_account_info(), + token::TransferChecked { + from: ctx.accounts.source_quote_vault.to_account_info(), + mint: ctx.accounts.source_quote_mint.to_account_info(), + to: ctx.accounts.depositor_quote_account.to_account_info(), + authority: ctx.accounts.relaunch_signer.to_account_info(), + }, + signer, + ), + quote_refund, + ctx.accounts.source_quote_mint.decimals, + )?; + } + + let deposit_record = &mut ctx.accounts.deposit_record; + + // A fresh record holds the default pubkey, an existing one + // holds the depositor's pubkey. + if deposit_record.depositor == ctx.accounts.depositor.key() { + deposit_record.amount_deposited += tokens_bought; + deposit_record.seq_num += 1; + } else { + deposit_record.set_inner(DepositRecord { + relaunch: relaunch_key, + depositor: ctx.accounts.depositor.key(), + amount_deposited: tokens_bought, + claimed: false, + seq_num: 0, + pda_bump: ctx.bumps.deposit_record, + }); + } + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.total_deposited += tokens_bought; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(TokensDepositedViaBuyEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + depositor: ctx.accounts.depositor.key(), + deposit_record: ctx.accounts.deposit_record.key(), + amount: tokens_bought, + quote_spent, + total_deposited: ctx.accounts.relaunch.total_deposited, + total_deposited_by_depositor: ctx.accounts.deposit_record.amount_deposited, + deposit_record_seq_num: ctx.accounts.deposit_record.seq_num, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 4be39068e..742b36eac 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,6 +1,7 @@ pub mod claim_refund; pub mod close_deposits; pub mod deposit; +pub mod deposit_via_buy; pub mod execute_sell; pub mod execute_usdc_swap; pub mod initialize_relaunch; @@ -10,6 +11,7 @@ pub mod start_deposits; pub use claim_refund::*; pub use close_deposits::*; pub use deposit::*; +pub use deposit_via_buy::*; pub use execute_sell::*; pub use execute_usdc_swap::*; pub use initialize_relaunch::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 9706beebb..7ed798fc4 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -52,6 +52,11 @@ pub mod relaunch { Deposit::handle(ctx, args) } + #[access_control(ctx.accounts.validate(&args))] + pub fn deposit_via_buy(ctx: Context, args: DepositViaBuyArgs) -> Result<()> { + DepositViaBuy::handle(ctx, args) + } + #[access_control(ctx.accounts.validate())] pub fn close_deposits(ctx: Context) -> Result<()> { CloseDeposits::handle(ctx) diff --git a/programs/relaunch/src/pump_amm.rs b/programs/relaunch/src/pump_amm.rs index 5f0941b92..686369314 100644 --- a/programs/relaunch/src/pump_amm.rs +++ b/programs/relaunch/src/pump_amm.rs @@ -6,7 +6,7 @@ //! mainnet swaps 2026-08-04). use anchor_lang::prelude::*; use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; -use anchor_lang::solana_program::program::invoke_signed; +use anchor_lang::solana_program::program::{invoke, invoke_signed}; use crate::error::RelaunchError; use crate::pump_amm_program; @@ -15,6 +15,11 @@ pub const POOL_DISCRIMINATOR: [u8; 8] = [241, 154, 109, 4, 17, 177, 109, 188]; pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173]; +pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234]; + +pub const INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR: [u8; 8] = + [94, 6, 202, 115, 255, 96, 232, 183]; + /// The prefix of pump_amm's `Pool` account that canonicality validation /// reads; trailing fields are ignored. #[derive(AnchorDeserialize)] @@ -143,3 +148,161 @@ pub fn sell( Ok(()) } + +/// The accounts of pump_amm's `buy`, in instruction order. +pub struct Buy<'info> { + pub pool: AccountInfo<'info>, + pub user: AccountInfo<'info>, + pub global_config: AccountInfo<'info>, + pub base_mint: AccountInfo<'info>, + pub quote_mint: AccountInfo<'info>, + pub user_base_token_account: AccountInfo<'info>, + pub user_quote_token_account: AccountInfo<'info>, + pub pool_base_token_account: AccountInfo<'info>, + pub pool_quote_token_account: AccountInfo<'info>, + pub protocol_fee_recipient: AccountInfo<'info>, + pub protocol_fee_recipient_token_account: AccountInfo<'info>, + pub base_token_program: AccountInfo<'info>, + pub quote_token_program: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, + pub associated_token_program: AccountInfo<'info>, + pub event_authority: AccountInfo<'info>, + pub program: AccountInfo<'info>, + pub coin_creator_vault_ata: AccountInfo<'info>, + pub coin_creator_vault_authority: AccountInfo<'info>, + pub global_volume_accumulator: AccountInfo<'info>, + pub user_volume_accumulator: AccountInfo<'info>, + pub fee_config: AccountInfo<'info>, + pub fee_program: AccountInfo<'info>, + // remaining-accounts tail + pub pool_v2: AccountInfo<'info>, + pub buyback_fee_recipient: AccountInfo<'info>, + pub buyback_fee_recipient_token_account: AccountInfo<'info>, +} + +pub fn buy( + accounts: Buy, + base_amount_out: u64, + max_quote_amount_in: u64, + track_volume: bool, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + let mut data = Vec::with_capacity(25); + data.extend_from_slice(&BUY_DISCRIMINATOR); + data.extend_from_slice(&base_amount_out.to_le_bytes()); + data.extend_from_slice(&max_quote_amount_in.to_le_bytes()); + data.push(track_volume as u8); + + let metas = vec![ + AccountMeta::new(accounts.pool.key(), false), + AccountMeta::new(accounts.user.key(), true), + AccountMeta::new_readonly(accounts.global_config.key(), false), + AccountMeta::new_readonly(accounts.base_mint.key(), false), + AccountMeta::new_readonly(accounts.quote_mint.key(), false), + AccountMeta::new(accounts.user_base_token_account.key(), false), + AccountMeta::new(accounts.user_quote_token_account.key(), false), + AccountMeta::new(accounts.pool_base_token_account.key(), false), + AccountMeta::new(accounts.pool_quote_token_account.key(), false), + AccountMeta::new_readonly(accounts.protocol_fee_recipient.key(), false), + AccountMeta::new(accounts.protocol_fee_recipient_token_account.key(), false), + AccountMeta::new_readonly(accounts.base_token_program.key(), false), + AccountMeta::new_readonly(accounts.quote_token_program.key(), false), + AccountMeta::new_readonly(accounts.system_program.key(), false), + AccountMeta::new_readonly(accounts.associated_token_program.key(), false), + AccountMeta::new_readonly(accounts.event_authority.key(), false), + AccountMeta::new_readonly(accounts.program.key(), false), + AccountMeta::new(accounts.coin_creator_vault_ata.key(), false), + AccountMeta::new_readonly(accounts.coin_creator_vault_authority.key(), false), + AccountMeta::new_readonly(accounts.global_volume_accumulator.key(), false), + AccountMeta::new(accounts.user_volume_accumulator.key(), false), + AccountMeta::new_readonly(accounts.fee_config.key(), false), + AccountMeta::new_readonly(accounts.fee_program.key(), false), + AccountMeta::new_readonly(accounts.pool_v2.key(), false), + AccountMeta::new_readonly(accounts.buyback_fee_recipient.key(), false), + AccountMeta::new(accounts.buyback_fee_recipient_token_account.key(), false), + ]; + + let account_infos = [ + accounts.pool, + accounts.user, + accounts.global_config, + accounts.base_mint, + accounts.quote_mint, + accounts.user_base_token_account, + accounts.user_quote_token_account, + accounts.pool_base_token_account, + accounts.pool_quote_token_account, + accounts.protocol_fee_recipient, + accounts.protocol_fee_recipient_token_account, + accounts.base_token_program, + accounts.quote_token_program, + accounts.system_program, + accounts.associated_token_program, + accounts.event_authority, + accounts.program, + accounts.coin_creator_vault_ata, + accounts.coin_creator_vault_authority, + accounts.global_volume_accumulator, + accounts.user_volume_accumulator, + accounts.fee_config, + accounts.fee_program, + accounts.pool_v2, + accounts.buyback_fee_recipient, + accounts.buyback_fee_recipient_token_account, + ]; + + invoke_signed( + &Instruction { + program_id: pump_amm_program::id(), + accounts: metas, + data, + }, + &account_infos, + signer_seeds, + )?; + + Ok(()) +} + +/// The accounts of pump_amm's `init_user_volume_accumulator`, in instruction +/// order. Buys require the user's volume accumulator PDA to exist; this +/// creates it (rent paid by `payer`). +pub struct InitUserVolumeAccumulator<'info> { + pub payer: AccountInfo<'info>, + pub user: AccountInfo<'info>, + pub user_volume_accumulator: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, + pub event_authority: AccountInfo<'info>, + pub program: AccountInfo<'info>, +} + +pub fn init_user_volume_accumulator(accounts: InitUserVolumeAccumulator) -> Result<()> { + let metas = vec![ + AccountMeta::new(accounts.payer.key(), true), + AccountMeta::new_readonly(accounts.user.key(), false), + AccountMeta::new(accounts.user_volume_accumulator.key(), false), + AccountMeta::new_readonly(accounts.system_program.key(), false), + AccountMeta::new_readonly(accounts.event_authority.key(), false), + AccountMeta::new_readonly(accounts.program.key(), false), + ]; + + let account_infos = [ + accounts.payer, + accounts.user, + accounts.user_volume_accumulator, + accounts.system_program, + accounts.event_authority, + accounts.program, + ]; + + invoke( + &Instruction { + program_id: pump_amm_program::id(), + accounts: metas, + data: INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR.to_vec(), + }, + &account_infos, + )?; + + Ok(()) +} diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 7b26ee0f0..a66d1fadb 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -5,12 +5,15 @@ import { Keypair, PublicKey, SystemProgram, + Transaction, TransactionSignature, } from "@solana/web3.js"; import { AccountLayout, ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotentInstruction, createInitializeMint2Instruction, + createSyncNativeInstruction, getAssociatedTokenAddressSync, MINT_SIZE, NATIVE_MINT, @@ -39,11 +42,13 @@ import { import { getPumpCreatorVaultAuthorityAddr, getPumpPoolV2Addr, + getPumpUserVolumeAccumulatorAddr, parsePumpGlobalConfig, parsePumpPool, PUMP_AMM_EVENT_AUTHORITY, PUMP_AMM_FEE_CONFIG, PUMP_AMM_GLOBAL_CONFIG, + PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, } from "./pumpAmm.js"; import { getWhirlpoolOracleAddr, @@ -255,6 +260,116 @@ export class RelaunchClient { }); } + depositViaBuyIx({ + relaunch, + oldMint, + oldTokenProgram, + sourceQuoteMint, + sourcePool, + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreator, + protocolFeeRecipient, + buybackFeeRecipient, + baseOut, + maxQuoteIn, + depositor = this.provider.publicKey, + payer = this.provider.publicKey, + depositorQuoteAccount = getAssociatedTokenAddressSync( + sourceQuoteMint, + depositor, + ), + }: { + relaunch: PublicKey; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + sourceQuoteMint: PublicKey; + sourcePool: PublicKey; + poolBaseTokenAccount: PublicKey; + poolQuoteTokenAccount: PublicKey; + coinCreator: PublicKey; + protocolFeeRecipient: PublicKey; + buybackFeeRecipient: PublicKey; + baseOut: BN; + maxQuoteIn: BN; + depositor?: PublicKey; + payer?: PublicKey; + depositorQuoteAccount?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + oldTokenProgram, + ); + const sourceQuoteVault = getAssociatedTokenAddressSync( + sourceQuoteMint, + relaunchSigner, + true, + ); + const coinCreatorVaultAuthority = + getPumpCreatorVaultAuthorityAddr(coinCreator); + + return this.relaunchProgram.methods + .depositViaBuy({ baseOut, maxQuoteIn }) + .accounts({ + relaunch, + depositRecord, + depositor, + payer, + relaunchSigner, + oldMint, + sourceQuoteMint, + oldTokenVault, + sourceQuoteVault, + depositorQuoteAccount, + sourcePool, + pumpGlobalConfig: PUMP_AMM_GLOBAL_CONFIG, + protocolFeeRecipient, + protocolFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + protocolFeeRecipient, + true, + ), + poolBaseTokenAccount, + poolQuoteTokenAccount, + coinCreatorVaultAta: getAssociatedTokenAddressSync( + sourceQuoteMint, + coinCreatorVaultAuthority, + true, + ), + coinCreatorVaultAuthority, + globalVolumeAccumulator: PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, + userVolumeAccumulator: getPumpUserVolumeAccumulatorAddr(relaunchSigner), + pumpFeeConfig: PUMP_AMM_FEE_CONFIG, + pumpFeeProgram: PUMP_FEES_PROGRAM_ID, + poolV2: getPumpPoolV2Addr(oldMint), + buybackFeeRecipient, + buybackFeeRecipientTokenAccount: getAssociatedTokenAddressSync( + sourceQuoteMint, + buybackFeeRecipient, + true, + ), + pumpEventAuthority: PUMP_AMM_EVENT_AUTHORITY, + pumpAmmProgram: PUMP_AMM_PROGRAM_ID, + baseTokenProgram: oldTokenProgram, + quoteTokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .preInstructions([ + // The buy does the sell's work plus volume-accumulator handling and + // the pull/refund transfers; give it fixed headroom over 200k. + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ]); + } + executeSellIx({ relaunch, oldMint, @@ -530,6 +645,128 @@ export class RelaunchClient { }).rpc(); } + // Buys baseOut old tokens off the source pool as the provider wallet and + // credits them as a deposit, deriving the pump account set like + // executeSell. When maxQuoteIn is not given, it is computed live from the + // pool reserves: the constant-product input for the exact output plus + // slippageBps (which must also cover pump's swap fees). For WSOL-quoted + // pools, any shortfall in the depositor's WSOL ATA is wrapped from SOL in + // a separate preparatory transaction — the buy transaction sits within a + // few bytes of the 1232-byte legacy size limit and cannot absorb the wrap + // instructions for Token-2022 old mints. + async depositViaBuy({ + relaunch, + baseOut, + maxQuoteIn, + slippageBps = 100, + }: { + relaunch: PublicKey; + baseOut: BN; + maxQuoteIn?: BN; + slippageBps?: number; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + const oldMintAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.oldMint, + ); + if (oldMintAccount === null) { + throw new Error( + `old mint ${storedRelaunch.oldMint.toBase58()} does not exist`, + ); + } + + const poolAccount = await this.provider.connection.getAccountInfo( + storedRelaunch.sourcePool, + ); + if (poolAccount === null) { + throw new Error( + `source pool ${storedRelaunch.sourcePool.toBase58()} does not exist`, + ); + } + const pool = parsePumpPool(poolAccount.data); + + const globalConfigAccount = await this.provider.connection.getAccountInfo( + PUMP_AMM_GLOBAL_CONFIG, + ); + if (globalConfigAccount === null) { + throw new Error("pump_amm global config does not exist"); + } + const globalConfig = parsePumpGlobalConfig(globalConfigAccount.data); + + if (maxQuoteIn === undefined) { + const [baseReserve, quoteReserve] = await Promise.all( + [pool.poolBaseTokenAccount, pool.poolQuoteTokenAccount].map((address) => + this.fetchTokenBalance(address), + ), + ); + const baseOutBig = BigInt(baseOut.toString()); + if (baseOutBig >= baseReserve) { + throw new Error( + `baseOut ${baseOutBig} exceeds the pool's base reserve ${baseReserve}`, + ); + } + const grossIn = (quoteReserve * baseOutBig) / (baseReserve - baseOutBig); + const cap = (grossIn * (10_000n + BigInt(slippageBps))) / 10_000n; + maxQuoteIn = new BN(cap.toString()); + } + + if (storedRelaunch.sourceQuoteMint.equals(NATIVE_MINT)) { + const wsolAta = getAssociatedTokenAddressSync( + NATIVE_MINT, + this.provider.publicKey, + ); + let wsolAtaAccount: AccountInfo | null = null; + try { + wsolAtaAccount = await this.provider.connection.getAccountInfo(wsolAta); + } catch { + // anchor-bankrun's connection proxy throws for missing accounts + // instead of returning null. + } + const wsolBalance = + wsolAtaAccount === null + ? 0n + : AccountLayout.decode(wsolAtaAccount.data).amount; + const shortfall = BigInt(maxQuoteIn.toString()) - wsolBalance; + if (shortfall > 0n) { + await this.provider.sendAndConfirm!( + new Transaction().add( + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + wsolAta, + this.provider.publicKey, + NATIVE_MINT, + ), + SystemProgram.transfer({ + fromPubkey: this.provider.publicKey, + toPubkey: wsolAta, + lamports: Number(shortfall), + }), + createSyncNativeInstruction(wsolAta), + ), + ); + } + } + + return this.depositViaBuyIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + oldTokenProgram: oldMintAccount.owner, + sourceQuoteMint: storedRelaunch.sourceQuoteMint, + sourcePool: storedRelaunch.sourcePool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient: globalConfig.protocolFeeRecipients[0], + buybackFeeRecipient: globalConfig.buybackFeeRecipients[0], + baseOut, + maxQuoteIn, + }).rpc(); + } + // Sells the whole old-token vault as the admin (the provider wallet), // deriving the pump account set from the stored relaunch, its pool, and // pump's global config. When minQuoteOut is not given, it is computed live diff --git a/sdk/src/relaunch/v0.1/pumpAmm.ts b/sdk/src/relaunch/v0.1/pumpAmm.ts index 346b621a1..c17f62320 100644 --- a/sdk/src/relaunch/v0.1/pumpAmm.ts +++ b/sdk/src/relaunch/v0.1/pumpAmm.ts @@ -17,6 +17,19 @@ export const PUMP_AMM_FEE_CONFIG = PublicKey.findProgramAddressSync( PUMP_FEES_PROGRAM_ID, )[0]; +export const PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR = + PublicKey.findProgramAddressSync( + [Buffer.from("global_volume_accumulator")], + PUMP_AMM_PROGRAM_ID, + )[0]; + +export function getPumpUserVolumeAccumulatorAddr(user: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("user_volume_accumulator"), user.toBuffer()], + PUMP_AMM_PROGRAM_ID, + )[0]; +} + // The current pump_amm requires this PDA as the first remaining account on // buys and sells (checked by address only — the account need not exist). export function getPumpPoolV2Addr(baseMint: PublicKey): PublicKey { diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 17a5ba5b9..079cce99d 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -235,6 +235,187 @@ export type Relaunch = { }, ]; }, + { + name: "depositViaBuy"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "depositRecord"; + isMut: true; + isSigner: false; + }, + { + name: "depositor"; + isMut: false; + isSigner: true; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: true; + isSigner: false; + docs: ["user account writable."]; + }, + { + name: "oldMint"; + isMut: false; + isSigner: false; + }, + { + name: "sourceQuoteMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "depositorQuoteAccount"; + isMut: true; + isSigner: false; + }, + { + name: "sourcePool"; + isMut: true; + isSigner: false; + docs: ["rechecks its internal consistency."]; + }, + { + name: "pumpGlobalConfig"; + isMut: false; + isSigner: false; + }, + { + name: "protocolFeeRecipient"; + isMut: false; + isSigner: false; + }, + { + name: "protocolFeeRecipientTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "poolBaseTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "poolQuoteTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "coinCreatorVaultAta"; + isMut: true; + isSigner: false; + }, + { + name: "coinCreatorVaultAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "globalVolumeAccumulator"; + isMut: false; + isSigner: false; + }, + { + name: "userVolumeAccumulator"; + isMut: true; + isSigner: false; + docs: ["on the first buy"]; + }, + { + name: "pumpFeeConfig"; + isMut: false; + isSigner: false; + }, + { + name: "pumpFeeProgram"; + isMut: false; + isSigner: false; + }, + { + name: "poolV2"; + isMut: false; + isSigner: false; + }, + { + name: "buybackFeeRecipient"; + isMut: false; + isSigner: false; + }, + { + name: "buybackFeeRecipientTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "pumpEventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "pumpAmmProgram"; + isMut: false; + isSigner: false; + }, + { + name: "baseTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "quoteTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "DepositViaBuyArgs"; + }; + }, + ]; + }, { name: "closeDeposits"; accounts: [ @@ -867,6 +1048,30 @@ export type Relaunch = { ]; }; }, + { + name: "DepositViaBuyArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "baseOut"; + docs: [ + "The exact amount of old tokens to buy off the source pool (pump's buy", + "is exact-output).", + ]; + type: "u64"; + }, + { + name: "maxQuoteIn"; + docs: [ + "The depositor's live slippage cap on the quote spent, inclusive of", + "pump's fees.", + ]; + type: "u64"; + }, + ]; + }; + }, { name: "DepositArgs"; type: { @@ -1308,6 +1513,58 @@ export type Relaunch = { }, ]; }, + { + name: "TokensDepositedViaBuyEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "depositor"; + type: "publicKey"; + index: false; + }, + { + name: "depositRecord"; + type: "publicKey"; + index: false; + }, + { + name: "amount"; + type: "u64"; + index: false; + }, + { + name: "quoteSpent"; + type: "u64"; + index: false; + }, + { + name: "totalDeposited"; + type: "u64"; + index: false; + }, + { + name: "totalDepositedByDepositor"; + type: "u64"; + index: false; + }, + { + name: "depositRecordSeqNum"; + type: "u64"; + index: false; + }, + ]; + }, ]; errors: [ { @@ -1665,6 +1922,187 @@ export const IDL: Relaunch = { }, ], }, + { + name: "depositViaBuy", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "depositRecord", + isMut: true, + isSigner: false, + }, + { + name: "depositor", + isMut: false, + isSigner: true, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: true, + isSigner: false, + docs: ["user account writable."], + }, + { + name: "oldMint", + isMut: false, + isSigner: false, + }, + { + name: "sourceQuoteMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "depositorQuoteAccount", + isMut: true, + isSigner: false, + }, + { + name: "sourcePool", + isMut: true, + isSigner: false, + docs: ["rechecks its internal consistency."], + }, + { + name: "pumpGlobalConfig", + isMut: false, + isSigner: false, + }, + { + name: "protocolFeeRecipient", + isMut: false, + isSigner: false, + }, + { + name: "protocolFeeRecipientTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "poolBaseTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "poolQuoteTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "coinCreatorVaultAta", + isMut: true, + isSigner: false, + }, + { + name: "coinCreatorVaultAuthority", + isMut: false, + isSigner: false, + }, + { + name: "globalVolumeAccumulator", + isMut: false, + isSigner: false, + }, + { + name: "userVolumeAccumulator", + isMut: true, + isSigner: false, + docs: ["on the first buy"], + }, + { + name: "pumpFeeConfig", + isMut: false, + isSigner: false, + }, + { + name: "pumpFeeProgram", + isMut: false, + isSigner: false, + }, + { + name: "poolV2", + isMut: false, + isSigner: false, + }, + { + name: "buybackFeeRecipient", + isMut: false, + isSigner: false, + }, + { + name: "buybackFeeRecipientTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "pumpEventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "pumpAmmProgram", + isMut: false, + isSigner: false, + }, + { + name: "baseTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "quoteTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "DepositViaBuyArgs", + }, + }, + ], + }, { name: "closeDeposits", accounts: [ @@ -2297,6 +2735,30 @@ export const IDL: Relaunch = { ], }, }, + { + name: "DepositViaBuyArgs", + type: { + kind: "struct", + fields: [ + { + name: "baseOut", + docs: [ + "The exact amount of old tokens to buy off the source pool (pump's buy", + "is exact-output).", + ], + type: "u64", + }, + { + name: "maxQuoteIn", + docs: [ + "The depositor's live slippage cap on the quote spent, inclusive of", + "pump's fees.", + ], + type: "u64", + }, + ], + }, + }, { name: "DepositArgs", type: { @@ -2738,6 +3200,58 @@ export const IDL: Relaunch = { }, ], }, + { + name: "TokensDepositedViaBuyEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "depositor", + type: "publicKey", + index: false, + }, + { + name: "depositRecord", + type: "publicKey", + index: false, + }, + { + name: "amount", + type: "u64", + index: false, + }, + { + name: "quoteSpent", + type: "u64", + index: false, + }, + { + name: "totalDeposited", + type: "u64", + index: false, + }, + { + name: "totalDepositedByDepositor", + type: "u64", + index: false, + }, + { + name: "depositRecordSeqNum", + type: "u64", + index: false, + }, + ], + }, ], errors: [ { diff --git a/tasks.md b/tasks.md index f0fa55791..882124bfb 100644 --- a/tasks.md +++ b/tasks.md @@ -23,20 +23,11 @@ ## Tasks -### Phase 3: PumpSwap + Whirlpool legs - -> Reference: `relaunch-implementation-plan.md` → "Stage 3 — execute_sell, execute_usdc_swap, deposit_via_buy" - -- [NEXT] 3.3 `deposit_via_buy` (instruction + SDK + tests) - - `cpi/pump_amm.rs` buy builder (23 accounts = sell + volume accumulators); pull quote → buy → refund unspent → credit measured old-vault delta - - Tests per plan §4: both quote variants, mixes with direct deposits, tight `max_quote_in` fails cleanly, window/state gates, credited tokens refund on later failure - - SDK: `depositViaBuy()` + SOL→WSOL wrap pre-instructions - ### Phase 4: complete_relaunch > Reference: `relaunch-implementation-plan.md` → "Stage 4 — complete_relaunch" -- [ ] 4.1 `complete_relaunch` (instruction + SDK + tests) +- [NEXT] 4.1 `complete_relaunch` (instruction + SDK + tests) - u128 `price_1e12`; CPI `futarchy::initialize_dao` (launchpad-parity params incl. 1.5M min stake) + `provide_liquidity` (2M base, `usdc_recovered / 5` quote, Squads-vault position); transfer remaining USDC + mint & metadata authorities to Squads vault; store `dao`/`dao_vault`; `Complete` - Squads accounts pre-created/PDA-derived exactly as launchpad (reentrancy-safe shape) - Tests per plan §8: param assertions, AMM-ratio == twap identity, treasury remainder, authority handoffs, permissionless crank, rounding conservation diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index c03138e0d..a7e1e3bba 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -3,6 +3,7 @@ import venues from "./unit/venues.test.js"; import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; +import depositViaBuy from "./unit/depositViaBuy.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; import executeUsdcSwap from "./unit/executeUsdcSwap.test.js"; @@ -24,6 +25,7 @@ export default function suite() { describe("#initialize_relaunch", initializeRelaunch); describe("#start_deposits", startDeposits); describe("#deposit", deposit); + describe("#deposit_via_buy", depositViaBuy); describe("#close_deposits", closeDeposits); describe("#execute_sell", executeSell); describe("#execute_usdc_swap", executeUsdcSwap); diff --git a/tests/relaunch/unit/depositViaBuy.test.ts b/tests/relaunch/unit/depositViaBuy.test.ts new file mode 100644 index 000000000..0ae54dc45 --- /dev/null +++ b/tests/relaunch/unit/depositViaBuy.test.ts @@ -0,0 +1,653 @@ +import { PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BanksClient } from "solana-bankrun"; +import { + getDepositRecordAddr, + MAINNET_USDC, + RelaunchClient, +} from "@metadaoproject/programs"; +import { setupRelaunch } from "../utils.js"; +import { + getBuybackFeeRecipients, + getProtocolFeeRecipient, + getUserVolumeAccumulatorAddr, + writePumpPool, + PumpPool, +} from "../pumpAmm.js"; +import { wrapSol } from "../whirlpool.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const BASE_OUT = 10_000n * 10n ** 6n; // 10k old tokens + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount( + address, + { ...raw, data: Buffer.from(raw.data) } as any, + tokenProgram, + ).amount; +} + +// The constant-product input for an exact-output buy, before fees. +function grossQuoteIn(quoteReserve: bigint, baseOut: bigint): bigint { + return (quoteReserve * baseOut) / (POOL_BASE_RESERVE - baseOut); +} + +export default function suite() { + let client: RelaunchClient; + let protocolFeeRecipient: PublicKey; + let buybackFeeRecipient: PublicKey; + + before(async function () { + client = this.relaunch; + // Both recipients come from the loaded global-config fixture, so one + // fetch serves the whole suite. + protocolFeeRecipient = await getProtocolFeeRecipient(this.banksClient); + buybackFeeRecipient = (await getBuybackFeeRecipients(this.banksClient))[0]; + }); + + const setupLiveRelaunch = async function ( + this: Mocha.Context, + { + quoteMint = token.NATIVE_MINT, + oldTokenProgram = token.TOKEN_PROGRAM_ID, + start = true, + }: { + quoteMint?: PublicKey; + oldTokenProgram?: PublicKey; + start?: boolean; + } = {}, + ): Promise<{ + relaunch: PublicKey; + pool: PumpPool; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + payerOldTokenAccount: PublicKey; + }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: quoteMint.equals(token.NATIVE_MINT) + ? WSOL_POOL_QUOTE_RESERVE + : USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: quoteMint, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + + if (start) { + await client.startDepositsIx({ relaunch }).rpc(); + } + + return { ...setup, pool, relaunch }; + }; + + const depositViaBuyIx = ({ + relaunch, + pool, + oldTokenProgram, + baseOut, + maxQuoteIn, + }: { + relaunch: PublicKey; + pool: PumpPool; + oldTokenProgram: PublicKey; + baseOut: bigint; + maxQuoteIn: bigint; + }) => + client.depositViaBuyIx({ + relaunch, + oldMint: pool.baseMint, + oldTokenProgram, + sourceQuoteMint: pool.quoteMint, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient, + buybackFeeRecipient, + baseOut: new BN(baseOut.toString()), + maxQuoteIn: new BN(maxQuoteIn.toString()), + }); + + it("buys old tokens off a WSOL-quoted pool and credits them as a deposit", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + const wsolAta = await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + const maxQuoteIn = 2n * 10n ** 9n; + + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn, + }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), BASE_OUT.toString()); + assert.equal(storedRelaunch.seqNum.toString(), "2"); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.relaunch.equals(relaunch)); + assert.isTrue(record.depositor.equals(this.payer.publicKey)); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + assert.isFalse(record.claimed); + assert.equal(record.seqNum.toString(), "0"); + const [, recordBump] = getDepositRecordAddr({ + programId: client.getProgramId(), + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.pdaBump, recordBump); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + const poolBaseBalance = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ); + assert.equal( + poolBaseBalance.toString(), + (POOL_BASE_RESERVE - BASE_OUT).toString(), + ); + + // The unspent quote was refunded: the cost stays within a few percent of + // the constant-product input, and the vault keeps none of the pull. + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + const spent = wsolBefore - wsolAfter; + const gross = grossQuoteIn(WSOL_POOL_QUOTE_RESERVE, BASE_OUT); + assert.isTrue(spent >= gross && spent < (gross * 103n) / 100n); + assert.isTrue(spent < maxQuoteIn); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal(quoteVaultBalance.toString(), "0"); + + // The first buy created the relaunch signer's volume accumulator. + const userVolumeAccumulator = await this.banksClient.getAccount( + getUserVolumeAccumulatorAddr( + client.getRelaunchSignerAddress({ relaunch }), + ), + ); + assert.isNotNull(userVolumeAccumulator); + }); + + it("buys old tokens off a USDC-quoted pool and credits them as a deposit", async function () { + const { relaunch, pool, oldTokenProgram } = await setupLiveRelaunch.call( + this, + { quoteMint: MAINNET_USDC }, + ); + const usdcAta = token.getAssociatedTokenAddressSync( + MAINNET_USDC, + this.payer.publicKey, + ); + const usdcBefore = await tokenBalance(this.banksClient, usdcAta); + const maxQuoteIn = 2_000n * 10n ** 6n; // 2k USDC + + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn, + }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), BASE_OUT.toString()); + assert.equal(storedRelaunch.seqNum.toString(), "2"); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.relaunch.equals(relaunch)); + assert.isTrue(record.depositor.equals(this.payer.publicKey)); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + assert.isFalse(record.claimed); + assert.equal(record.seqNum.toString(), "0"); + const [, recordBump] = getDepositRecordAddr({ + programId: client.getProgramId(), + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.pdaBump, recordBump); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + const poolBaseBalance = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ); + assert.equal( + poolBaseBalance.toString(), + (POOL_BASE_RESERVE - BASE_OUT).toString(), + ); + + const usdcAfter = await tokenBalance(this.banksClient, usdcAta); + const spent = usdcBefore - usdcAfter; + const gross = grossQuoteIn(USDC_POOL_QUOTE_RESERVE, BASE_OUT); + assert.isTrue(spent >= gross && spent < (gross * 103n) / 100n); + assert.isTrue(spent < maxQuoteIn); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal(quoteVaultBalance.toString(), "0"); + + const userVolumeAccumulator = await this.banksClient.getAccount( + getUserVolumeAccumulatorAddr( + client.getRelaunchSignerAddress({ relaunch }), + ), + ); + assert.isNotNull(userVolumeAccumulator); + }); + + it("computes max_quote_in from pool reserves when omitted", async function () { + const { relaunch, oldTokenProgram } = await setupLiveRelaunch.call(this, { + quoteMint: MAINNET_USDC, + }); + + const usdcAta = token.getAssociatedTokenAddressSync( + MAINNET_USDC, + this.payer.publicKey, + ); + const usdcBefore = await tokenBalance(this.banksClient, usdcAta); + + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BASE_OUT.toString()), + }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const vaultBalance = await tokenBalance( + this.banksClient, + (await client.fetchRelaunch(relaunch)).oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The computed cap is the constant-product input plus the default + // 100 bps of slippage, which the actual cost stays within. + const usdcAfter = await tokenBalance(this.banksClient, usdcAta); + const spent = usdcBefore - usdcAfter; + const gross = grossQuoteIn(USDC_POOL_QUOTE_RESERVE, BASE_OUT); + const computedCap = (gross * 10_100n) / 10_000n; + assert.isTrue(spent >= gross && spent <= computedCap); + }); + + it("wraps the depositor's SOL shortfall for WSOL-quoted buys", async function () { + const { relaunch, oldTokenProgram } = await setupLiveRelaunch.call(this); + + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + this.payer.publicKey, + ); + // Whatever WSOL is left over from earlier tests, this max_quote_in + // forces a 2-SOL shortfall that the convenience method must wrap. + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + const maxQuoteIn = wsolBefore + 2n * 10n ** 9n; + + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN(maxQuoteIn.toString()), + }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const vaultBalance = await tokenBalance( + this.banksClient, + (await client.fetchRelaunch(relaunch)).oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The wrap topped the ATA up to exactly max_quote_in before the buy + // spent from it, so the remainder is max_quote_in minus the cost. + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + const spent = maxQuoteIn - wsolAfter; + const gross = grossQuoteIn(WSOL_POOL_QUOTE_RESERVE, BASE_OUT); + assert.isTrue(spent >= gross && spent < (gross * 103n) / 100n); + }); + + it("accumulates buy-deposits and direct deposits in the same record", async function () { + const { relaunch, pool, oldMint, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), // 100 tokens + }) + .rpc(); + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: 50n * 10n ** 6n, // 50 tokens + maxQuoteIn: 10n ** 9n, + }).rpc(); + // A second buy exercises the existing-accumulator and existing-record + // branches. + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: 25n * 10n ** 6n, // 25 tokens + maxQuoteIn: 10n ** 9n, + }).rpc(); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), "175000000"); + assert.equal(record.seqNum.toString(), "2"); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "175000000"); + assert.equal(storedRelaunch.seqNum.toString(), "4"); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), "175000000"); + }); + + it("buys a Token-2022 old token", async function () { + const { relaunch, pool, oldTokenProgram } = await setupLiveRelaunch.call( + this, + { oldTokenProgram: token.TOKEN_2022_PROGRAM_ID }, + ); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: 2n * 10n ** 9n, + }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), BASE_OUT.toString()); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + }); + + it("fails when max_quote_in is too tight, crediting nothing", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + this.payer.publicKey, + ); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const gross = grossQuoteIn(WSOL_POOL_QUOTE_RESERVE, BASE_OUT); + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: (gross * 90n) / 100n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) {} + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + + const depositRecord = client.getDepositRecordAddress({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isNull(await this.banksClient.getAccount(depositRecord)); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), "0"); + + // The whole transaction reverted, including the quote pull. + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + assert.equal(wsolAfter.toString(), wsolBefore.toString()); + }); + + it("fails to buy zero tokens", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: 0n, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InvalidAmount"); + } + }); + + it("fails with a zero max_quote_in", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: 0n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InvalidAmount"); + } + }); + + it("fails when the depositor's quote balance is insufficient", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + const wsolBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + this.payer.publicKey, + ), + ); + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: wsolBalance + 1n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InsufficientFunds"); + } + }); + + it("fails before deposits start", async function () { + const { relaunch, pool, oldTokenProgram } = await setupLiveRelaunch.call( + this, + { start: false }, + ); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotLive"); + } + }); + + it("fails after the deposit window closes", async function () { + const { relaunch, pool, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + await this.advanceBySeconds(ONE_WEEK); + + try { + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "DepositWindowClosed"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + }); + + it("refunds buy-credited tokens as old tokens after the relaunch fails", async function () { + const { relaunch, pool, oldTokenProgram, payerOldTokenAccount } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + await depositViaBuyIx({ + relaunch, + pool, + oldTokenProgram, + baseOut: BASE_OUT, + maxQuoteIn: 2n * 10n ** 9n, + }).rpc(); + + // 10k tokens misses the 10% threshold of the 1B supply. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + + const oldBalanceBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + oldTokenProgram, + ); + + await client.claimRefund({ relaunch }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + + // The buy is not unwound: the depositor is refunded the old tokens the + // buy acquired. + const oldBalanceAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + oldTokenProgram, + ); + assert.equal( + (oldBalanceAfter - oldBalanceBefore).toString(), + BASE_OUT.toString(), + ); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + oldTokenProgram, + ); + assert.equal(vaultBalance.toString(), "0"); + }); +} From d2c29257da7a56b93891763379d9018f936e80d2 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 11 Aug 2026 11:36:56 -0700 Subject: [PATCH 17/31] complete_relaunch ix, sdk, tests --- programs/relaunch/src/constants.rs | 2 + programs/relaunch/src/error.rs | 2 + programs/relaunch/src/events.rs | 12 + .../src/instructions/complete_relaunch.rs | 349 ++++++++++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 108 +++++ sdk/src/relaunch/v0.1/types/relaunch.ts | 404 ++++++++++++++++ tasks.md | 11 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/completeRelaunch.test.ts | 439 ++++++++++++++++++ 11 files changed, 1326 insertions(+), 10 deletions(-) create mode 100644 programs/relaunch/src/instructions/complete_relaunch.rs create mode 100644 tests/relaunch/unit/completeRelaunch.test.ts diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index d9faa3f64..e21587846 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -6,6 +6,8 @@ pub const PRICE_SCALE: u128 = 1_000_000_000_000; pub const TOKENS_TO_DEPOSITORS: u64 = 10_000_000 * TOKEN_SCALE; /// 2M tokens with 6 decimals, paired with recovered USDC in the futarchy AMM. pub const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 2_000_000 * TOKEN_SCALE; +/// 1.5M tokens with 6 decimals, staked to create a proposal (launchpad's value). +pub const PROPOSAL_MIN_STAKE_TOKENS: u64 = 1_500_000 * TOKEN_SCALE; /// 1 year. pub const MAX_SECONDS_FOR_DEPOSITS: u32 = 60 * 60 * 24 * 365; diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index a82336e19..833a691a5 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -48,4 +48,6 @@ pub enum RelaunchError { RelaunchNotSold, #[msg("Swap output is below the minimum output amount")] SlippageExceeded, + #[msg("Relaunch must be in the Swapped state")] + RelaunchNotSwapped, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index ed09c22d7..48fb2408e 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -103,6 +103,18 @@ pub struct RefundClaimedEvent { pub deposit_record_seq_num: u64, } +#[event] +pub struct RelaunchCompletedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub dao: Pubkey, + pub dao_vault: Pubkey, + pub usdc_recovered: u64, + pub twap_initial_observation: u128, + pub usdc_to_lp: u64, + pub usdc_to_treasury: u64, +} + #[event] pub struct TokensDepositedViaBuyEvent { pub common: CommonFields, diff --git a/programs/relaunch/src/instructions/complete_relaunch.rs b/programs/relaunch/src/instructions/complete_relaunch.rs new file mode 100644 index 000000000..a7a9ae0f6 --- /dev/null +++ b/programs/relaunch/src/instructions/complete_relaunch.rs @@ -0,0 +1,349 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::{get_associated_token_address, AssociatedToken}; +use anchor_spl::metadata::{ + mpl_token_metadata::ID as MPL_TOKEN_METADATA_PROGRAM_ID, update_metadata_accounts_v2, Metadata, + UpdateMetadataAccountsV2, +}; +use anchor_spl::token::{ + self, spl_token::instruction::AuthorityType, Mint, SetAuthority, Token, TokenAccount, Transfer, +}; + +use futarchy::program::Futarchy; +use futarchy::{ + InitialSpendingLimit, InitializeDaoParams, ProvideLiquidityParams, SEED_AMM_POSITION, SEED_DAO, +}; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, RelaunchCompletedEvent}; +use crate::state::{Relaunch, RelaunchState}; +use crate::{ + usdc_mint, PRICE_SCALE, PROPOSAL_MIN_STAKE_TOKENS, TOKENS_TO_DEPOSITORS, + TOKENS_TO_FUTARCHY_LIQUIDITY, +}; + +#[event_cpi] +#[derive(Accounts)] +pub struct CompleteRelaunch<'info> { + #[account( + mut, + has_one = relaunch_signer, + has_one = new_mint, + has_one = new_token_vault, + has_one = usdc_vault, + )] + pub relaunch: Box>, + + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: the vault authority; signs the futarchy CPIs and the authority + /// handoffs. + pub relaunch_signer: UncheckedAccount<'info>, + + /// The DAO's base mint; its mint authority moves to the Squads vault. + #[account(mut)] + pub new_mint: Box>, + + /// The DAO's quote mint. + #[account(address = usdc_mint::id())] + pub usdc_mint: Box>, + + #[account(mut)] + pub new_token_vault: Box>, + + #[account(mut)] + pub usdc_vault: Box>, + + /// CHECK: the new token's metadata; its update authority moves to the + /// Squads vault. + #[account( + mut, + seeds = [b"metadata", MPL_TOKEN_METADATA_PROGRAM_ID.as_ref(), new_mint.key().as_ref()], + seeds::program = MPL_TOKEN_METADATA_PROGRAM_ID, + bump + )] + pub token_metadata: UncheckedAccount<'info>, + + /// CHECK: initialized by the futarchy program; the nonce seed matches the + /// hardcoded `nonce: 0` CPI param. + #[account( + mut, + seeds = [SEED_DAO, relaunch_signer.key().as_ref(), 0_u64.to_le_bytes().as_ref()], + bump, + seeds::program = futarchy_program, + )] + pub dao: UncheckedAccount<'info>, + + /// CHECK: initialized by the futarchy program; the DAO's AMM base ATA. + #[account(mut, address = get_associated_token_address(&dao.key(), &new_mint.key()))] + pub futarchy_amm_base_vault: UncheckedAccount<'info>, + + /// CHECK: initialized by the futarchy program; the DAO's AMM quote ATA. + #[account(mut, address = get_associated_token_address(&dao.key(), &usdc_mint.key()))] + pub futarchy_amm_quote_vault: UncheckedAccount<'info>, + + /// CHECK: initialized by the futarchy program; the Squads-vault-owned LP + /// position. + #[account( + mut, + seeds = [SEED_AMM_POSITION, dao.key().as_ref(), squads_multisig_vault.key().as_ref()], + bump, + seeds::program = futarchy_program, + )] + pub amm_position: UncheckedAccount<'info>, + + /// CHECK: initialized by squads via the futarchy CPI. + #[account( + mut, + seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig_program::SEED_MULTISIG, dao.key().as_ref()], + bump, + seeds::program = squads_program, + )] + pub squads_multisig: UncheckedAccount<'info>, + + /// CHECK: the DAO treasury that receives the remaining USDC and both + /// authorities. + #[account( + seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig.key().as_ref(), squads_multisig_program::SEED_VAULT, 0_u8.to_le_bytes().as_ref()], + bump, + seeds::program = squads_program, + )] + pub squads_multisig_vault: UncheckedAccount<'info>, + + /// CHECK: initialized by squads when a spending limit is configured. + #[account( + mut, + seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig.key().as_ref(), squads_multisig_program::SEED_SPENDING_LIMIT, dao.key().as_ref()], + bump, + seeds::program = squads_program, + )] + pub spending_limit: UncheckedAccount<'info>, + + /// CHECK: checked by squads. + #[account( + seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig_program::SEED_PROGRAM_CONFIG], + bump, + seeds::program = squads_program, + )] + pub squads_program_config: UncheckedAccount<'info>, + + /// CHECK: checked by squads. + #[account(mut)] + pub squads_program_config_treasury: UncheckedAccount<'info>, + + #[account( + init_if_needed, + payer = payer, + associated_token::mint = usdc_mint, + associated_token::authority = squads_multisig_vault, + )] + pub treasury_usdc_account: Box>, + + pub futarchy_program: Program<'info, Futarchy>, + /// CHECK: the futarchy program's event-CPI authority PDA. + #[account(seeds = [b"__event_authority"], bump, seeds::program = futarchy_program)] + pub futarchy_event_authority: UncheckedAccount<'info>, + pub squads_program: Program<'info, squads_multisig_program::program::SquadsMultisigProgram>, + pub token_metadata_program: Program<'info, Metadata>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl CompleteRelaunch<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Swapped, + RelaunchError::RelaunchNotSwapped + ); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let usdc_recovered = ctx.accounts.relaunch.usdc_recovered; + let price_1e12 = (usdc_recovered as u128 * PRICE_SCALE) / (TOKENS_TO_DEPOSITORS as u128); + let usdc_to_lp = usdc_recovered / 5; + + ctx.accounts.initialize_dao(price_1e12, signer)?; + ctx.accounts + .provide_futarchy_amm_liquidity(usdc_to_lp, signer)?; + let usdc_to_treasury = ctx.accounts.send_remaining_usdc_to_dao(signer)?; + ctx.accounts.transfer_mint_authority_to_dao(signer)?; + ctx.accounts.transfer_metadata_authority_to_dao(signer)?; + + let clock = Clock::get()?; + let relaunch = &mut ctx.accounts.relaunch; + relaunch.dao = Some(ctx.accounts.dao.key()); + relaunch.dao_vault = Some(ctx.accounts.squads_multisig_vault.key()); + relaunch.state = RelaunchState::Complete; + relaunch.unix_timestamp_completed = Some(clock.unix_timestamp); + relaunch.seq_num += 1; + + emit_cpi!(RelaunchCompletedEvent { + common: CommonFields::new(&clock, relaunch.seq_num), + relaunch: relaunch_key, + dao: ctx.accounts.dao.key(), + dao_vault: ctx.accounts.squads_multisig_vault.key(), + usdc_recovered, + twap_initial_observation: price_1e12, + usdc_to_lp, + usdc_to_treasury, + }); + + Ok(()) + } + + #[inline(never)] + fn initialize_dao(&self, price_1e12: u128, signer: &[&[&[u8]]]) -> Result<()> { + // A zero/empty config means the DAO launches without a Squads + // spending limit. + let initial_spending_limit = if self.relaunch.monthly_spending_limit_amount == 0 { + None + } else { + Some(InitialSpendingLimit { + amount_per_month: self.relaunch.monthly_spending_limit_amount, + members: self.relaunch.monthly_spending_limit_members.clone(), + }) + }; + + futarchy::cpi::initialize_dao( + CpiContext::new_with_signer( + self.futarchy_program.to_account_info(), + futarchy::cpi::accounts::InitializeDao { + dao: self.dao.to_account_info(), + dao_creator: self.relaunch_signer.to_account_info(), + payer: self.payer.to_account_info(), + system_program: self.system_program.to_account_info(), + base_mint: self.new_mint.to_account_info(), + quote_mint: self.usdc_mint.to_account_info(), + event_authority: self.futarchy_event_authority.to_account_info(), + program: self.futarchy_program.to_account_info(), + squads_multisig: self.squads_multisig.to_account_info(), + squads_multisig_vault: self.squads_multisig_vault.to_account_info(), + squads_program: self.squads_program.to_account_info(), + squads_program_config: self.squads_program_config.to_account_info(), + squads_program_config_treasury: self + .squads_program_config_treasury + .to_account_info(), + spending_limit: self.spending_limit.to_account_info(), + futarchy_amm_base_vault: self.futarchy_amm_base_vault.to_account_info(), + futarchy_amm_quote_vault: self.futarchy_amm_quote_vault.to_account_info(), + associated_token_program: self.associated_token_program.to_account_info(), + token_program: self.token_program.to_account_info(), + }, + signer, + ), + InitializeDaoParams { + twap_initial_observation: price_1e12, + twap_max_observation_change_per_update: price_1e12 / 20, + twap_start_delay_seconds: 24 * 60 * 60, + min_quote_futarchic_liquidity: 1, + min_base_futarchic_liquidity: 1, + base_to_stake: PROPOSAL_MIN_STAKE_TOKENS, + pass_threshold_bps: 300, + seconds_per_proposal: 3 * 24 * 60 * 60, + nonce: 0, + initial_spending_limit, + team_sponsored_pass_threshold_bps: -300, + team_address: self.relaunch.team_address, + }, + ) + } + + #[inline(never)] + fn provide_futarchy_amm_liquidity(&self, usdc_to_lp: u64, signer: &[&[&[u8]]]) -> Result<()> { + futarchy::cpi::provide_liquidity( + CpiContext::new_with_signer( + self.futarchy_program.to_account_info(), + futarchy::cpi::accounts::ProvideLiquidity { + dao: self.dao.to_account_info(), + liquidity_provider: self.relaunch_signer.to_account_info(), + liquidity_provider_base_account: self.new_token_vault.to_account_info(), + liquidity_provider_quote_account: self.usdc_vault.to_account_info(), + payer: self.payer.to_account_info(), + system_program: self.system_program.to_account_info(), + amm_base_vault: self.futarchy_amm_base_vault.to_account_info(), + amm_quote_vault: self.futarchy_amm_quote_vault.to_account_info(), + amm_position: self.amm_position.to_account_info(), + token_program: self.token_program.to_account_info(), + program: self.futarchy_program.to_account_info(), + event_authority: self.futarchy_event_authority.to_account_info(), + }, + signer, + ), + ProvideLiquidityParams { + quote_amount: usdc_to_lp, + max_base_amount: TOKENS_TO_FUTARCHY_LIQUIDITY, + min_liquidity: 0, + position_authority: self.squads_multisig_vault.key(), + }, + ) + } + + /// Sweeps everything left in the USDC vault to the DAO treasury. + #[inline(never)] + fn send_remaining_usdc_to_dao(&mut self, signer: &[&[&[u8]]]) -> Result { + // Ensure the vault is updated before reading its amount. + self.usdc_vault.reload()?; + let usdc_to_treasury = self.usdc_vault.amount; + + token::transfer( + CpiContext::new_with_signer( + self.token_program.to_account_info(), + Transfer { + from: self.usdc_vault.to_account_info(), + to: self.treasury_usdc_account.to_account_info(), + authority: self.relaunch_signer.to_account_info(), + }, + signer, + ), + usdc_to_treasury, + )?; + + Ok(usdc_to_treasury) + } + + #[inline(never)] + fn transfer_mint_authority_to_dao(&self, signer: &[&[&[u8]]]) -> Result<()> { + token::set_authority( + CpiContext::new_with_signer( + self.token_program.to_account_info(), + SetAuthority { + account_or_mint: self.new_mint.to_account_info(), + current_authority: self.relaunch_signer.to_account_info(), + }, + signer, + ), + AuthorityType::MintTokens, + Some(self.squads_multisig_vault.key()), + ) + } + + #[inline(never)] + fn transfer_metadata_authority_to_dao(&self, signer: &[&[&[u8]]]) -> Result<()> { + update_metadata_accounts_v2( + CpiContext::new_with_signer( + self.token_metadata_program.to_account_info(), + UpdateMetadataAccountsV2 { + metadata: self.token_metadata.to_account_info(), + update_authority: self.relaunch_signer.to_account_info(), + }, + signer, + ), + Some(self.squads_multisig_vault.key()), + None, + None, + None, + ) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 742b36eac..8a673a4c1 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,5 +1,6 @@ pub mod claim_refund; pub mod close_deposits; +pub mod complete_relaunch; pub mod deposit; pub mod deposit_via_buy; pub mod execute_sell; @@ -10,6 +11,7 @@ pub mod start_deposits; pub use claim_refund::*; pub use close_deposits::*; +pub use complete_relaunch::*; pub use deposit::*; pub use deposit_via_buy::*; pub use execute_sell::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 7ed798fc4..703f612a4 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -75,6 +75,11 @@ pub mod relaunch { ExecuteUsdcSwap::handle(ctx, args) } + #[access_control(ctx.accounts.validate())] + pub fn complete_relaunch(ctx: Context) -> Result<()> { + CompleteRelaunch::handle(ctx) + } + #[access_control(ctx.accounts.validate())] pub fn mark_failed(ctx: Context) -> Result<()> { MarkFailed::handle(ctx) diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index a66d1fadb..fa953bd74 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -20,14 +20,20 @@ import { TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import BN from "bn.js"; +import * as multisig from "@sqds/multisig"; import { + FUTARCHY_V0_6_PROGRAM_ID, MAINNET_USDC, MPL_TOKEN_METADATA_PROGRAM_ID, PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID, RELAUNCH_V0_1_PROGRAM_ID, + SQUADS_PROGRAM_CONFIG, + SQUADS_PROGRAM_CONFIG_TREASURY, + SQUADS_PROGRAM_ID, WHIRLPOOL_PROGRAM_ID, } from "../../constants.js"; +import { getDaoAddr } from "../../futarchy/v0.6/index.js"; import { RelaunchProgram, RelaunchIDL, @@ -566,6 +572,108 @@ export class RelaunchClient { }).rpc(); } + completeRelaunchIx({ + relaunch, + newMint, + payer = this.provider.publicKey, + }: { + relaunch: PublicKey; + newMint: PublicKey; + payer?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + + const newTokenVault = getAssociatedTokenAddressSync( + newMint, + relaunchSigner, + true, + ); + const usdcVault = getAssociatedTokenAddressSync( + MAINNET_USDC, + relaunchSigner, + true, + ); + const [tokenMetadata] = getMetadataAddr(newMint); + + const [dao] = getDaoAddr({ nonce: new BN(0), daoCreator: relaunchSigner }); + const [futarchyEventAuthority] = getEventAuthorityAddr( + FUTARCHY_V0_6_PROGRAM_ID, + ); + + const [multisigPda] = multisig.getMultisigPda({ createKey: dao }); + const [multisigVault] = multisig.getVaultPda({ multisigPda, index: 0 }); + const [spendingLimit] = multisig.getSpendingLimitPda({ + multisigPda, + createKey: dao, + }); + + const [ammPosition] = PublicKey.findProgramAddressSync( + [Buffer.from("amm_position"), dao.toBuffer(), multisigVault.toBuffer()], + FUTARCHY_V0_6_PROGRAM_ID, + ); + + return this.relaunchProgram.methods + .completeRelaunch() + .accounts({ + relaunch, + payer, + relaunchSigner, + newMint, + usdcMint: MAINNET_USDC, + newTokenVault, + usdcVault, + tokenMetadata, + dao, + futarchyAmmBaseVault: getAssociatedTokenAddressSync(newMint, dao, true), + futarchyAmmQuoteVault: getAssociatedTokenAddressSync( + MAINNET_USDC, + dao, + true, + ), + ammPosition, + squadsMultisig: multisigPda, + squadsMultisigVault: multisigVault, + spendingLimit, + squadsProgramConfig: SQUADS_PROGRAM_CONFIG, + squadsProgramConfigTreasury: SQUADS_PROGRAM_CONFIG_TREASURY, + treasuryUsdcAccount: getAssociatedTokenAddressSync( + MAINNET_USDC, + multisigVault, + true, + ), + futarchyProgram: FUTARCHY_V0_6_PROGRAM_ID, + futarchyEventAuthority, + squadsProgram: SQUADS_PROGRAM_ID, + tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .preInstructions([ + // DAO init + Squads multisig creation + AMM seeding in one tx run + // just shy of 400k; give the budget real headroom. + ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000 }), + ]); + } + + // Completes the relaunch as any cranker (the provider wallet), reading the + // new mint from the stored relaunch. + async completeRelaunch({ + relaunch, + }: { + relaunch: PublicKey; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + return this.completeRelaunchIx({ + relaunch, + newMint: storedRelaunch.newMint, + }).rpc(); + } + markFailedIx({ relaunch }: { relaunch: PublicKey }) { return this.relaunchProgram.methods.markFailed().accounts({ relaunch, diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 079cce99d..960c28e23 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -705,6 +705,156 @@ export type Relaunch = { }, ]; }, + { + name: "completeRelaunch"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + docs: ["handoffs."]; + }, + { + name: "newMint"; + isMut: true; + isSigner: false; + docs: [ + "The DAO's base mint; its mint authority moves to the Squads vault.", + ]; + }, + { + name: "usdcMint"; + isMut: false; + isSigner: false; + docs: ["The DAO's quote mint."]; + }, + { + name: "newTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "usdcVault"; + isMut: true; + isSigner: false; + }, + { + name: "tokenMetadata"; + isMut: true; + isSigner: false; + docs: ["Squads vault."]; + }, + { + name: "dao"; + isMut: true; + isSigner: false; + docs: ["hardcoded `nonce: 0` CPI param."]; + }, + { + name: "futarchyAmmBaseVault"; + isMut: true; + isSigner: false; + }, + { + name: "futarchyAmmQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "ammPosition"; + isMut: true; + isSigner: false; + docs: ["position."]; + }, + { + name: "squadsMultisig"; + isMut: true; + isSigner: false; + }, + { + name: "squadsMultisigVault"; + isMut: false; + isSigner: false; + docs: ["authorities."]; + }, + { + name: "spendingLimit"; + isMut: true; + isSigner: false; + }, + { + name: "squadsProgramConfig"; + isMut: false; + isSigner: false; + }, + { + name: "squadsProgramConfigTreasury"; + isMut: true; + isSigner: false; + }, + { + name: "treasuryUsdcAccount"; + isMut: true; + isSigner: false; + }, + { + name: "futarchyProgram"; + isMut: false; + isSigner: false; + }, + { + name: "futarchyEventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "squadsProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenMetadataProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, { name: "markFailed"; accounts: [ @@ -1513,6 +1663,53 @@ export type Relaunch = { }, ]; }, + { + name: "RelaunchCompletedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "dao"; + type: "publicKey"; + index: false; + }, + { + name: "daoVault"; + type: "publicKey"; + index: false; + }, + { + name: "usdcRecovered"; + type: "u64"; + index: false; + }, + { + name: "twapInitialObservation"; + type: "u128"; + index: false; + }, + { + name: "usdcToLp"; + type: "u64"; + index: false; + }, + { + name: "usdcToTreasury"; + type: "u64"; + index: false; + }, + ]; + }, { name: "TokensDepositedViaBuyEvent"; fields: [ @@ -1682,6 +1879,11 @@ export type Relaunch = { name: "SlippageExceeded"; msg: "Swap output is below the minimum output amount"; }, + { + code: 6023; + name: "RelaunchNotSwapped"; + msg: "Relaunch must be in the Swapped state"; + }, ]; }; @@ -2392,6 +2594,156 @@ export const IDL: Relaunch = { }, ], }, + { + name: "completeRelaunch", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + docs: ["handoffs."], + }, + { + name: "newMint", + isMut: true, + isSigner: false, + docs: [ + "The DAO's base mint; its mint authority moves to the Squads vault.", + ], + }, + { + name: "usdcMint", + isMut: false, + isSigner: false, + docs: ["The DAO's quote mint."], + }, + { + name: "newTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "usdcVault", + isMut: true, + isSigner: false, + }, + { + name: "tokenMetadata", + isMut: true, + isSigner: false, + docs: ["Squads vault."], + }, + { + name: "dao", + isMut: true, + isSigner: false, + docs: ["hardcoded `nonce: 0` CPI param."], + }, + { + name: "futarchyAmmBaseVault", + isMut: true, + isSigner: false, + }, + { + name: "futarchyAmmQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "ammPosition", + isMut: true, + isSigner: false, + docs: ["position."], + }, + { + name: "squadsMultisig", + isMut: true, + isSigner: false, + }, + { + name: "squadsMultisigVault", + isMut: false, + isSigner: false, + docs: ["authorities."], + }, + { + name: "spendingLimit", + isMut: true, + isSigner: false, + }, + { + name: "squadsProgramConfig", + isMut: false, + isSigner: false, + }, + { + name: "squadsProgramConfigTreasury", + isMut: true, + isSigner: false, + }, + { + name: "treasuryUsdcAccount", + isMut: true, + isSigner: false, + }, + { + name: "futarchyProgram", + isMut: false, + isSigner: false, + }, + { + name: "futarchyEventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "squadsProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenMetadataProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, { name: "markFailed", accounts: [ @@ -3200,6 +3552,53 @@ export const IDL: Relaunch = { }, ], }, + { + name: "RelaunchCompletedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "dao", + type: "publicKey", + index: false, + }, + { + name: "daoVault", + type: "publicKey", + index: false, + }, + { + name: "usdcRecovered", + type: "u64", + index: false, + }, + { + name: "twapInitialObservation", + type: "u128", + index: false, + }, + { + name: "usdcToLp", + type: "u64", + index: false, + }, + { + name: "usdcToTreasury", + type: "u64", + index: false, + }, + ], + }, { name: "TokensDepositedViaBuyEvent", fields: [ @@ -3369,5 +3768,10 @@ export const IDL: Relaunch = { name: "SlippageExceeded", msg: "Swap output is below the minimum output amount", }, + { + code: 6023, + name: "RelaunchNotSwapped", + msg: "Relaunch must be in the Swapped state", + }, ], }; diff --git a/tasks.md b/tasks.md index 882124bfb..d49528a33 100644 --- a/tasks.md +++ b/tasks.md @@ -23,20 +23,11 @@ ## Tasks -### Phase 4: complete_relaunch - -> Reference: `relaunch-implementation-plan.md` → "Stage 4 — complete_relaunch" - -- [NEXT] 4.1 `complete_relaunch` (instruction + SDK + tests) - - u128 `price_1e12`; CPI `futarchy::initialize_dao` (launchpad-parity params incl. 1.5M min stake) + `provide_liquidity` (2M base, `usdc_recovered / 5` quote, Squads-vault position); transfer remaining USDC + mint & metadata authorities to Squads vault; store `dao`/`dao_vault`; `Complete` - - Squads accounts pre-created/PDA-derived exactly as launchpad (reentrancy-safe shape) - - Tests per plan §8: param assertions, AMM-ratio == twap identity, treasury remainder, authority handoffs, permissionless crank, rounding conservation - ### Phase 5: claim + full lifecycle > Reference: `relaunch-implementation-plan.md` → "Stage 5 — claim + full lifecycle" -- [ ] 5.1 `claim` (instruction + SDK + tests) +- [NEXT] 5.1 `claim` (instruction + SDK + tests) - `Complete`, u128 floor pro-rata, transfer to depositor ATA (`associated_token` constraints), set `claimed`; dust stays in vault (no closes in v1) - Tests per plan §9: pro-rata correctness, dust bound, double-claim, wrong state, buy-depositor parity diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index a7e1e3bba..5b2d835a4 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -7,6 +7,7 @@ import depositViaBuy from "./unit/depositViaBuy.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; import executeUsdcSwap from "./unit/executeUsdcSwap.test.js"; +import completeRelaunch from "./unit/completeRelaunch.test.js"; import markFailed from "./unit/markFailed.test.js"; import claimRefund from "./unit/claimRefund.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; @@ -29,6 +30,7 @@ export default function suite() { describe("#close_deposits", closeDeposits); describe("#execute_sell", executeSell); describe("#execute_usdc_swap", executeUsdcSwap); + describe("#complete_relaunch", completeRelaunch); describe("#mark_failed", markFailed); describe("#claim_refund", claimRefund); } diff --git a/tests/relaunch/unit/completeRelaunch.test.ts b/tests/relaunch/unit/completeRelaunch.test.ts new file mode 100644 index 000000000..719bbf633 --- /dev/null +++ b/tests/relaunch/unit/completeRelaunch.test.ts @@ -0,0 +1,439 @@ +import { + ComputeBudgetProgram, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import * as multisig from "@sqds/multisig"; +import { assert } from "chai"; +import BN from "bn.js"; +import { BanksClient } from "solana-bankrun"; +import { + FutarchyClient, + getDaoAddr, + getMetadataAddr, + MAINNET_USDC, + RelaunchClient, +} from "@metadaoproject/programs"; +import { deserializeMetadata } from "@metaplex-foundation/mpl-token-metadata"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; +const TOKENS_TO_FUTARCHY_LIQUIDITY = 2_000_000n * 10n ** 6n; +const PROPOSAL_MIN_STAKE_TOKENS = 1_500_000n * 10n ** 6n; +const PRICE_SCALE = 10n ** 12n; + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const DEFAULT_THRESHOLD_BPS = 1000; +// 10% of the 1B-token default supply = 100M tokens. +const DEPOSIT_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount(address, { + ...raw, + data: Buffer.from(raw.data), + } as any).amount; +} + +async function metadataUpdateAuthority( + banksClient: BanksClient, + mint: PublicKey, +): Promise { + const [tokenMetadata] = getMetadataAddr(mint); + const raw = await banksClient.getAccount(tokenMetadata); + const metadata = deserializeMetadata({ + ...raw, + publicKey: fromWeb3JsPublicKey(tokenMetadata), + owner: fromWeb3JsPublicKey(raw.owner), + lamports: { + basisPoints: BigInt(raw.lamports), + identifier: "SOL", + decimals: 9, + }, + rentEpoch: raw.rentEpoch ? BigInt(raw.rentEpoch) : undefined, + } as any); + return toWeb3JsPublicKey(metadata.updateAuthority); +} + +export default function suite() { + let client: RelaunchClient; + let futarchyClient: FutarchyClient; + + before(function () { + client = this.relaunch; + futarchyClient = this.futarchy; + }); + + const setupSwappedRelaunch = async function ( + this: Mocha.Context, + { + quoteMint = MAINNET_USDC, + depositAmount = DEPOSIT_AMOUNT, + thresholdBps = DEFAULT_THRESHOLD_BPS, + monthlySpendingLimitAmount, + monthlySpendingLimitMembers, + sell = true, + }: { + quoteMint?: PublicKey; + depositAmount?: bigint; + thresholdBps?: number; + monthlySpendingLimitAmount?: BN; + monthlySpendingLimitMembers?: PublicKey[]; + sell?: boolean; + } = {}, + ): Promise<{ relaunch: PublicKey; newMint: PublicKey }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: quoteMint.equals(token.NATIVE_MINT) + ? WSOL_POOL_QUOTE_RESERVE + : USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: setup.oldTokenProgram, + }); + + const { relaunch, newMint } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: quoteMint, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps, + monthlySpendingLimitAmount, + monthlySpendingLimitMembers, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + await client + .depositIx({ + relaunch, + oldMint: setup.oldMint, + oldTokenProgram: setup.oldTokenProgram, + amount: new BN(depositAmount.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + if (sell) { + await client.executeSell({ relaunch }); + } + + return { relaunch, newMint }; + }; + + // The happy path runs relaunch → futarchy → squads in one transaction, so + // it doubles as the reentrancy-shape check: no program twice in the stack. + it("completes into a DAO with launchpad-parity params in a single transaction", async function () { + const monthlySpend = new BN(100_000_000); // 100 USDC + const { relaunch, newMint } = await setupSwappedRelaunch.call(this, { + monthlySpendingLimitAmount: monthlySpend, + monthlySpendingLimitMembers: [this.payer.publicKey], + }); + + let storedRelaunch = await client.fetchRelaunch(relaunch); + const usdcRecovered = BigInt(storedRelaunch.usdcRecovered.toString()); + const usdcToLp = usdcRecovered / 5n; + + await client.completeRelaunch({ relaunch }); + + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.complete); + assert.isNotNull(storedRelaunch.unixTimestampCompleted); + assert.equal(storedRelaunch.seqNum.toString(), "5"); + + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const [dao] = getDaoAddr({ nonce: new BN(0), daoCreator: relaunchSigner }); + const [multisigPda] = multisig.getMultisigPda({ createKey: dao }); + const [multisigVault] = multisig.getVaultPda({ multisigPda, index: 0 }); + assert.ok(storedRelaunch.dao.equals(dao)); + assert.ok(storedRelaunch.daoVault.equals(multisigVault)); + + const storedDao = await futarchyClient.getDao(dao); + assert.ok(storedDao.daoCreator.equals(relaunchSigner)); + assert.equal(storedDao.nonce.toString(), "0"); + assert.ok(storedDao.baseMint.equals(newMint)); + assert.ok(storedDao.quoteMint.equals(MAINNET_USDC)); + assert.ok(storedDao.squadsMultisig.equals(multisigPda)); + assert.ok(storedDao.squadsMultisigVault.equals(multisigVault)); + + const expectedTwap = (usdcRecovered * PRICE_SCALE) / TOKENS_TO_DEPOSITORS; + assert.equal( + storedDao.twapInitialObservation.toString(), + expectedTwap.toString(), + ); + assert.equal( + storedDao.twapMaxObservationChangePerUpdate.toString(), + (expectedTwap / 20n).toString(), + ); + assert.equal(storedDao.twapStartDelaySeconds, 24 * 60 * 60); + assert.equal(storedDao.passThresholdBps, 300); + assert.equal(storedDao.secondsPerProposal, 3 * 24 * 60 * 60); + assert.equal( + storedDao.baseToStake.toString(), + PROPOSAL_MIN_STAKE_TOKENS.toString(), + ); + assert.equal(storedDao.minBaseFutarchicLiquidity.toString(), "1"); + assert.equal(storedDao.minQuoteFutarchicLiquidity.toString(), "1"); + assert.equal(storedDao.teamSponsoredPassThresholdBps, -300); + assert.ok(storedDao.teamAddress.equals(this.payer.publicKey)); + assert.equal( + storedDao.initialSpendingLimit.amountPerMonth.toString(), + monthlySpend.toString(), + ); + assert.deepEqual( + storedDao.initialSpendingLimit.members.map((member) => member.toBase58()), + [this.payer.publicKey.toBase58()], + ); + + const [spendingLimit] = multisig.getSpendingLimitPda({ + multisigPda, + createKey: dao, + }); + assert.isNotNull(await this.banksClient.getAccount(spendingLimit)); + + // The AMM holds the full 2M bucket against raise/5, so its open ratio is + // exactly the TWAP's initial observation. + const spot = storedDao.amm.state.spot.spot; + const baseReserves = BigInt(spot.baseReserves.toString()); + const quoteReserves = BigInt(spot.quoteReserves.toString()); + assert.equal( + baseReserves.toString(), + TOKENS_TO_FUTARCHY_LIQUIDITY.toString(), + ); + assert.equal(quoteReserves.toString(), usdcToLp.toString()); + assert.equal( + ((quoteReserves * PRICE_SCALE) / baseReserves).toString(), + expectedTwap.toString(), + ); + + const ammBaseVaultBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync(newMint, dao, true), + ); + assert.equal( + ammBaseVaultBalance.toString(), + TOKENS_TO_FUTARCHY_LIQUIDITY.toString(), + ); + const ammQuoteVaultBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync(MAINNET_USDC, dao, true), + ); + assert.equal(ammQuoteVaultBalance.toString(), usdcToLp.toString()); + + const [ammPosition] = PublicKey.findProgramAddressSync( + [Buffer.from("amm_position"), dao.toBuffer(), multisigVault.toBuffer()], + futarchyClient.getProgramId(), + ); + const storedPosition = + await futarchyClient.futarchy.account.ammPosition.fetch(ammPosition); + assert.ok(storedPosition.positionAuthority.equals(multisigVault)); + assert.ok(storedPosition.dao.equals(dao)); + + const treasuryBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync(MAINNET_USDC, multisigVault, true), + ); + assert.equal( + treasuryBalance.toString(), + (usdcRecovered - usdcToLp).toString(), + ); + + const usdcVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.usdcVault, + ); + assert.equal(usdcVaultBalance.toString(), "0"); + + const newTokenVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.newTokenVault, + ); + assert.equal( + newTokenVaultBalance.toString(), + TOKENS_TO_DEPOSITORS.toString(), + ); + + const mint = await this.getMint(newMint); + assert.ok(mint.mintAuthority.equals(multisigVault)); + assert.ok( + (await metadataUpdateAuthority(this.banksClient, newMint)).equals( + multisigVault, + ), + ); + }); + + it("completes without a Squads spending limit when none is configured", async function () { + const { relaunch } = await setupSwappedRelaunch.call(this); + + await client.completeRelaunch({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.complete); + + const storedDao = await futarchyClient.getDao(storedRelaunch.dao); + assert.isNull(storedDao.initialSpendingLimit); + + const [multisigPda] = multisig.getMultisigPda({ + createKey: storedRelaunch.dao, + }); + const [spendingLimit] = multisig.getSpendingLimitPda({ + multisigPda, + createKey: storedRelaunch.dao, + }); + assert.isNull(await this.banksClient.getAccount(spendingLimit)); + }); + + it("lets any keypair crank the completion", async function () { + const { relaunch, newMint } = await setupSwappedRelaunch.call(this); + + const cranker = Keypair.generate(); + const fund = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: this.payer.publicKey, + toPubkey: cranker.publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ); + fund.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + fund.feePayer = this.payer.publicKey; + fund.sign(this.payer); + await this.banksClient.processTransaction(fund); + + const tx = await client + .completeRelaunchIx({ + relaunch, + newMint, + payer: cranker.publicKey, + }) + .transaction(); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = cranker.publicKey; + tx.sign(cranker); + await this.banksClient.processTransaction(tx); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.complete); + }); + + it("conserves every USDC raw unit across the LP/treasury split", async function () { + // 7% of the supply at a 5% threshold, so the recovered amount lands on a + // different residue than the happy path's. + const { relaunch } = await setupSwappedRelaunch.call(this, { + depositAmount: (DEFAULT_OLD_SUPPLY * 7n) / 100n, + thresholdBps: 500, + }); + + let storedRelaunch = await client.fetchRelaunch(relaunch); + const usdcRecovered = BigInt(storedRelaunch.usdcRecovered.toString()); + + await client.completeRelaunch({ relaunch }); + + storedRelaunch = await client.fetchRelaunch(relaunch); + const storedDao = await futarchyClient.getDao(storedRelaunch.dao); + const quoteReserves = BigInt( + storedDao.amm.state.spot.spot.quoteReserves.toString(), + ); + assert.equal(quoteReserves.toString(), (usdcRecovered / 5n).toString()); + + const treasuryBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync( + MAINNET_USDC, + storedRelaunch.daoVault, + true, + ), + ); + assert.equal( + (quoteReserves + treasuryBalance).toString(), + usdcRecovered.toString(), + ); + + const usdcVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.usdcVault, + ); + assert.equal(usdcVaultBalance.toString(), "0"); + }); + + it("fails before the USDC swap for a WSOL-quoted source", async function () { + const { relaunch } = await setupSwappedRelaunch.call(this, { + quoteMint: token.NATIVE_MINT, + }); + + try { + await client.completeRelaunch({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSwapped"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + }); + + it("fails for a failed relaunch", async function () { + // Half the threshold, so closing lands in Failed. + const { relaunch } = await setupSwappedRelaunch.call(this, { + depositAmount: DEPOSIT_AMOUNT / 2n, + sell: false, + }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + + try { + await client.completeRelaunch({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSwapped"); + } + }); + + it("fails when the relaunch has already been completed", async function () { + const { relaunch, newMint } = await setupSwappedRelaunch.call(this); + + await client.completeRelaunch({ relaunch }); + + try { + // The compute-unit-price instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first completion. + await client + .completeRelaunchIx({ relaunch, newMint }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSwapped"); + } + }); +} From 84c4208905bc409f89ee6a2aa0213007c37ee283 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 11 Aug 2026 12:08:36 -0700 Subject: [PATCH 18/31] claim ix, sdk, tests --- programs/relaunch/src/error.rs | 4 + programs/relaunch/src/events.rs | 10 + programs/relaunch/src/instructions/claim.rs | 113 +++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 5 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 71 ++++ sdk/src/relaunch/v0.1/types/relaunch.ts | 208 ++++++++++++ tasks.md | 6 +- tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/claim.test.ts | 349 ++++++++++++++++++++ 10 files changed, 765 insertions(+), 5 deletions(-) create mode 100644 programs/relaunch/src/instructions/claim.rs create mode 100644 tests/relaunch/unit/claim.test.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 833a691a5..555e10856 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -50,4 +50,8 @@ pub enum RelaunchError { SlippageExceeded, #[msg("Relaunch must be in the Swapped state")] RelaunchNotSwapped, + #[msg("Relaunch must be in the Complete state")] + RelaunchNotComplete, + #[msg("Casting overflow. If you're seeing this, please report this")] + CastingOverflow, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 48fb2408e..1cb1805c3 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -115,6 +115,16 @@ pub struct RelaunchCompletedEvent { pub usdc_to_treasury: u64, } +#[event] +pub struct TokensClaimedEvent { + pub common: CommonFields, + pub relaunch: Pubkey, + pub depositor: Pubkey, + pub deposit_record: Pubkey, + pub amount_claimed: u64, + pub deposit_record_seq_num: u64, +} + #[event] pub struct TokensDepositedViaBuyEvent { pub common: CommonFields, diff --git a/programs/relaunch/src/instructions/claim.rs b/programs/relaunch/src/instructions/claim.rs new file mode 100644 index 000000000..b1c425cfd --- /dev/null +++ b/programs/relaunch/src/instructions/claim.rs @@ -0,0 +1,113 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{self, Mint, Token, TokenAccount, Transfer}; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, TokensClaimedEvent}; +use crate::state::{DepositRecord, Relaunch, RelaunchState}; +use crate::TOKENS_TO_DEPOSITORS; + +#[event_cpi] +#[derive(Accounts)] +pub struct Claim<'info> { + #[account( + mut, + has_one = new_mint, + has_one = new_token_vault, + has_one = relaunch_signer, + )] + pub relaunch: Box>, + + #[account( + mut, + has_one = relaunch, + has_one = depositor, + seeds = [b"deposit_record", relaunch.key().as_ref(), depositor.key().as_ref()], + bump = deposit_record.pda_bump + )] + pub deposit_record: Box>, + + pub new_mint: Box>, + + #[account(mut)] + pub new_token_vault: Box>, + + /// CHECK: just a signer + pub relaunch_signer: UncheckedAccount<'info>, + + /// CHECK: the claim recipient; not required to sign, so anyone can crank + /// claims for any depositor. + pub depositor: UncheckedAccount<'info>, + + #[account( + mut, + associated_token::mint = new_mint, + associated_token::authority = depositor, + )] + pub depositor_token_account: Box>, + + pub token_program: Program<'info, Token>, +} + +impl Claim<'_> { + pub fn validate(&self) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Complete, + RelaunchError::RelaunchNotComplete + ); + + require!(!self.deposit_record.claimed, RelaunchError::AlreadyClaimed); + + Ok(()) + } + + pub fn handle(ctx: Context) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + // The depositor's floor pro-rata share of the depositor bucket; + // rounding dust stays in the vault. + let amount_claimed = u64::try_from( + ctx.accounts.deposit_record.amount_deposited as u128 * TOKENS_TO_DEPOSITORS as u128 + / ctx.accounts.relaunch.total_deposited as u128, + ) + .map_err(|_| RelaunchError::CastingOverflow)?; + + let deposit_record = &mut ctx.accounts.deposit_record; + deposit_record.claimed = true; + deposit_record.seq_num += 1; + + token::transfer( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + Transfer { + from: ctx.accounts.new_token_vault.to_account_info(), + to: ctx.accounts.depositor_token_account.to_account_info(), + authority: ctx.accounts.relaunch_signer.to_account_info(), + }, + signer, + ), + amount_claimed, + )?; + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(TokensClaimedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + depositor: ctx.accounts.depositor.key(), + deposit_record: ctx.accounts.deposit_record.key(), + amount_claimed, + deposit_record_seq_num: ctx.accounts.deposit_record.seq_num, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 8a673a4c1..a891008f4 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -1,3 +1,4 @@ +pub mod claim; pub mod claim_refund; pub mod close_deposits; pub mod complete_relaunch; @@ -9,6 +10,7 @@ pub mod initialize_relaunch; pub mod mark_failed; pub mod start_deposits; +pub use claim::*; pub use claim_refund::*; pub use close_deposits::*; pub use complete_relaunch::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 703f612a4..8a1383a66 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -80,6 +80,11 @@ pub mod relaunch { CompleteRelaunch::handle(ctx) } + #[access_control(ctx.accounts.validate())] + pub fn claim(ctx: Context) -> Result<()> { + Claim::handle(ctx) + } + #[access_control(ctx.accounts.validate())] pub fn mark_failed(ctx: Context) -> Result<()> { MarkFailed::handle(ctx) diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index fa953bd74..0a1b62b8d 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -722,6 +722,56 @@ export class RelaunchClient { }); } + claimIx({ + relaunch, + newMint, + depositor = this.provider.publicKey, + payer = this.provider.publicKey, + }: { + relaunch: PublicKey; + newMint: PublicKey; + depositor?: PublicKey; + payer?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + + const newTokenVault = getAssociatedTokenAddressSync( + newMint, + relaunchSigner, + true, + ); + const depositorTokenAccount = getAssociatedTokenAddressSync( + newMint, + depositor, + false, + ); + + return this.relaunchProgram.methods + .claim() + .accounts({ + relaunch, + depositRecord, + newMint, + newTokenVault, + relaunchSigner, + depositor, + depositorTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + payer, + depositorTokenAccount, + depositor, + newMint, + ), + ]); + } + // Deposits from the provider wallet, reading the old mint and its owner // program from the stored relaunch. async deposit({ @@ -988,6 +1038,27 @@ export class RelaunchClient { }).rpc(); } + // Claims the depositor's pro-rata share of the new token (the provider + // wallet by default), reading the new mint from the stored relaunch. + async claim({ + relaunch, + depositor = this.provider.publicKey, + }: { + relaunch: PublicKey; + depositor?: PublicKey; + }): Promise { + const storedRelaunch = await this.fetchRelaunch(relaunch); + if (storedRelaunch === null) { + throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); + } + + return this.claimIx({ + relaunch, + newMint: storedRelaunch.newMint, + depositor, + }).rpc(); + } + // Builds the create-mint-to-self pre-instructions: a `createAccountWithSeed` // + `initializeMint2` pair with the payer as mint authority, so // `initialize_relaunch` can take the authority from a mint the payer diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 960c28e23..ee86918fc 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -855,6 +855,63 @@ export type Relaunch = { ]; args: []; }, + { + name: "claim"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "depositRecord"; + isMut: true; + isSigner: false; + }, + { + name: "newMint"; + isMut: false; + isSigner: false; + }, + { + name: "newTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "depositor"; + isMut: false; + isSigner: false; + docs: ["claims for any depositor."]; + }, + { + name: "depositorTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, { name: "markFailed"; accounts: [ @@ -1710,6 +1767,43 @@ export type Relaunch = { }, ]; }, + { + name: "TokensClaimedEvent"; + fields: [ + { + name: "common"; + type: { + defined: "CommonFields"; + }; + index: false; + }, + { + name: "relaunch"; + type: "publicKey"; + index: false; + }, + { + name: "depositor"; + type: "publicKey"; + index: false; + }, + { + name: "depositRecord"; + type: "publicKey"; + index: false; + }, + { + name: "amountClaimed"; + type: "u64"; + index: false; + }, + { + name: "depositRecordSeqNum"; + type: "u64"; + index: false; + }, + ]; + }, { name: "TokensDepositedViaBuyEvent"; fields: [ @@ -1884,6 +1978,16 @@ export type Relaunch = { name: "RelaunchNotSwapped"; msg: "Relaunch must be in the Swapped state"; }, + { + code: 6024; + name: "RelaunchNotComplete"; + msg: "Relaunch must be in the Complete state"; + }, + { + code: 6025; + name: "CastingOverflow"; + msg: "Casting overflow. If you're seeing this, please report this"; + }, ]; }; @@ -2744,6 +2848,63 @@ export const IDL: Relaunch = { ], args: [], }, + { + name: "claim", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "depositRecord", + isMut: true, + isSigner: false, + }, + { + name: "newMint", + isMut: false, + isSigner: false, + }, + { + name: "newTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "depositor", + isMut: false, + isSigner: false, + docs: ["claims for any depositor."], + }, + { + name: "depositorTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, { name: "markFailed", accounts: [ @@ -3599,6 +3760,43 @@ export const IDL: Relaunch = { }, ], }, + { + name: "TokensClaimedEvent", + fields: [ + { + name: "common", + type: { + defined: "CommonFields", + }, + index: false, + }, + { + name: "relaunch", + type: "publicKey", + index: false, + }, + { + name: "depositor", + type: "publicKey", + index: false, + }, + { + name: "depositRecord", + type: "publicKey", + index: false, + }, + { + name: "amountClaimed", + type: "u64", + index: false, + }, + { + name: "depositRecordSeqNum", + type: "u64", + index: false, + }, + ], + }, { name: "TokensDepositedViaBuyEvent", fields: [ @@ -3773,5 +3971,15 @@ export const IDL: Relaunch = { name: "RelaunchNotSwapped", msg: "Relaunch must be in the Swapped state", }, + { + code: 6024, + name: "RelaunchNotComplete", + msg: "Relaunch must be in the Complete state", + }, + { + code: 6025, + name: "CastingOverflow", + msg: "Casting overflow. If you're seeing this, please report this", + }, ], }; diff --git a/tasks.md b/tasks.md index d49528a33..a92481042 100644 --- a/tasks.md +++ b/tasks.md @@ -27,11 +27,7 @@ > Reference: `relaunch-implementation-plan.md` → "Stage 5 — claim + full lifecycle" -- [NEXT] 5.1 `claim` (instruction + SDK + tests) - - `Complete`, u128 floor pro-rata, transfer to depositor ATA (`associated_token` constraints), set `claimed`; dust stays in vault (no closes in v1) - - Tests per plan §9: pro-rata correctness, dust bound, double-claim, wrong state, buy-depositor parity - -- [ ] 5.2 Full-lifecycle integration tests +- [NEXT] 5.2 Full-lifecycle integration tests - `tests/integration/relaunch.test.ts`: happy path with money-math identities at each hop; failure path A (threshold miss → refunds); failure path B (grace lapse → `mark_failed` → refunds) - Matrix: WSOL × USDC source, classic-SPL × Token-2022 old mint (happy path minimum per combination) - Finish: full `anchor test --skip-build` suite green, no `.only` anywhere diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 5b2d835a4..09a72dfeb 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -8,6 +8,7 @@ import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; import executeUsdcSwap from "./unit/executeUsdcSwap.test.js"; import completeRelaunch from "./unit/completeRelaunch.test.js"; +import claim from "./unit/claim.test.js"; import markFailed from "./unit/markFailed.test.js"; import claimRefund from "./unit/claimRefund.test.js"; import { RelaunchClient } from "@metadaoproject/programs"; @@ -31,6 +32,7 @@ export default function suite() { describe("#execute_sell", executeSell); describe("#execute_usdc_swap", executeUsdcSwap); describe("#complete_relaunch", completeRelaunch); + describe("#claim", claim); describe("#mark_failed", markFailed); describe("#claim_refund", claimRefund); } diff --git a/tests/relaunch/unit/claim.test.ts b/tests/relaunch/unit/claim.test.ts new file mode 100644 index 000000000..46d39cadb --- /dev/null +++ b/tests/relaunch/unit/claim.test.ts @@ -0,0 +1,349 @@ +import { + ComputeBudgetProgram, + Keypair, + PublicKey, + Transaction, +} from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { MAINNET_USDC, RelaunchClient } from "@metadaoproject/programs"; +import { getAccount } from "spl-token-bankrun"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +// 10% of the 1B-token default supply = 100M tokens. +const DEFAULT_THRESHOLD_BPS = 1000; +const DEFAULT_THRESHOLD_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +export default function suite() { + let client: RelaunchClient; + let oldMint: PublicKey; + let oldTokenProgram: PublicKey; + let payerOldTokenAccount: PublicKey; + let relaunch: PublicKey; + let newMint: PublicKey; + + before(function () { + client = this.relaunch; + }); + + // Initializes a Live relaunch on a USDC-quoted source pool, so the sell + // leg lands directly in Swapped and completion needs no whirlpool swap. + const setupLiveRelaunch = async function ( + this: Mocha.Context, + thresholdBps: number = DEFAULT_THRESHOLD_BPS, + ) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: MAINNET_USDC, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: setup.oldTokenProgram, + }); + + ({ oldMint, oldTokenProgram, payerOldTokenAccount } = setup); + ({ relaunch, newMint } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: MAINNET_USDC, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps, + teamAddress: this.payer.publicKey, + })); + + await client.startDepositsIx({ relaunch }).rpc(); + }; + + // Creates the depositor's ATA and funds it with old tokens from the payer. + const fundDepositor = async function ( + this: Mocha.Context, + depositor: PublicKey, + amount: bigint, + ): Promise { + const ata = token.getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + this.payer.publicKey, + ata, + depositor, + oldMint, + oldTokenProgram, + ), + token.createTransferCheckedInstruction( + payerOldTokenAccount, + oldMint, + ata, + this.payer.publicKey, + amount, + 6, + [], + oldTokenProgram, + ), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + return ata; + }; + + const deposit = async function ( + this: Mocha.Context, + amount: bigint, + depositor?: Keypair, + ) { + const builder = client.depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(amount.toString()), + depositor: depositor?.publicKey, + }); + if (depositor !== undefined) { + builder.signers([depositor]); + } + await builder.rpc(); + }; + + const closeDeposits = async function (this: Mocha.Context) { + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + }; + + const sellAndComplete = async function (this: Mocha.Context) { + await closeDeposits.call(this); + await client.executeSell({ relaunch }); + await client.completeRelaunch({ relaunch }); + }; + + const newTokenBalance = async function ( + this: Mocha.Context, + owner: PublicKey, + ): Promise { + const account = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync(newMint, owner), + ); + return account.amount; + }; + + it("distributes the depositor bucket pro-rata across depositors", async function () { + await setupLiveRelaunch.call(this); + const alice = Keypair.generate(); + const bob = Keypair.generate(); + await fundDepositor.call(this, alice.publicKey, 25_000_000_000_000n); + await fundDepositor.call(this, bob.publicKey, 75_000_000_000_000n); + + await deposit.call(this, 25_000_000_000_000n, alice); // 25M tokens + await deposit.call(this, 75_000_000_000_000n, bob); // 75M tokens + await deposit.call(this, 100_000_000_000_000n); // 100M tokens + await sellAndComplete.call(this); + + // The depositor never signs — the provider wallet cranks alice's and + // bob's claims. + await client + .claimIx({ relaunch, newMint, depositor: alice.publicKey }) + .rpc(); + await client.claimIx({ relaunch, newMint, depositor: bob.publicKey }).rpc(); + await client.claimIx({ relaunch, newMint }).rpc(); + + // 10M × 25/200, 10M × 75/200, 10M × 100/200. + assert.equal( + (await newTokenBalance.call(this, alice.publicKey)).toString(), + "1250000000000", + ); + assert.equal( + (await newTokenBalance.call(this, bob.publicKey)).toString(), + "3750000000000", + ); + assert.equal( + (await newTokenBalance.call(this, this.payer.publicKey)).toString(), + "5000000000000", + ); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + assert.equal(record.seqNum.toString(), "1"); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.seqNum.toString(), "10"); + + // The shares divide evenly, so the vault empties completely. + const vault = await getAccount( + this.banksClient, + storedRelaunch.newTokenVault, + ); + assert.equal(vault.amount.toString(), "0"); + }); + + it("floors each entitlement and strands the dust in the vault", async function () { + await setupLiveRelaunch.call(this); + const alice = Keypair.generate(); + await fundDepositor.call(this, alice.publicKey, 100_000_000_000_000n); + + const aliceDeposit = 100_000_000_000_000n; // 100M tokens + const payerDeposit = 200_000_000_000_000n; // 200M tokens + const total = aliceDeposit + payerDeposit; + await deposit.call(this, aliceDeposit, alice); + await deposit.call(this, payerDeposit); + await sellAndComplete.call(this); + + await client + .claimIx({ relaunch, newMint, depositor: alice.publicKey }) + .rpc(); + await client.claimIx({ relaunch, newMint }).rpc(); + + const aliceClaimed = await newTokenBalance.call(this, alice.publicKey); + const payerClaimed = await newTokenBalance.call(this, this.payer.publicKey); + assert.equal( + aliceClaimed.toString(), + ((TOKENS_TO_DEPOSITORS * aliceDeposit) / total).toString(), + ); + assert.equal( + payerClaimed.toString(), + ((TOKENS_TO_DEPOSITORS * payerDeposit) / total).toString(), + ); + + // Both thirds floor, so exactly one raw unit of dust stays behind. + assert.isTrue(aliceClaimed + payerClaimed <= TOKENS_TO_DEPOSITORS); + const storedRelaunch = await client.fetchRelaunch(relaunch); + const vault = await getAccount( + this.banksClient, + storedRelaunch.newTokenVault, + ); + assert.equal(vault.amount.toString(), "1"); + assert.equal( + (aliceClaimed + payerClaimed + vault.amount).toString(), + TOKENS_TO_DEPOSITORS.toString(), + ); + }); + + it("claims identically for a depositor who entered via deposit_via_buy", async function () { + // 1 bps of the 1B supply = 100k tokens, reachable with a 50k-token buy + // off the 1M-token pool. + await setupLiveRelaunch.call(this, 1); + const alice = Keypair.generate(); + await fundDepositor.call(this, alice.publicKey, 50_000_000_000n); + + await deposit.call(this, 50_000_000_000n, alice); // 50k tokens + await client.depositViaBuy({ + relaunch, + baseOut: new BN(50_000_000_000), // 50k tokens + }); + await sellAndComplete.call(this); + + await client + .claimIx({ relaunch, newMint, depositor: alice.publicKey }) + .rpc(); + await client.claimIx({ relaunch, newMint }).rpc(); + + // Equal deposits, equal shares: 10M × 50k/100k each. + const aliceClaimed = await newTokenBalance.call(this, alice.publicKey); + const payerClaimed = await newTokenBalance.call(this, this.payer.publicKey); + assert.equal(aliceClaimed.toString(), "5000000000000"); + assert.equal(payerClaimed.toString(), aliceClaimed.toString()); + }); + + it("fails to claim the same record twice", async function () { + await setupLiveRelaunch.call(this); + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await sellAndComplete.call(this); + + await client.claimIx({ relaunch, newMint }).rpc(); + + try { + // The compute-unit-price instruction makes the transaction hash unique + // so the retry isn't rejected as a duplicate of the first claim. + await client + .claimIx({ relaunch, newMint }) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }), + ]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "AlreadyClaimed"); + } + + assert.equal( + (await newTokenBalance.call(this, this.payer.publicKey)).toString(), + TOKENS_TO_DEPOSITORS.toString(), + ); + }); + + it("fails without a deposit record", async function () { + await setupLiveRelaunch.call(this); + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await sellAndComplete.call(this); + + const rando = Keypair.generate(); + try { + await client + .claimIx({ relaunch, newMint, depositor: rando.publicKey }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "AccountNotInitialized"); + } + }); + + it("fails before the relaunch completes", async function () { + await setupLiveRelaunch.call(this); + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT); + await closeDeposits.call(this); + await client.executeSell({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.swapped); + + try { + await client.claimIx({ relaunch, newMint }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotComplete"); + } + }); + + it("fails for a failed relaunch", async function () { + await setupLiveRelaunch.call(this); + // Half the threshold, so closing lands in Failed. + await deposit.call(this, DEFAULT_THRESHOLD_AMOUNT / 2n); + await closeDeposits.call(this); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + + try { + await client.claimIx({ relaunch, newMint }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotComplete"); + } + }); +} From 3e4c545a11003f029d4249116f0bb70dd2220ba4 Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 11 Aug 2026 12:59:11 -0700 Subject: [PATCH 19/31] integration tests --- tasks.md | 14 +- tests/integration/relaunch.test.ts | 642 +++++++++++++++++++++++++++++ tests/main.test.ts | 2 + 3 files changed, 645 insertions(+), 13 deletions(-) create mode 100644 tests/integration/relaunch.test.ts diff --git a/tasks.md b/tasks.md index a92481042..6c0c7251a 100644 --- a/tasks.md +++ b/tasks.md @@ -23,21 +23,9 @@ ## Tasks -### Phase 5: claim + full lifecycle - -> Reference: `relaunch-implementation-plan.md` → "Stage 5 — claim + full lifecycle" - -- [NEXT] 5.2 Full-lifecycle integration tests - - `tests/integration/relaunch.test.ts`: happy path with money-math identities at each hop; failure path A (threshold miss → refunds); failure path B (grace lapse → `mark_failed` → refunds) - - Matrix: WSOL × USDC source, classic-SPL × Token-2022 old mint (happy path minimum per combination) - - Finish: full `anchor test --skip-build` suite green, no `.only` anywhere - ### Phase 6: Release prep > Reference: `relaunch-implementation-plan.md` → "After the code" -- [ ] 6.1 Final program ID - - Grind vanity keypair, set `declare_id!` + Anchor.toml, `./rebuild.sh`, full suite green - -- [ ] 6.2 README deployments-table entry + verifiable build +- [NEXT] 6.2 README deployments-table entry + verifiable build - Note: first mainnet deploy itself is manual (Squads flow is upgrade-only) — user-led, not a task here diff --git a/tests/integration/relaunch.test.ts b/tests/integration/relaunch.test.ts new file mode 100644 index 000000000..a6da50143 --- /dev/null +++ b/tests/integration/relaunch.test.ts @@ -0,0 +1,642 @@ +import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BankrunProvider } from "anchor-bankrun"; +import { BanksClient } from "solana-bankrun"; +import { + FutarchyClient, + getDaoAddr, + MAINNET_USDC, + parseWhirlpool, + RelaunchClient, + USDC_SWAP_POOL, +} from "@metadaoproject/programs"; +import { + setupRelaunch, + RelaunchSetup, + DEFAULT_OLD_SUPPLY, +} from "../relaunch/utils.js"; +import { writePumpPool } from "../relaunch/pumpAmm.js"; +import { ensureWhirlpool, WhirlpoolFixture } from "../relaunch/whirlpool.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +// Small enough that the sell proceeds (~5 SOL) swap through the whirlpool +// fixture (1000 SOL / 100k USDC) with well under 1% price impact. +const WSOL_POOL_QUOTE_RESERVE = 5n * 10n ** 9n; // 5 SOL +const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC + +const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; +const TOKENS_TO_FUTARCHY_LIQUIDITY = 2_000_000n * 10n ** 6n; +const PRICE_SCALE = 10n ** 12n; + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +// 10% of the 1B-token default supply = 100M tokens. +const THRESHOLD_BPS = 1000; +const THRESHOLD_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +// The happy-path deposits: 60M + 39.99M direct plus 10k bought lands exactly +// on the 100M threshold and splits the 10M depositor bucket into +// 6M / 3.999M / 1k with zero dust. +const ALICE_DEPOSIT = 60_000_000n * 10n ** 6n; +const BOB_DEPOSIT = 39_990_000n * 10n ** 6n; +const BUY_DEPOSIT = 10_000n * 10n ** 6n; + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, + tokenProgram: PublicKey = token.TOKEN_PROGRAM_ID, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount( + address, + { ...raw, data: Buffer.from(raw.data) } as any, + tokenProgram, + ).amount; +} + +export default function suite() { + let client: RelaunchClient; + let futarchyClient: FutarchyClient; + let whirlpool: WhirlpoolFixture; + + before(async function () { + // Created here rather than taken from the context so the suite also runs + // standalone, without the relaunch unit suites' before hook. + const provider = new BankrunProvider(this.context); + client = RelaunchClient.createClient({ provider: provider as any }); + futarchyClient = this.futarchy; + whirlpool = await ensureWhirlpool({ + provider: provider as any, + payer: this.payer, + banksClient: this.banksClient, + }); + }); + + const initializeLiveRelaunch = async function ( + this: Mocha.Context, + { + quoteMint, + oldTokenProgram, + }: { quoteMint: PublicKey; oldTokenProgram: PublicKey }, + ) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + oldTokenProgram, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: quoteMint.equals(token.NATIVE_MINT) + ? WSOL_POOL_QUOTE_RESERVE + : USDC_POOL_QUOTE_RESERVE, + baseTokenProgram: oldTokenProgram, + }); + + const { relaunch, newMint } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: quoteMint, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + + return { setup, pool, relaunch, newMint }; + }; + + // Creates the depositor's ATA and funds it with old tokens from the payer. + const fundDepositor = async function ( + this: Mocha.Context, + { oldMint, oldTokenProgram, payerOldTokenAccount }: RelaunchSetup, + depositor: PublicKey, + amount: bigint, + ): Promise { + const ata = token.getAssociatedTokenAddressSync( + oldMint, + depositor, + false, + oldTokenProgram, + ); + const tx = new Transaction().add( + token.createAssociatedTokenAccountIdempotentInstruction( + this.payer.publicKey, + ata, + depositor, + oldMint, + oldTokenProgram, + ), + token.createTransferCheckedInstruction( + payerOldTokenAccount, + oldMint, + ata, + this.payer.publicKey, + amount, + 6, + [], + oldTokenProgram, + ), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + return ata; + }; + + const deposit = async function ( + this: Mocha.Context, + relaunch: PublicKey, + { oldMint, oldTokenProgram }: RelaunchSetup, + amount: bigint, + depositor?: Keypair, + ) { + const builder = client.depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(amount.toString()), + depositor: depositor?.publicKey, + }); + if (depositor !== undefined) { + builder.signers([depositor]); + } + await builder.rpc(); + }; + + const runHappyPath = async function ( + this: Mocha.Context, + { + quoteMint, + oldTokenProgram, + }: { quoteMint: PublicKey; oldTokenProgram: PublicKey }, + ) { + const isWsol = quoteMint.equals(token.NATIVE_MINT); + const { setup, pool, relaunch, newMint } = + await initializeLiveRelaunch.call(this, { quoteMint, oldTokenProgram }); + + let stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.live); + assert.equal( + stored.oldSupplySnapshot.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), + (TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY).toString(), + ); + + // Mixed entry: two direct depositors interleaved with a bought deposit. + const alice = Keypair.generate(); + const bob = Keypair.generate(); + await fundDepositor.call(this, setup, alice.publicKey, ALICE_DEPOSIT); + await fundDepositor.call(this, setup, bob.publicKey, BOB_DEPOSIT); + + await deposit.call(this, relaunch, setup, ALICE_DEPOSIT, alice); + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BUY_DEPOSIT.toString()), + maxQuoteIn: new BN((isWsol ? 10n ** 9n : 2_000n * 10n ** 6n).toString()), + }); + await deposit.call(this, relaunch, setup, BOB_DEPOSIT, bob); + + stored = await client.fetchRelaunch(relaunch); + assert.equal(stored.totalDeposited.toString(), THRESHOLD_AMOUNT.toString()); + assert.equal( + ( + await tokenBalance( + this.banksClient, + stored.oldTokenVault, + oldTokenProgram, + ) + ).toString(), + THRESHOLD_AMOUNT.toString(), + ); + const expectedDeposits: [PublicKey, bigint][] = [ + [alice.publicKey, ALICE_DEPOSIT], + [bob.publicKey, BOB_DEPOSIT], + [this.payer.publicKey, BUY_DEPOSIT], + ]; + for (const [depositor, amount] of expectedDeposits) { + const record = await client.getDepositRecord({ relaunch, depositor }); + assert.equal(record.amountDeposited.toString(), amount.toString()); + } + + // The deposits land exactly on the threshold, so closing moves to + // SellPending. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.sellPending); + + const poolBaseBefore = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ); + const poolQuoteBefore = await tokenBalance( + this.banksClient, + pool.poolQuoteTokenAccount, + ); + + // 100 bps of slippage can exactly match pump's fee, leaving no rounding + // margin; give the sell floor explicit headroom. + await client.executeSell({ relaunch, slippageBps: 200 }); + + stored = await client.fetchRelaunch(relaunch); + const quoteRecovered = BigInt(stored.quoteRecovered.toString()); + assert.equal( + ( + await tokenBalance( + this.banksClient, + stored.oldTokenVault, + oldTokenProgram, + ) + ).toString(), + "0", + ); + assert.equal( + ( + await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ) + ).toString(), + (poolBaseBefore + THRESHOLD_AMOUNT).toString(), + ); + + // The proceeds are the constant-product output minus pump's fees. + const grossQuoteOut = + (poolQuoteBefore * THRESHOLD_AMOUNT) / + (poolBaseBefore + THRESHOLD_AMOUNT); + assert.isTrue(quoteRecovered > (grossQuoteOut * 97n) / 100n); + assert.isTrue(quoteRecovered <= grossQuoteOut); + + if (isWsol) { + assert.isDefined(stored.state.sold); + assert.equal(stored.usdcRecovered.toString(), "0"); + assert.equal( + ( + await tokenBalance(this.banksClient, stored.sourceQuoteVault) + ).toString(), + quoteRecovered.toString(), + ); + + const whirlpoolState = parseWhirlpool( + Buffer.from( + (await this.banksClient.getAccount(USDC_SWAP_POOL))!.data, + ), + ); + // Spot price in USDC-raw per WSOL-raw is (sqrtPrice / 2^64)^2. + const spotUsdcOut = + (quoteRecovered * + whirlpoolState.sqrtPrice * + whirlpoolState.sqrtPrice) >> + 128n; + const whirlpoolWsolBefore = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultA, + ); + const whirlpoolUsdcBefore = await tokenBalance( + this.banksClient, + whirlpool.tokenVaultB, + ); + + await client.executeUsdcSwap({ relaunch }); + + stored = await client.fetchRelaunch(relaunch); + assert.equal(stored.quoteRecovered.toString(), quoteRecovered.toString()); + assert.equal( + ( + await tokenBalance(this.banksClient, stored.sourceQuoteVault) + ).toString(), + "0", + ); + + // The full WSOL proceeds went into the whirlpool, every USDC it paid + // out landed in the relaunch's vault, and the output sits at spot + // minus the fee and a little price impact. + const usdcRecovered = BigInt(stored.usdcRecovered.toString()); + assert.equal( + ( + (await tokenBalance(this.banksClient, whirlpool.tokenVaultA)) - + whirlpoolWsolBefore + ).toString(), + quoteRecovered.toString(), + ); + assert.equal( + ( + whirlpoolUsdcBefore - + (await tokenBalance(this.banksClient, whirlpool.tokenVaultB)) + ).toString(), + usdcRecovered.toString(), + ); + assert.isTrue(usdcRecovered > (spotUsdcOut * 97n) / 100n); + assert.isTrue(usdcRecovered <= spotUsdcOut); + } else { + // USDC-quoted sources share one vault for quote and USDC and jump + // straight to Swapped. + assert.ok(stored.sourceQuoteVault.equals(stored.usdcVault)); + assert.equal(stored.usdcRecovered.toString(), quoteRecovered.toString()); + } + + assert.isDefined(stored.state.swapped); + const usdcRecovered = BigInt(stored.usdcRecovered.toString()); + assert.equal( + (await tokenBalance(this.banksClient, stored.usdcVault)).toString(), + usdcRecovered.toString(), + ); + + await client.completeRelaunch({ relaunch }); + + stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.complete); + + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const [dao] = getDaoAddr({ nonce: new BN(0), daoCreator: relaunchSigner }); + assert.ok(stored.dao.equals(dao)); + + const storedDao = await futarchyClient.getDao(dao); + assert.ok(storedDao.baseMint.equals(newMint)); + assert.ok(storedDao.quoteMint.equals(MAINNET_USDC)); + + // Price identity: the TWAP opens at the raise valued over the depositor + // bucket, and the AMM opens at exactly that ratio — the full 2M + // liquidity bucket against raise/5. + const expectedTwap = (usdcRecovered * PRICE_SCALE) / TOKENS_TO_DEPOSITORS; + assert.equal( + storedDao.twapInitialObservation.toString(), + expectedTwap.toString(), + ); + assert.equal( + storedDao.twapMaxObservationChangePerUpdate.toString(), + (expectedTwap / 20n).toString(), + ); + + const usdcToLp = usdcRecovered / 5n; + const spot = storedDao.amm.state.spot.spot; + const baseReserves = BigInt(spot.baseReserves.toString()); + const quoteReserves = BigInt(spot.quoteReserves.toString()); + assert.equal( + baseReserves.toString(), + TOKENS_TO_FUTARCHY_LIQUIDITY.toString(), + ); + assert.equal(quoteReserves.toString(), usdcToLp.toString()); + assert.equal( + ((quoteReserves * PRICE_SCALE) / baseReserves).toString(), + expectedTwap.toString(), + ); + + // Treasury remainder: whatever the LP seed left goes to the Squads + // vault, conserving every raw unit of the raise. + const treasuryBalance = await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync(MAINNET_USDC, stored.daoVault, true), + ); + assert.equal( + treasuryBalance.toString(), + (usdcRecovered - usdcToLp).toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.usdcVault)).toString(), + "0", + ); + + assert.equal( + (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), + TOKENS_TO_DEPOSITORS.toString(), + ); + const mint = await this.getMint(newMint); + assert.ok(mint.mintAuthority.equals(stored.daoVault)); + + // Claims: 10M × 60M/100M, 10M × 39.99M/100M, 10M × 10k/100M — every + // share divides exactly, so the vault empties with zero dust. + await client + .claimIx({ relaunch, newMint, depositor: alice.publicKey }) + .rpc(); + await client.claimIx({ relaunch, newMint, depositor: bob.publicKey }).rpc(); + await client.claimIx({ relaunch, newMint }).rpc(); + + const expectedClaims: [PublicKey, bigint][] = [ + [alice.publicKey, 6_000_000n * 10n ** 6n], + [bob.publicKey, 3_999_000n * 10n ** 6n], + [this.payer.publicKey, 1_000n * 10n ** 6n], + ]; + for (const [owner, amount] of expectedClaims) { + assert.equal( + ( + await tokenBalance( + this.banksClient, + token.getAssociatedTokenAddressSync(newMint, owner), + ) + ).toString(), + amount.toString(), + ); + const record = await client.getDepositRecord({ + relaunch, + depositor: owner, + }); + assert.isTrue(record.claimed); + } + assert.equal( + (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), + "0", + ); + + // Every hop emitted exactly one event: start, three deposits, close, + // sell, (the USDC swap,) complete, three claims. + stored = await client.fetchRelaunch(relaunch); + assert.equal(stored.seqNum.toString(), isWsol ? "11" : "10"); + }; + + describe("happy path", function () { + it("relaunches a classic-SPL token from a WSOL-quoted pool", async function () { + await runHappyPath.call(this, { + quoteMint: token.NATIVE_MINT, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + }); + }); + + it("relaunches a Token-2022 token from a WSOL-quoted pool", async function () { + await runHappyPath.call(this, { + quoteMint: token.NATIVE_MINT, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + }); + + it("relaunches a classic-SPL token from a USDC-quoted pool", async function () { + await runHappyPath.call(this, { + quoteMint: MAINNET_USDC, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + }); + }); + + it("relaunches a Token-2022 token from a USDC-quoted pool", async function () { + await runHappyPath.call(this, { + quoteMint: MAINNET_USDC, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + }); + }); + + it("misses the threshold and refunds every deposit exactly", async function () { + const { setup, relaunch } = await initializeLiveRelaunch.call(this, { + quoteMint: token.NATIVE_MINT, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + }); + + const alice = Keypair.generate(); + const bob = Keypair.generate(); + const aliceDeposit = 30_000_000n * 10n ** 6n; + const bobDeposit = 20_000_000n * 10n ** 6n; + const aliceAta = await fundDepositor.call( + this, + setup, + alice.publicKey, + aliceDeposit, + ); + const bobAta = await fundDepositor.call( + this, + setup, + bob.publicKey, + bobDeposit, + ); + + await deposit.call(this, relaunch, setup, aliceDeposit, alice); + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BUY_DEPOSIT.toString()), + maxQuoteIn: new BN((10n ** 9n).toString()), + }); + await deposit.call(this, relaunch, setup, bobDeposit, bob); + + // 50.01M of the 100M threshold, so closing lands in Failed. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + + const stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.failed); + const totalDeposited = aliceDeposit + bobDeposit + BUY_DEPOSIT; + assert.equal(stored.totalDeposited.toString(), totalDeposited.toString()); + assert.equal( + (await tokenBalance(this.banksClient, stored.oldTokenVault)).toString(), + totalDeposited.toString(), + ); + + const payerBefore = await tokenBalance( + this.banksClient, + setup.payerOldTokenAccount, + ); + + await client.claimRefund({ relaunch, depositor: alice.publicKey }); + await client.claimRefund({ relaunch, depositor: bob.publicKey }); + await client.claimRefund({ relaunch }); + + assert.equal( + (await tokenBalance(this.banksClient, aliceAta)).toString(), + aliceDeposit.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, bobAta)).toString(), + bobDeposit.toString(), + ); + // The buy is not unwound — the bought tokens refund as old tokens on + // top of what the payer held. + assert.equal( + ( + (await tokenBalance(this.banksClient, setup.payerOldTokenAccount)) - + payerBefore + ).toString(), + BUY_DEPOSIT.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.oldTokenVault)).toString(), + "0", + ); + }); + + it("marks a stalled sell Failed after the grace period and refunds exactly", async function () { + const { setup, relaunch } = await initializeLiveRelaunch.call(this, { + quoteMint: MAINNET_USDC, + oldTokenProgram: token.TOKEN_2022_PROGRAM_ID, + }); + + const alice = Keypair.generate(); + const aliceDeposit = 60_000_000n * 10n ** 6n; + const payerDeposit = 40_000_000n * 10n ** 6n; + const aliceAta = await fundDepositor.call( + this, + setup, + alice.publicKey, + aliceDeposit, + ); + + await deposit.call(this, relaunch, setup, aliceDeposit, alice); + await deposit.call(this, relaunch, setup, payerDeposit); + + // The threshold is met, so closing lands in SellPending — but the admin + // never sells. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + let stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.sellPending); + + await this.advanceBySeconds(ONE_DAY + 1); + await client.markFailedIx({ relaunch }).rpc(); + stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.failed); + + const payerBefore = await tokenBalance( + this.banksClient, + setup.payerOldTokenAccount, + token.TOKEN_2022_PROGRAM_ID, + ); + + await client.claimRefund({ relaunch, depositor: alice.publicKey }); + await client.claimRefund({ relaunch }); + + assert.equal( + ( + await tokenBalance( + this.banksClient, + aliceAta, + token.TOKEN_2022_PROGRAM_ID, + ) + ).toString(), + aliceDeposit.toString(), + ); + assert.equal( + ( + (await tokenBalance( + this.banksClient, + setup.payerOldTokenAccount, + token.TOKEN_2022_PROGRAM_ID, + )) - payerBefore + ).toString(), + payerDeposit.toString(), + ); + assert.equal( + ( + await tokenBalance( + this.banksClient, + stored.oldTokenVault, + token.TOKEN_2022_PROGRAM_ID, + ) + ).toString(), + "0", + ); + }); +} diff --git a/tests/main.test.ts b/tests/main.test.ts index 1b8d43803..59dbc55a4 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -94,6 +94,7 @@ import fullLaunch_v7 from "./integration/fullLaunch_v7.test.js"; import fullLaunch_v8 from "./integration/launchpad_v8_full_lifecycle.test.js"; import gatedLaunchpadV8 from "./integration/gatedLaunchpadV8.test.js"; import trancheLifecycle_v8 from "./integration/launchpad_v8_tranche_lifecycle.test.js"; +import relaunchLifecycle from "./integration/relaunch.test.js"; import { BN } from "bn.js"; const ONE_BUCK_PRICE = PriceMath.getAmmPrice(1, 6, 6); @@ -862,4 +863,5 @@ describe("project-wide integration tests", function () { describe("full launch v8", fullLaunch_v8); describe("gated_mint + launchpad v8", gatedLaunchpadV8); describe("full launch v8 - tranche lifecycle", trancheLifecycle_v8); + describe("relaunch full lifecycle", relaunchLifecycle); }); From 8ea1f771eedf1a2dfa4ffac272d0d3d8a65156bb Mon Sep 17 00:00:00 2001 From: Pileks Date: Tue, 11 Aug 2026 14:12:24 -0700 Subject: [PATCH 20/31] CI/CD --- .../workflows/generate-verifiable-builds.yaml | 19 +++++++++++- README.md | 1 + tasks.md | 31 ------------------- 3 files changed, 19 insertions(+), 32 deletions(-) delete mode 100644 tasks.md diff --git a/.github/workflows/generate-verifiable-builds.yaml b/.github/workflows/generate-verifiable-builds.yaml index 78680bf54..205121b30 100644 --- a/.github/workflows/generate-verifiable-builds.yaml +++ b/.github/workflows/generate-verifiable-builds.yaml @@ -201,4 +201,21 @@ jobs: uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 with: default_author: github_actions - message: 'Update gated_mint verifiable build' \ No newline at end of file + message: 'Update gated_mint verifiable build' + generate-verifiable-relaunch: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: metadaoproject/anchor-verifiable-build@6d8fc1999ea4b7ff701e8b166903b398741e1c50 # v0.4 + with: + program: relaunch + anchor-version: '0.29.0' + solana-cli-version: '1.17.31' + features: 'production' + - run: 'git pull --rebase' + - run: cp target/deploy/relaunch.so ./verifiable-builds + - name: Commit verifiable build back to mainline + uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 + with: + default_author: github_actions + message: 'Update relaunch verifiable build' \ No newline at end of file diff --git a/README.md b/README.md index d14e0bcc3..5daba823f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Programs for unruggable capital formation and market-driven governance. | program | tag | program ID | | ----------------- | ---- | -------------------------------------------- | +| relaunch | v0.1.0 | vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik | | gated_mint | v0.1.0 | GaTEjZy6eMdHg2BcL8dk3iE78jkJ9sPtyw1q2tMNi8PA | | launchpad | v0.8.0 | moonDJUoHteKkGATejA5bdJVwJ6V6Dg74gyqyJTx73n | | launchpad | v0.7.0 | moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM | diff --git a/tasks.md b/tasks.md deleted file mode 100644 index 6c0c7251a..000000000 --- a/tasks.md +++ /dev/null @@ -1,31 +0,0 @@ -# Relaunch Implementation Tasks - -## Instructions for Claude - -**READ THIS FIRST:** - -1. Look at this file and find the task marked with `[NEXT]` -2. Read the referenced section in `vibes/relaunch-implementation-plan.md` for full context (design rationale lives in `vibes/relaunch-program-design.html`) -3. Do ONLY that task - nothing else -4. After completing the task, verify: `./rebuild.sh` after any program/SDK change, then `anchor test --skip-build` (use `.only` on the new test file while iterating, remove it and run the full suite before finishing) -5. If successful, remove the completed task from this file -6. Mark the next task with `[NEXT]` -7. Stop and wait for the user - -**DO NOT:** -- Do multiple tasks at once -- Skip ahead -- Forget to verify - -**Reference:** Full implementation plan is in `vibes/relaunch-implementation-plan.md` - ---- - -## Tasks - -### Phase 6: Release prep - -> Reference: `relaunch-implementation-plan.md` → "After the code" - -- [NEXT] 6.2 README deployments-table entry + verifiable build - - Note: first mainnet deploy itself is manual (Squads flow is upgrade-only) — user-led, not a task here From 707df37302933dcb9718724200f39b5f5fc67b21 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 10:55:23 -0700 Subject: [PATCH 21/31] alt tooling --- package.json | 1 + scripts/relaunch/createAlt.ts | 480 ++++++++++++++++++++ sdk/src/constants.ts | 9 + tests/fixtures/relaunch-global-alt | Bin 0 -> 5880 bytes tests/integration/relaunch.test.ts | 4 +- tests/main.test.ts | 21 + tests/relaunch/main.test.ts | 2 + tests/relaunch/unit/altTransactions.test.ts | 363 +++++++++++++++ tests/relaunch/utils.ts | 30 ++ 9 files changed, 907 insertions(+), 3 deletions(-) create mode 100644 scripts/relaunch/createAlt.ts create mode 100644 tests/fixtures/relaunch-global-alt create mode 100644 tests/relaunch/unit/altTransactions.test.ts diff --git a/package.json b/package.json index 6a7dd00b9..e6df7b647 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "redeem-launch": "NODE_OPTIONS=\"--no-deprecation\" tsx --tsconfig tsconfig.json scripts/redeemLaunch.ts", "setup-futarchy-amm": "NODE_OPTIONS=\"--no-deprecation\" tsx --tsconfig tsconfig.json scripts/setupFutarchyAmm.ts", "initialize-dao": "NODE_OPTIONS=\"--no-deprecation\" tsx --tsconfig tsconfig.json scripts/v0.6/initializeDao.ts", + "relaunch-create-alt": "NODE_OPTIONS=\"--no-deprecation\" tsx --tsconfig tsconfig.json scripts/relaunch/createAlt.ts", "prepare": "husky" }, "lint-staged": { diff --git a/scripts/relaunch/createAlt.ts b/scripts/relaunch/createAlt.ts new file mode 100644 index 000000000..0b7d12f73 --- /dev/null +++ b/scripts/relaunch/createAlt.ts @@ -0,0 +1,480 @@ +// Creates the relaunch SDK's global frozen address lookup table. +// +// The address list mirrors vibes/relaunch-alt-contents.html: groups A-G are +// protocol statics, group H is the whirlpool tick-array band (32 arrays below +// the current price up through tick 0 = $1,000/SOL). Groups D/E/H are +// re-resolved from mainnet at run time, so run `plan` and eyeball the list +// right before creating. +// +// Usage (env: ANCHOR_PROVIDER_URL, ANCHOR_WALLET, PRIORITY_FEE_MICRO_LAMPORTS): +// yarn relaunch-create-alt # plan: print the list, no txs +// yarn relaunch-create-alt create # create table + extend + verify +// yarn relaunch-create-alt create --table # resume a partially extended table +// yarn relaunch-create-alt verify --table # compare on-chain contents to the plan +// yarn relaunch-create-alt freeze --table # verify, confirm, then freeze (PERMANENT) +// yarn relaunch-create-alt dump --table --out +// # write the frozen table's account data as a bankrun fixture; zeroes +// # last_extended_slot so all entries are active at bankrun's low slots +// +// The table authority is the script wallet until `freeze` removes it. Only +// freeze after `verify` passes and the address is pinned nowhere yet — once +// frozen the table can never be extended, closed, or edited. + +import * as anchor from "@coral-xyz/anchor"; +import { + AddressLookupTableProgram, + ComputeBudgetProgram, + Connection, + PublicKey, + SYSVAR_RENT_PUBKEY, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + NATIVE_MINT, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; +import { + FUTARCHY_V0_6_PROGRAM_ID, + MAINNET_USDC, + MPL_TOKEN_METADATA_PROGRAM_ID, + PUMP_AMM_PROGRAM_ID, + PUMP_FEES_PROGRAM_ID, + RELAUNCH_V0_1_PROGRAM_ID, + SQUADS_PROGRAM_CONFIG, + SQUADS_PROGRAM_CONFIG_TREASURY, + SQUADS_PROGRAM_ID, + WHIRLPOOL_PROGRAM_ID, +} from "@metadaoproject/programs"; +import { + MEMO_PROGRAM_ID, + PUMP_AMM_EVENT_AUTHORITY, + PUMP_AMM_FEE_CONFIG, + PUMP_AMM_GLOBAL_CONFIG, + PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, + USDC_SWAP_POOL, + getWhirlpoolTickArrayAddr, + getWhirlpoolOracleAddr, + parsePumpGlobalConfig, + parseWhirlpool, +} from "@metadaoproject/programs/relaunch"; +import * as readline from "readline/promises"; + +const MAINNET_GENESIS_HASH = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d"; +const LOOKUP_TABLE_MAX_ADDRESSES = 256; +const ADDRESSES_PER_EXTEND = 20; +const TICK_ARRAYS_BELOW_CURRENT = 32; +// The array starting at tick 0 covers prices through 1000 * 1.0001^span, +// i.e. just past $1,000/SOL given the pool's 10^(9-6) decimal shift. +const TICK_BAND_TOP_START = 0; + +type Entry = { label: string; key: PublicKey }; + +async function buildAddressList(connection: Connection): Promise { + const [globalConfigInfo, poolInfo] = await connection.getMultipleAccountsInfo( + [PUMP_AMM_GLOBAL_CONFIG, USDC_SWAP_POOL], + ); + if (!globalConfigInfo) throw new Error("pump global config not found"); + if (!poolInfo) throw new Error("whirlpool USDC_SWAP_POOL not found"); + + const { protocolFeeRecipients, buybackFeeRecipients } = parsePumpGlobalConfig( + globalConfigInfo.data, + ); + const pool = parseWhirlpool(poolInfo.data); + + if ( + !pool.tokenMintA.equals(NATIVE_MINT) || + !pool.tokenMintB.equals(MAINNET_USDC) + ) { + throw new Error("USDC_SWAP_POOL mints are not WSOL/USDC"); + } + + const entries: Entry[] = []; + const push = (label: string, key: PublicKey) => entries.push({ label, key }); + + // A. Relaunch protocol + push("relaunch program", RELAUNCH_V0_1_PROGRAM_ID); + push( + "relaunch event authority", + PublicKey.findProgramAddressSync( + [Buffer.from("__event_authority")], + RELAUNCH_V0_1_PROGRAM_ID, + )[0], + ); + + // B. Core programs, sysvars, mints + push("system program", SystemProgram.programId); + push("token program", TOKEN_PROGRAM_ID); + push("token-2022 program", TOKEN_2022_PROGRAM_ID); + push("associated token program", ASSOCIATED_TOKEN_PROGRAM_ID); + push("memo program", MEMO_PROGRAM_ID); + push("rent sysvar", SYSVAR_RENT_PUBKEY); + push("mpl token metadata program", MPL_TOKEN_METADATA_PROGRAM_ID); + push("WSOL mint", NATIVE_MINT); + push("USDC mint", MAINNET_USDC); + + // C. pump statics + push("pump_amm program", PUMP_AMM_PROGRAM_ID); + push("pump fees program", PUMP_FEES_PROGRAM_ID); + push("pump global config", PUMP_AMM_GLOBAL_CONFIG); + push("pump event authority", PUMP_AMM_EVENT_AUTHORITY); + push("pump fee config", PUMP_AMM_FEE_CONFIG); + push("pump global volume accumulator", PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR); + + // D/E. fee recipients + their quote-mint ATAs (config-order snapshot) + const pushRecipients = (kind: string, recipients: PublicKey[]) => { + recipients.forEach((recipient, i) => { + push(`${kind} fee recipient #${i + 1}`, recipient); + push( + `${kind} fee recipient #${i + 1} WSOL ATA`, + getAssociatedTokenAddressSync(NATIVE_MINT, recipient, true), + ); + push( + `${kind} fee recipient #${i + 1} USDC ATA`, + getAssociatedTokenAddressSync(MAINNET_USDC, recipient, true), + ); + }); + }; + pushRecipients("protocol", protocolFeeRecipients); + pushRecipients("buyback", buybackFeeRecipients); + + // F. whirlpool statics + push("whirlpool program", WHIRLPOOL_PROGRAM_ID); + push("USDC_SWAP_POOL", USDC_SWAP_POOL); + push("USDC_SWAP_POOL WSOL vault", pool.tokenVaultA); + push("USDC_SWAP_POOL USDC vault", pool.tokenVaultB); + push("USDC_SWAP_POOL oracle", getWhirlpoolOracleAddr(USDC_SWAP_POOL)); + + // G. futarchy + squads statics + push("futarchy program", FUTARCHY_V0_6_PROGRAM_ID); + push( + "futarchy event authority", + PublicKey.findProgramAddressSync( + [Buffer.from("__event_authority")], + FUTARCHY_V0_6_PROGRAM_ID, + )[0], + ); + push("squads program", SQUADS_PROGRAM_ID); + push("squads program config", SQUADS_PROGRAM_CONFIG); + push("squads program config treasury", SQUADS_PROGRAM_CONFIG_TREASURY); + + // H. whirlpool tick-array band + const span = 88 * pool.tickSpacing; + const currentStart = Math.floor(pool.tickCurrentIndex / span) * span; + if (currentStart > TICK_BAND_TOP_START) { + throw new Error( + `pool tick ${pool.tickCurrentIndex} is above the band top ($1,000/SOL); revisit the band bounds`, + ); + } + for ( + let start = currentStart - TICK_ARRAYS_BELOW_CURRENT * span; + start <= TICK_BAND_TOP_START; + start += span + ) { + push( + `tick array [${start}, ${start + span})`, + getWhirlpoolTickArrayAddr(USDC_SWAP_POOL, start), + ); + } + + const seen = new Set(); + for (const { label, key } of entries) { + const b58 = key.toBase58(); + if (seen.has(b58)) + throw new Error(`duplicate address in list: ${label} (${b58})`); + seen.add(b58); + } + if (entries.length > LOOKUP_TABLE_MAX_ADDRESSES) { + throw new Error( + `list has ${entries.length} entries, max is ${LOOKUP_TABLE_MAX_ADDRESSES}`, + ); + } + + return entries; +} + +function priorityFeeIx() { + return ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: parseInt(process.env.PRIORITY_FEE_MICRO_LAMPORTS ?? "10000"), + }); +} + +async function fetchTableAddresses(connection: Connection, table: PublicKey) { + const res = await connection.getAddressLookupTable(table); + if (!res.value) throw new Error(`lookup table ${table.toBase58()} not found`); + return res.value; +} + +/// Compares the on-chain table against the computed list. `asPrefix` allows +/// the on-chain table to be an incomplete prefix (for resuming extends). +function compareAddresses( + onChain: PublicKey[], + expected: Entry[], + asPrefix: boolean, +): { matches: boolean; problems: string[] } { + const problems: string[] = []; + if ( + asPrefix + ? onChain.length > expected.length + : onChain.length !== expected.length + ) { + problems.push( + `length mismatch: on-chain ${onChain.length}, expected ${expected.length}`, + ); + } + const upTo = Math.min(onChain.length, expected.length); + for (let i = 0; i < upTo; i++) { + if (!onChain[i].equals(expected[i].key)) { + problems.push( + `index ${i}: on-chain ${onChain[i].toBase58()}, expected ${expected[i].key.toBase58()} (${expected[i].label})`, + ); + } + } + return { matches: problems.length === 0, problems }; +} + +async function extendTable( + provider: anchor.AnchorProvider, + table: PublicKey, + entries: Entry[], + alreadyExtended: number, +) { + for (let i = alreadyExtended; i < entries.length; i += ADDRESSES_PER_EXTEND) { + const chunk = entries.slice(i, i + ADDRESSES_PER_EXTEND); + const ix = AddressLookupTableProgram.extendLookupTable({ + lookupTable: table, + authority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + addresses: chunk.map((e) => e.key), + }); + const signature = await provider.sendAndConfirm( + new Transaction().add(priorityFeeIx(), ix), + ); + console.log( + `extended ${i}..${i + chunk.length - 1} (${chunk[0].label} .. ${chunk[chunk.length - 1].label}): ${signature}`, + ); + } +} + +async function main() { + const args = process.argv.slice(2); + const command = args.find((a) => !a.startsWith("--")) ?? "plan"; + const tableArg = args.includes("--table") + ? new PublicKey(args[args.indexOf("--table") + 1]) + : undefined; + + const provider = anchor.AnchorProvider.env(); + const connection = provider.connection; + + const genesisHash = await connection.getGenesisHash(); + if ( + genesisHash !== MAINNET_GENESIS_HASH && + !args.includes("--allow-non-mainnet") + ) { + throw new Error( + `RPC is not mainnet (genesis ${genesisHash}); pass --allow-non-mainnet to override`, + ); + } + + const entries = await buildAddressList(connection); + const rent = await connection.getMinimumBalanceForRentExemption( + 56 + 32 * entries.length, + ); + console.log( + `computed ${entries.length} addresses (${LOOKUP_TABLE_MAX_ADDRESSES - entries.length} slots spare); rent ${rent / 1e9} SOL\n`, + ); + + if (command === "plan") { + entries.forEach((e, i) => + console.log(String(i).padStart(3), e.key.toBase58(), e.label), + ); + console.log( + `\ndry run only. Next: yarn relaunch-create-alt create (then verify, then freeze).`, + ); + return; + } + + if (command === "create") { + let table = tableArg; + let alreadyExtended = 0; + if (table) { + const existing = await fetchTableAddresses(connection, table); + if (!existing.state.authority?.equals(provider.wallet.publicKey)) { + throw new Error( + "wallet is not the table authority (or table is frozen)", + ); + } + const { matches, problems } = compareAddresses( + existing.state.addresses, + entries, + true, + ); + if (!matches) { + throw new Error( + `existing table is not a prefix of the computed list:\n${problems.join("\n")}\n` + + `(if groups D/E/H changed on-chain since the table was created, start a fresh table)`, + ); + } + alreadyExtended = existing.state.addresses.length; + console.log(`resuming ${table.toBase58()} at index ${alreadyExtended}`); + } else { + const recentSlot = await connection.getSlot("finalized"); + const [createIx, tableAddr] = AddressLookupTableProgram.createLookupTable( + { + authority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + recentSlot, + }, + ); + table = tableAddr; + const signature = await provider.sendAndConfirm( + new Transaction().add(priorityFeeIx(), createIx), + ); + console.log(`created lookup table ${table.toBase58()}: ${signature}`); + } + + await extendTable(provider, table, entries, alreadyExtended); + + const final = await fetchTableAddresses(connection, table); + const { matches, problems } = compareAddresses( + final.state.addresses, + entries, + false, + ); + if (!matches) + throw new Error( + `post-extend verification FAILED:\n${problems.join("\n")}`, + ); + console.log( + `\nverified: ${table.toBase58()} holds all ${entries.length} addresses in order.\n` + + `Table is usable one slot after the last extend. When satisfied:\n` + + ` yarn relaunch-create-alt freeze --table ${table.toBase58()}`, + ); + return; + } + + if (command === "verify" || command === "freeze") { + if (!tableArg) throw new Error(`${command} requires --table

`); + const account = await fetchTableAddresses(connection, tableArg); + const { matches, problems } = compareAddresses( + account.state.addresses, + entries, + false, + ); + const frozen = !account.state.authority; + console.log( + `table ${tableArg.toBase58()}: ${account.state.addresses.length} addresses, ` + + (frozen ? "FROZEN" : `authority ${account.state.authority.toBase58()}`), + ); + if (!matches) { + console.log(`MISMATCH vs computed list:\n${problems.join("\n")}`); + if (command === "verify") process.exitCode = 1; + if (command === "freeze") + throw new Error("refusing to freeze a mismatched table"); + return; + } + console.log("contents match the computed list exactly."); + if (command === "verify") return; + + if (frozen) { + console.log("table is already frozen; nothing to do."); + return; + } + if (!args.includes("--yes")) { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + const answer = await rl.question( + "Freezing is PERMANENT: the table can never be extended, closed, or edited, " + + "and its rent is locked forever.\nType FREEZE to continue: ", + ); + rl.close(); + if (answer.trim() !== "FREEZE") { + console.log("aborted."); + return; + } + } + const freezeIx = AddressLookupTableProgram.freezeLookupTable({ + lookupTable: tableArg, + authority: provider.wallet.publicKey, + }); + const signature = await provider.sendAndConfirm( + new Transaction().add(priorityFeeIx(), freezeIx), + ); + const after = await fetchTableAddresses(connection, tableArg); + if (after.state.authority) + throw new Error( + "freeze transaction landed but table still has an authority?", + ); + console.log( + `frozen: ${signature}\n` + + `Pin ${tableArg.toBase58()} in sdk/src/constants.ts (RELAUNCH_V0_1_GLOBAL_ALT).`, + ); + return; + } + + if (command === "dump") { + if (!tableArg) throw new Error("dump requires --table
"); + const outIdx = args.indexOf("--out"); + if (outIdx === -1) throw new Error("dump requires --out "); + const outPath = args[outIdx + 1]; + + const info = await connection.getAccountInfo(tableArg); + if (!info) throw new Error(`lookup table ${tableArg.toBase58()} not found`); + if (!info.owner.equals(AddressLookupTableProgram.programId)) { + throw new Error( + `account is owned by ${info.owner.toBase58()}, not the lookup table program`, + ); + } + + const account = await fetchTableAddresses(connection, tableArg); + const { matches, problems } = compareAddresses( + account.state.addresses, + entries, + false, + ); + if (!matches) + throw new Error( + `table does not match the computed list:\n${problems.join("\n")}`, + ); + if (account.state.authority) { + throw new Error( + "table is not frozen; freeze it first so the fixture mirrors the final shape", + ); + } + + // Meta layout: u32 discriminant, u64 deactivation_slot, u64 + // last_extended_slot, u8 last_extended_slot_start_index, Option + // authority, u16 padding. Zero the last-extended fields: entries only + // activate on slots past last_extended_slot, and bankrun clocks start + // near slot 0 — far below the slot this table was extended at. + const data = Buffer.from(info.data); + data.writeBigUInt64LE(0n, 12); + data.writeUInt8(0, 20); + + const fs = await import("fs"); + fs.writeFileSync(outPath, data); + console.log( + `wrote ${data.length} bytes (${account.state.addresses.length} addresses, frozen) to ${outPath}\n` + + `load in bankrun with owner ${AddressLookupTableProgram.programId.toBase58()} ` + + `and lamports ${info.lamports} at address ${tableArg.toBase58()}`, + ); + return; + } + + throw new Error( + `unknown command "${command}" (expected plan | create | verify | freeze | dump)`, + ); +} + +main().then( + () => process.exit(), + (err) => { + console.error(err); + process.exit(1); + }, +); diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index 3a3070de0..3dbe5ec3e 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -72,6 +72,15 @@ export const RELAUNCH_V0_1_PROGRAM_ID = new PublicKey( "vaMpdXN2P3Z5v8y6GtAU5NzCUjxtphnRVpvqu37Spik", ); +// The relaunch global frozen address lookup table (contents documented in +// vibes/relaunch-alt-contents.html, created by `yarn relaunch-create-alt`). +// PLACEHOLDER: this is the table from the 2026-08-11 surfpool dry run, which +// tests/fixtures/relaunch-global-alt mirrors byte-for-byte. Replace with the +// real table address once it is created and frozen on mainnet. +export const RELAUNCH_V0_1_GLOBAL_ALT = new PublicKey( + "4Wer9Wo1me9kzQewyzc4bEKw3qgVjUFuagcs65mujdoS", +); + export const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey( "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", ); diff --git a/tests/fixtures/relaunch-global-alt b/tests/fixtures/relaunch-global-alt new file mode 100644 index 0000000000000000000000000000000000000000..1bc656bd70604b9885b0c7609de1f048501e66a4 GIT binary patch literal 5880 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 { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount( + address, + { ...raw, data: Buffer.from(raw.data) } as any, + tokenProgram, + ).amount; +} + +async function lamports( + banksClient: BanksClient, + address: PublicKey, +): Promise { + return BigInt((await banksClient.getAccount(address))!.lamports); +} + +// The constant-product input for an exact-output buy, before fees. +function grossQuoteIn(baseOut: bigint): bigint { + return (WSOL_POOL_QUOTE_RESERVE * baseOut) / (POOL_BASE_RESERVE - baseOut); +} + +export default function suite() { + let client: RelaunchClient; + let globalAlt: AddressLookupTableAccount; + let protocolFeeRecipient: PublicKey; + let buybackFeeRecipient: PublicKey; + + before(async function () { + client = this.relaunch; + protocolFeeRecipient = await getProtocolFeeRecipient(this.banksClient); + buybackFeeRecipient = (await getBuybackFeeRecipients(this.banksClient))[0]; + // Fetched the way a script would fetch it: the harness connection runs + // web3.js's real getAddressLookupTable against bankrun state. + globalAlt = ( + await this.connection.getAddressLookupTable(RELAUNCH_V0_1_GLOBAL_ALT) + ).value!; + }); + + const setupLiveRelaunch = async function (this: Mocha.Context): Promise<{ + relaunch: PublicKey; + pool: PumpPool; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: setup.oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + await client.startDepositsIx({ relaunch }).rpc(); + + return { ...setup, pool, relaunch }; + }; + + const fundSol = async function ( + this: Mocha.Context, + to: PublicKey, + amount: bigint, + ) { + const tx = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: this.payer.publicKey, + toPubkey: to, + lamports: Number(amount), + }), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + }; + + // The full single-transaction buy flow for a depositor holding native SOL: + // wrap instructions, the buy, and an unwrap of the refund. + const wrapBuyUnwrapIxs = async ({ + relaunch, + pool, + depositor, + payer, + }: { + relaunch: PublicKey; + pool: PumpPool; + depositor: PublicKey; + payer: PublicKey; + }) => { + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + depositor, + ); + return [ + // .instruction() drops the builder's compute-budget preInstruction, so + // the assembled transaction states its own 400k limit. + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + token.createAssociatedTokenAccountIdempotentInstruction( + depositor, + wsolAta, + depositor, + token.NATIVE_MINT, + ), + SystemProgram.transfer({ + fromPubkey: depositor, + toPubkey: wsolAta, + lamports: Number(MAX_QUOTE_IN), + }), + token.createSyncNativeInstruction(wsolAta), + await client + .depositViaBuyIx({ + relaunch, + oldMint: pool.baseMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + sourceQuoteMint: pool.quoteMint, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient, + buybackFeeRecipient, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN(MAX_QUOTE_IN.toString()), + depositor, + payer, + }) + .instruction(), + token.createCloseAccountInstruction(wsolAta, depositor, depositor), + ]; + }; + + it("deposit_via_buy: wrap, buy, and unwrap the refund in one atomic transaction", async function () { + const { relaunch, pool } = await setupLiveRelaunch.call(this); + const depositor = Keypair.generate(); + await fundSol.call(this, depositor.publicKey, 5n * 10n ** 9n); + + const ixs = await wrapBuyUnwrapIxs({ + relaunch, + pool, + depositor: depositor.publicKey, + payer: depositor.publicKey, + }); + + // As a legacy transaction this instruction list does not fit — which is + // why, without the ALT, the wrap rides a separate (non-atomic) + // preparatory transaction. + const legacyTx = new Transaction().add(...ixs); + legacyTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + legacyTx.feePayer = depositor.publicKey; + legacyTx.sign(depositor); + assert.throws(() => legacyTx.serialize(), /too large/i); + + const solBefore = await lamports(this.banksClient, depositor.publicKey); + const tx = await buildV0Tx({ + banksClient: this.banksClient, + payerKey: depositor.publicKey, + instructions: ixs, + signers: [depositor], + tables: [globalAlt], + }); + assert.isAtMost(tx.serialize().length, 1232); + await this.banksClient.processTransaction(tx); + + const record = await client.getDepositRecord({ + relaunch, + depositor: depositor.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const vaultBalance = await tokenBalance( + this.banksClient, + (await client.fetchRelaunch(relaunch)).oldTokenVault, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The refund came back as native SOL: the WSOL ATA is gone, and the + // depositor's lamport outflow is the buy's cost (plus fee and the + // deposit-record/volume-accumulator rents), well under the 2 SOL cap. + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + depositor.publicKey, + ); + assert.isNull(await this.banksClient.getAccount(wsolAta)); + const outflow = + solBefore - (await lamports(this.banksClient, depositor.publicKey)); + assert.isTrue(outflow >= grossQuoteIn(BASE_OUT) && outflow < MAX_QUOTE_IN); + }); + + it("deposit_via_buy: sponsor pays fees and rent, depositor only spends the quote", async function () { + const { relaunch, pool } = await setupLiveRelaunch.call(this); + const sponsor = Keypair.generate(); + const depositor = Keypair.generate(); + await fundSol.call(this, sponsor.publicKey, 10n ** 9n); + await fundSol.call(this, depositor.publicKey, 3n * 10n ** 9n); + + const ixs = await wrapBuyUnwrapIxs({ + relaunch, + pool, + depositor: depositor.publicKey, + payer: sponsor.publicKey, + }); + + // Two distinct signers push the legacy encoding even further past the + // limit: this flow does not exist without the ALT. + const legacyTx = new Transaction().add(...ixs); + legacyTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + legacyTx.feePayer = sponsor.publicKey; + legacyTx.sign(sponsor, depositor); + assert.throws(() => legacyTx.serialize(), /too large/i); + + const sponsorBefore = await lamports(this.banksClient, sponsor.publicKey); + const depositorBefore = await lamports( + this.banksClient, + depositor.publicKey, + ); + const tx = await buildV0Tx({ + banksClient: this.banksClient, + payerKey: sponsor.publicKey, + instructions: ixs, + signers: [sponsor, depositor], + tables: [globalAlt], + }); + assert.isAtMost(tx.serialize().length, 1232); + await this.banksClient.processTransaction(tx); + + const record = await client.getDepositRecord({ + relaunch, + depositor: depositor.publicKey, + }); + assert.isTrue(record.depositor.equals(depositor.publicKey)); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + // The depositor paid exactly the buy's cost: no transaction fee, no + // record rent, and the wrap ATA's rent round-tripped back on close. The + // sponsor covered the rest. + const spent = + depositorBefore - (await lamports(this.banksClient, depositor.publicKey)); + const gross = grossQuoteIn(BASE_OUT); + assert.isTrue(spent >= gross && spent < (gross * 103n) / 100n); + assert.isTrue( + (await lamports(this.banksClient, sponsor.publicKey)) < sponsorBefore, + ); + }); + + it("execute_sell: built as a v0 transaction with the ALT", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + // 100M tokens meets the 10% threshold, so closing lands in SellPending. + const depositAmount = DEFAULT_OLD_SUPPLY / 10n; + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + amount: new BN(depositAmount.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + + // 90% of the constant-product output as the sell's slippage floor. + const minQuoteOut = + (((WSOL_POOL_QUOTE_RESERVE * depositAmount) / + (POOL_BASE_RESERVE + depositAmount)) * + 90n) / + 100n; + + const ixs = [ + ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + await client + .executeSellIx({ + relaunch, + oldMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + sourceQuoteMint: pool.quoteMint, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.poolBaseTokenAccount, + poolQuoteTokenAccount: pool.poolQuoteTokenAccount, + coinCreator: pool.coinCreator, + protocolFeeRecipient, + buybackFeeRecipient, + minQuoteOut: new BN(minQuoteOut.toString()), + }) + .instruction(), + ]; + + // Unlike the deposit flows this leg is not size-blocked; the example + // documents the assembly pattern, which applies unchanged to the other + // one-shot legs (execute_usdc_swap, complete_relaunch). + const tx = await buildV0Tx({ + banksClient: this.banksClient, + payerKey: this.payer.publicKey, + instructions: ixs, + signers: [this.payer], + tables: [globalAlt], + }); + await this.banksClient.processTransaction(tx); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + assert.isTrue( + storedRelaunch.quoteRecovered.gte(new BN(minQuoteOut.toString())), + ); + }); +} diff --git a/tests/relaunch/utils.ts b/tests/relaunch/utils.ts index f4d20413b..ed4edaaa0 100644 --- a/tests/relaunch/utils.ts +++ b/tests/relaunch/utils.ts @@ -1,9 +1,13 @@ import { + AddressLookupTableAccount, Keypair, PublicKey, Signer, SystemProgram, Transaction, + TransactionInstruction, + TransactionMessage, + VersionedTransaction, } from "@solana/web3.js"; import * as token from "@solana/spl-token"; import { BanksClient } from "solana-bankrun"; @@ -11,6 +15,32 @@ import { BanksClient } from "solana-bankrun"; // 1B tokens at 6 decimals — the supply of a pump token. export const DEFAULT_OLD_SUPPLY = 1_000_000_000n * 10n ** 6n; +// Compiles instructions into a signed v0 transaction, resolving every +// account it can through the given lookup tables. +export async function buildV0Tx({ + banksClient, + payerKey, + instructions, + signers, + tables, +}: { + banksClient: BanksClient; + payerKey: PublicKey; + instructions: TransactionInstruction[]; + signers: Keypair[]; + tables: AddressLookupTableAccount[]; +}): Promise { + const [blockhash] = (await banksClient.getLatestBlockhash())!; + const message = new TransactionMessage({ + payerKey, + recentBlockhash: blockhash, + instructions, + }).compileToV0Message(tables); + const tx = new VersionedTransaction(message); + tx.sign(signers); + return tx; +} + // Classic SPL mints are plain; Token-2022 mints get the pump-style shape: // metadata pointer + mint-embedded token metadata, the only extensions the // program's allowlist accepts. From 70aaadd7187e229cd5d6b5e2b01865ccbe2b44b1 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 11:38:18 -0700 Subject: [PATCH 22/31] compuite budget --- sdk/src/relaunch/v0.1/RelaunchClient.ts | 19 ++++--------------- tests/relaunch/unit/altTransactions.test.ts | 6 ++---- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 0a1b62b8d..5b1075b89 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -370,9 +370,7 @@ export class RelaunchClient { systemProgram: SystemProgram.programId, }) .preInstructions([ - // The buy does the sell's work plus volume-accumulator handling and - // the pull/refund transfers; give it fixed headroom over 200k. - ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ]); } @@ -462,9 +460,7 @@ export class RelaunchClient { systemProgram: SystemProgram.programId, }) .preInstructions([ - // The sell runs close to the 200k default, and PDA derivation costs - // vary with the bump seeds; give it fixed headroom. - ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }), ]); } @@ -518,12 +514,7 @@ export class RelaunchClient { memoProgram: MEMO_PROGRAM_ID, whirlpoolProgram: WHIRLPOOL_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, - }) - .preInstructions([ - // The whirlpool swap's cost grows with the initialized ticks it - // crosses; give it fixed headroom over the 200k default. - ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), - ]); + }); } // Swaps the whole WSOL vault to USDC as the admin (the provider wallet), @@ -650,9 +641,7 @@ export class RelaunchClient { systemProgram: SystemProgram.programId, }) .preInstructions([ - // DAO init + Squads multisig creation + AMM seeding in one tx run - // just shy of 400k; give the budget real headroom. - ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000 }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ]); } diff --git a/tests/relaunch/unit/altTransactions.test.ts b/tests/relaunch/unit/altTransactions.test.ts index a83d6f62d..ac39c8cd3 100644 --- a/tests/relaunch/unit/altTransactions.test.ts +++ b/tests/relaunch/unit/altTransactions.test.ts @@ -151,9 +151,7 @@ export default function suite() { depositor, ); return [ - // .instruction() drops the builder's compute-budget preInstruction, so - // the assembled transaction states its own 400k limit. - ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 350_000 }), token.createAssociatedTokenAccountIdempotentInstruction( depositor, wsolAta, @@ -324,7 +322,7 @@ export default function suite() { 100n; const ixs = [ - ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }), await client .executeSellIx({ relaunch, From e7b34a6531d6166ec0c3b0f3dd00194a51baf519 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 12:16:50 -0700 Subject: [PATCH 23/31] refactor deposit record credit logic --- programs/relaunch/src/instructions/deposit.rs | 23 +++++-------------- .../src/instructions/deposit_via_buy.rs | 23 +++++-------------- programs/relaunch/src/state/deposit_record.rs | 21 +++++++++++++++++ 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/programs/relaunch/src/instructions/deposit.rs b/programs/relaunch/src/instructions/deposit.rs index 1f495ad9c..e38efd0f0 100644 --- a/programs/relaunch/src/instructions/deposit.rs +++ b/programs/relaunch/src/instructions/deposit.rs @@ -93,23 +93,12 @@ impl Deposit<'_> { ctx.accounts.old_mint.decimals, )?; - let deposit_record = &mut ctx.accounts.deposit_record; - - // A fresh record holds the default pubkey, an existing one - // holds the depositor's pubkey. - if deposit_record.depositor == ctx.accounts.depositor.key() { - deposit_record.amount_deposited += args.amount; - deposit_record.seq_num += 1; - } else { - deposit_record.set_inner(DepositRecord { - relaunch: ctx.accounts.relaunch.key(), - depositor: ctx.accounts.depositor.key(), - amount_deposited: args.amount, - claimed: false, - seq_num: 0, - pda_bump: ctx.bumps.deposit_record, - }); - } + ctx.accounts.deposit_record.credit( + ctx.accounts.relaunch.key(), + ctx.accounts.depositor.key(), + args.amount, + ctx.bumps.deposit_record, + ); let relaunch = &mut ctx.accounts.relaunch; relaunch.total_deposited += args.amount; diff --git a/programs/relaunch/src/instructions/deposit_via_buy.rs b/programs/relaunch/src/instructions/deposit_via_buy.rs index 4b7ec5996..2d748b9a0 100644 --- a/programs/relaunch/src/instructions/deposit_via_buy.rs +++ b/programs/relaunch/src/instructions/deposit_via_buy.rs @@ -282,23 +282,12 @@ impl DepositViaBuy<'_> { )?; } - let deposit_record = &mut ctx.accounts.deposit_record; - - // A fresh record holds the default pubkey, an existing one - // holds the depositor's pubkey. - if deposit_record.depositor == ctx.accounts.depositor.key() { - deposit_record.amount_deposited += tokens_bought; - deposit_record.seq_num += 1; - } else { - deposit_record.set_inner(DepositRecord { - relaunch: relaunch_key, - depositor: ctx.accounts.depositor.key(), - amount_deposited: tokens_bought, - claimed: false, - seq_num: 0, - pda_bump: ctx.bumps.deposit_record, - }); - } + ctx.accounts.deposit_record.credit( + relaunch_key, + ctx.accounts.depositor.key(), + tokens_bought, + ctx.bumps.deposit_record, + ); let relaunch = &mut ctx.accounts.relaunch; relaunch.total_deposited += tokens_bought; diff --git a/programs/relaunch/src/state/deposit_record.rs b/programs/relaunch/src/state/deposit_record.rs index 1faddf6b3..4d7f98671 100644 --- a/programs/relaunch/src/state/deposit_record.rs +++ b/programs/relaunch/src/state/deposit_record.rs @@ -17,3 +17,24 @@ pub struct DepositRecord { /// The PDA bump. pub pda_bump: u8, } + +impl DepositRecord { + /// Credits a deposit, initializing the record on first use: a fresh + /// record holds the default pubkey, an existing one holds the + /// depositor's pubkey. + pub fn credit(&mut self, relaunch: Pubkey, depositor: Pubkey, amount: u64, pda_bump: u8) { + if self.depositor == depositor { + self.amount_deposited += amount; + self.seq_num += 1; + } else { + *self = DepositRecord { + relaunch, + depositor, + amount_deposited: amount, + claimed: false, + seq_num: 0, + pda_bump, + }; + } + } +} From 5d7a19c4cb367ddca45c6ef012efe6d02c341748 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 12:50:12 -0700 Subject: [PATCH 24/31] sdk refactor --- sdk/src/relaunch/v0.1/RelaunchClient.ts | 59 ++++++--------------- sdk/src/relaunch/v0.1/pumpAmm.ts | 46 +++++++++++++++- sdk/src/relaunch/v0.1/whirlpool.ts | 14 ++++- tests/relaunch/pumpAmm.ts | 23 ++------ tests/relaunch/unit/altTransactions.test.ts | 19 +++---- tests/relaunch/unit/depositViaBuy.test.ts | 14 ++--- tests/relaunch/unit/executeSell.test.ts | 22 ++++---- tests/relaunch/unit/venues.test.ts | 20 +++---- 8 files changed, 109 insertions(+), 108 deletions(-) diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 5b1075b89..798e6d85f 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -46,21 +46,21 @@ import { getDepositRecordAddr, } from "./pda.js"; import { + fetchPumpPool, getPumpCreatorVaultAuthorityAddr, + getPumpFeeRecipients, getPumpPoolV2Addr, getPumpUserVolumeAccumulatorAddr, - parsePumpGlobalConfig, - parsePumpPool, PUMP_AMM_EVENT_AUTHORITY, PUMP_AMM_FEE_CONFIG, PUMP_AMM_GLOBAL_CONFIG, PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, } from "./pumpAmm.js"; import { + fetchWhirlpool, getWhirlpoolOracleAddr, getWhirlpoolSwapTickArrayAddrs, MEMO_PROGRAM_ID, - parseWhirlpool, USDC_SWAP_POOL, } from "./whirlpool.js"; import { getEventAuthorityAddr, getMetadataAddr } from "../../pda.js"; @@ -530,12 +530,7 @@ export class RelaunchClient { minUsdcOut?: BN; slippageBps?: number; }): Promise { - const whirlpoolAccount = - await this.provider.connection.getAccountInfo(USDC_SWAP_POOL); - if (whirlpoolAccount === null) { - throw new Error(`whirlpool ${USDC_SWAP_POOL.toBase58()} does not exist`); - } - const whirlpool = parseWhirlpool(whirlpoolAccount.data); + const whirlpool = await fetchWhirlpool(this.provider.connection); if (minUsdcOut === undefined) { const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); @@ -826,23 +821,12 @@ export class RelaunchClient { ); } - const poolAccount = await this.provider.connection.getAccountInfo( + const pool = await fetchPumpPool( + this.provider.connection, storedRelaunch.sourcePool, ); - if (poolAccount === null) { - throw new Error( - `source pool ${storedRelaunch.sourcePool.toBase58()} does not exist`, - ); - } - const pool = parsePumpPool(poolAccount.data); - - const globalConfigAccount = await this.provider.connection.getAccountInfo( - PUMP_AMM_GLOBAL_CONFIG, - ); - if (globalConfigAccount === null) { - throw new Error("pump_amm global config does not exist"); - } - const globalConfig = parsePumpGlobalConfig(globalConfigAccount.data); + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.provider.connection); if (maxQuoteIn === undefined) { const [baseReserve, quoteReserve] = await Promise.all( @@ -907,8 +891,8 @@ export class RelaunchClient { poolBaseTokenAccount: pool.poolBaseTokenAccount, poolQuoteTokenAccount: pool.poolQuoteTokenAccount, coinCreator: pool.coinCreator, - protocolFeeRecipient: globalConfig.protocolFeeRecipients[0], - buybackFeeRecipient: globalConfig.buybackFeeRecipients[0], + protocolFeeRecipient, + buybackFeeRecipient, baseOut, maxQuoteIn, }).rpc(); @@ -942,23 +926,12 @@ export class RelaunchClient { ); } - const poolAccount = await this.provider.connection.getAccountInfo( + const pool = await fetchPumpPool( + this.provider.connection, storedRelaunch.sourcePool, ); - if (poolAccount === null) { - throw new Error( - `source pool ${storedRelaunch.sourcePool.toBase58()} does not exist`, - ); - } - const pool = parsePumpPool(poolAccount.data); - - const globalConfigAccount = await this.provider.connection.getAccountInfo( - PUMP_AMM_GLOBAL_CONFIG, - ); - if (globalConfigAccount === null) { - throw new Error("pump_amm global config does not exist"); - } - const globalConfig = parsePumpGlobalConfig(globalConfigAccount.data); + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.provider.connection); if (minQuoteOut === undefined) { const [baseIn, baseReserve, quoteReserve] = await Promise.all( @@ -982,8 +955,8 @@ export class RelaunchClient { poolBaseTokenAccount: pool.poolBaseTokenAccount, poolQuoteTokenAccount: pool.poolQuoteTokenAccount, coinCreator: pool.coinCreator, - protocolFeeRecipient: globalConfig.protocolFeeRecipients[0], - buybackFeeRecipient: globalConfig.buybackFeeRecipients[0], + protocolFeeRecipient, + buybackFeeRecipient, minQuoteOut, }).rpc(); } diff --git a/sdk/src/relaunch/v0.1/pumpAmm.ts b/sdk/src/relaunch/v0.1/pumpAmm.ts index c17f62320..fad0d5fa7 100644 --- a/sdk/src/relaunch/v0.1/pumpAmm.ts +++ b/sdk/src/relaunch/v0.1/pumpAmm.ts @@ -1,4 +1,4 @@ -import { PublicKey } from "@solana/web3.js"; +import { Connection, PublicKey } from "@solana/web3.js"; import { PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID } from "../../constants.js"; export const PUMP_AMM_GLOBAL_CONFIG = PublicKey.findProgramAddressSync( @@ -77,6 +77,18 @@ export function parsePumpPool(data: Buffer): PumpPoolAccount { }; } +/** Fetches and parses `pool` as a pump_amm pool account. */ +export async function fetchPumpPool( + connection: Connection, + pool: PublicKey, +): Promise { + const info = await connection.getAccountInfo(pool); + if (info === null) { + throw new Error(`pump pool ${pool.toBase58()} does not exist`); + } + return parsePumpPool(info.data); +} + export type PumpGlobalConfigAccount = { protocolFeeRecipients: PublicKey[]; buybackFeeRecipients: PublicKey[]; @@ -123,3 +135,35 @@ export function parsePumpGlobalConfig(data: Buffer): PumpGlobalConfigAccount { buybackFeeRecipients: readRecipients(data, 643), }; } + +/** Fetches and parses pump_amm's global config. */ +export async function fetchPumpGlobalConfig( + connection: Connection, +): Promise { + const info = await connection.getAccountInfo(PUMP_AMM_GLOBAL_CONFIG); + if (info === null) { + throw new Error("pump_amm global config does not exist"); + } + return parsePumpGlobalConfig(info.data); +} + +/** + * The fee-recipient pair pump's buy and sell instructions need, resolved + * from the global config. pump accepts any member of each list; this picks + * the first of both — pass a different member to the ix builders if + * write-lock contention on the recipients' ATAs matters. + */ +export async function getPumpFeeRecipients(connection: Connection): Promise<{ + protocolFeeRecipient: PublicKey; + buybackFeeRecipient: PublicKey; +}> { + const { protocolFeeRecipients, buybackFeeRecipients } = + await fetchPumpGlobalConfig(connection); + if (protocolFeeRecipients.length === 0 || buybackFeeRecipients.length === 0) { + throw new Error("pump_amm global config has no fee recipients"); + } + return { + protocolFeeRecipient: protocolFeeRecipients[0], + buybackFeeRecipient: buybackFeeRecipients[0], + }; +} diff --git a/sdk/src/relaunch/v0.1/whirlpool.ts b/sdk/src/relaunch/v0.1/whirlpool.ts index 23fc8f26a..5cd6dfe7f 100644 --- a/sdk/src/relaunch/v0.1/whirlpool.ts +++ b/sdk/src/relaunch/v0.1/whirlpool.ts @@ -1,4 +1,4 @@ -import { PublicKey } from "@solana/web3.js"; +import { Connection, PublicKey } from "@solana/web3.js"; import { WHIRLPOOL_PROGRAM_ID } from "../../constants.js"; // Orca Whirlpool SOL/USDC 0.04% — the swap venue pinned by the relaunch @@ -92,3 +92,15 @@ export function parseWhirlpool(data: Buffer): WhirlpoolAccount { tokenVaultB: new PublicKey(data.subarray(213, 245)), }; } + +/** Fetches and parses a whirlpool (the pinned USDC swap pool by default). */ +export async function fetchWhirlpool( + connection: Connection, + whirlpool: PublicKey = USDC_SWAP_POOL, +): Promise { + const info = await connection.getAccountInfo(whirlpool); + if (info === null) { + throw new Error(`whirlpool ${whirlpool.toBase58()} does not exist`); + } + return parseWhirlpool(info.data); +} diff --git a/tests/relaunch/pumpAmm.ts b/tests/relaunch/pumpAmm.ts index e6ebf7248..e55d5a32b 100644 --- a/tests/relaunch/pumpAmm.ts +++ b/tests/relaunch/pumpAmm.ts @@ -90,22 +90,6 @@ async function fetchGlobalConfig( return parsePumpGlobalConfig(Buffer.from(globalConfig!.data)); } -export async function getProtocolFeeRecipient( - banksClient: BanksClient, -): Promise { - const { protocolFeeRecipients } = await fetchGlobalConfig(banksClient); - if (protocolFeeRecipients.length === 0) { - throw new Error("no protocol fee recipient in global config"); - } - return protocolFeeRecipients[0]; -} - -export async function getBuybackFeeRecipients( - banksClient: BanksClient, -): Promise { - return (await fetchGlobalConfig(banksClient)).buybackFeeRecipients; -} - function packTokenAccount({ mint, owner, @@ -311,10 +295,9 @@ export async function writePumpPool({ amount: 0n, }); } - for (const recipient of [ - await getProtocolFeeRecipient(context.banksClient), - ...(await getBuybackFeeRecipients(context.banksClient)), - ]) { + const { protocolFeeRecipients, buybackFeeRecipients } = + await fetchGlobalConfig(context.banksClient); + for (const recipient of [protocolFeeRecipients[0], ...buybackFeeRecipients]) { const recipientAta = token.getAssociatedTokenAddressSync( quoteMint, recipient, diff --git a/tests/relaunch/unit/altTransactions.test.ts b/tests/relaunch/unit/altTransactions.test.ts index ac39c8cd3..dc35dc25b 100644 --- a/tests/relaunch/unit/altTransactions.test.ts +++ b/tests/relaunch/unit/altTransactions.test.ts @@ -17,16 +17,12 @@ import { assert } from "chai"; import { BN } from "bn.js"; import { BanksClient } from "solana-bankrun"; import { + getPumpFeeRecipients, RELAUNCH_V0_1_GLOBAL_ALT, RelaunchClient, } from "@metadaoproject/programs"; import { buildV0Tx, setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; -import { - getBuybackFeeRecipients, - getProtocolFeeRecipient, - writePumpPool, - PumpPool, -} from "../pumpAmm.js"; +import { writePumpPool, PumpPool } from "../pumpAmm.js"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL @@ -71,8 +67,9 @@ export default function suite() { before(async function () { client = this.relaunch; - protocolFeeRecipient = await getProtocolFeeRecipient(this.banksClient); - buybackFeeRecipient = (await getBuybackFeeRecipients(this.banksClient))[0]; + ({ protocolFeeRecipient, buybackFeeRecipient } = await getPumpFeeRecipients( + this.connection, + )); // Fetched the way a script would fetch it: the harness connection runs // web3.js's real getAddressLookupTable against bankrun state. globalAlt = ( @@ -224,10 +221,8 @@ export default function suite() { }); assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); - const vaultBalance = await tokenBalance( - this.banksClient, - (await client.fetchRelaunch(relaunch)).oldTokenVault, - ); + const { oldTokenVault } = await client.fetchRelaunch(relaunch); + const vaultBalance = await tokenBalance(this.banksClient, oldTokenVault); assert.equal(vaultBalance.toString(), BASE_OUT.toString()); // The refund came back as native SOL: the WSOL ATA is gone, and the diff --git a/tests/relaunch/unit/depositViaBuy.test.ts b/tests/relaunch/unit/depositViaBuy.test.ts index 0ae54dc45..78c5603c6 100644 --- a/tests/relaunch/unit/depositViaBuy.test.ts +++ b/tests/relaunch/unit/depositViaBuy.test.ts @@ -5,13 +5,12 @@ import { BN } from "bn.js"; import { BanksClient } from "solana-bankrun"; import { getDepositRecordAddr, + getPumpFeeRecipients, MAINNET_USDC, RelaunchClient, } from "@metadaoproject/programs"; import { setupRelaunch } from "../utils.js"; import { - getBuybackFeeRecipients, - getProtocolFeeRecipient, getUserVolumeAccumulatorAddr, writePumpPool, PumpPool, @@ -55,8 +54,9 @@ export default function suite() { client = this.relaunch; // Both recipients come from the loaded global-config fixture, so one // fetch serves the whole suite. - protocolFeeRecipient = await getProtocolFeeRecipient(this.banksClient); - buybackFeeRecipient = (await getBuybackFeeRecipients(this.banksClient))[0]; + ({ protocolFeeRecipient, buybackFeeRecipient } = await getPumpFeeRecipients( + this.connection, + )); }); const setupLiveRelaunch = async function ( @@ -315,9 +315,10 @@ export default function suite() { }); assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + const { oldTokenVault } = await client.fetchRelaunch(relaunch); const vaultBalance = await tokenBalance( this.banksClient, - (await client.fetchRelaunch(relaunch)).oldTokenVault, + oldTokenVault, oldTokenProgram, ); assert.equal(vaultBalance.toString(), BASE_OUT.toString()); @@ -355,9 +356,10 @@ export default function suite() { }); assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + const { oldTokenVault } = await client.fetchRelaunch(relaunch); const vaultBalance = await tokenBalance( this.banksClient, - (await client.fetchRelaunch(relaunch)).oldTokenVault, + oldTokenVault, oldTokenProgram, ); assert.equal(vaultBalance.toString(), BASE_OUT.toString()); diff --git a/tests/relaunch/unit/executeSell.test.ts b/tests/relaunch/unit/executeSell.test.ts index 98f126a47..e763c75a6 100644 --- a/tests/relaunch/unit/executeSell.test.ts +++ b/tests/relaunch/unit/executeSell.test.ts @@ -3,14 +3,13 @@ import * as token from "@solana/spl-token"; import { assert } from "chai"; import { BN } from "bn.js"; import { BanksClient } from "solana-bankrun"; -import { MAINNET_USDC, RelaunchClient } from "@metadaoproject/programs"; -import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; import { - getBuybackFeeRecipients, - getProtocolFeeRecipient, - writePumpPool, - PumpPool, -} from "../pumpAmm.js"; + getPumpFeeRecipients, + MAINNET_USDC, + RelaunchClient, +} from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool, PumpPool } from "../pumpAmm.js"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL @@ -258,6 +257,9 @@ export default function suite() { await setupSellPendingRelaunch.call(this); const nonAdmin = Keypair.generate(); + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.connection); + try { await client .executeSellIx({ @@ -269,10 +271,8 @@ export default function suite() { poolBaseTokenAccount: pool.poolBaseTokenAccount, poolQuoteTokenAccount: pool.poolQuoteTokenAccount, coinCreator: pool.coinCreator, - protocolFeeRecipient: await getProtocolFeeRecipient(this.banksClient), - buybackFeeRecipient: ( - await getBuybackFeeRecipients(this.banksClient) - )[0], + protocolFeeRecipient, + buybackFeeRecipient, minQuoteOut: new BN(0), admin: nonAdmin.publicKey, }) diff --git a/tests/relaunch/unit/venues.test.ts b/tests/relaunch/unit/venues.test.ts index 9d698bebb..e2d80e406 100644 --- a/tests/relaunch/unit/venues.test.ts +++ b/tests/relaunch/unit/venues.test.ts @@ -5,9 +5,7 @@ import { BankrunProvider } from "anchor-bankrun"; import { BanksClient } from "solana-bankrun"; import { setupRelaunch } from "../utils.js"; import { - getBuybackFeeRecipients, getCanonicalPumpPoolAddr, - getProtocolFeeRecipient, getUserVolumeAccumulatorAddr, pumpBuyIx, pumpInitUserVolumeAccumulatorIx, @@ -23,6 +21,7 @@ import { } from "../whirlpool.js"; import { getPumpCreatorVaultAuthorityAddr, + getPumpFeeRecipients, MAINNET_USDC, } from "@metadaoproject/programs"; @@ -55,12 +54,8 @@ export default function suite() { pool: PumpPool, quoteTokenAccount: PublicKey, ) { - const protocolFeeRecipient = await getProtocolFeeRecipient( - this.banksClient, - ); - const buybackFeeRecipient = ( - await getBuybackFeeRecipients(this.banksClient) - )[0]; + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.connection); const feeAtas = [ protocolFeeRecipient, buybackFeeRecipient, @@ -168,9 +163,8 @@ export default function suite() { await sellIntoPumpPool.call(this, pool, wsolAta); // Exact-output buy of the same 10k tokens, capped by max_quote_in. - const protocolFeeRecipient = await getProtocolFeeRecipient( - this.banksClient, - ); + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.connection); await wrapSol(this.bankrunProvider, this.payer, 2n * 10n ** 9n); const payerOldTokenAccount = token.getAssociatedTokenAddressSync( @@ -204,9 +198,7 @@ export default function suite() { pool, user: this.payer.publicKey, protocolFeeRecipient, - buybackFeeRecipient: ( - await getBuybackFeeRecipients(this.banksClient) - )[0], + buybackFeeRecipient, baseAmountOut: SELL_AMOUNT, maxQuoteAmountIn: 2n * 10n ** 9n, }), From 49073164929bd8e0434e269063ba4ab7813be8d7 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 18:37:56 -0700 Subject: [PATCH 25/31] raydium fixtures --- Anchor.toml | 4 + programs/relaunch/src/constants.rs | 29 +++ programs/relaunch/src/lib.rs | 1 + programs/relaunch/src/raydium_amm.rs | 155 +++++++++++++++ tasks.md | 73 +++++++ tests/fixtures/raydium_amm.so | Bin 0 -> 1406384 bytes tests/main.test.ts | 5 + tests/relaunch/raydiumAmm.ts | 281 +++++++++++++++++++++++++++ tests/relaunch/unit/venues.test.ts | 247 +++++++++++++++++++++++ 9 files changed, 795 insertions(+) create mode 100644 programs/relaunch/src/raydium_amm.rs create mode 100644 tasks.md create mode 100644 tests/fixtures/raydium_amm.so create mode 100644 tests/relaunch/raydiumAmm.ts diff --git a/Anchor.toml b/Anchor.toml index e371f7eb6..85dedd1d5 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -106,3 +106,7 @@ program = "./tests/fixtures/pump_fees.so" [[test.genesis]] address = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc" program = "./tests/fixtures/whirlpool.so" + +[[test.genesis]] +address = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" +program = "./tests/fixtures/raydium_amm.so" diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index e21587846..71abaf0b9 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -79,6 +79,35 @@ pub mod pump_amm_fee_config { declare_id!("5PHirr8joyTMp9JMm6nW7hNDVyEYdkzDqazxPD7RaTjx"); } +// Raydium + +/// Raydium's legacy "Standard" AMM v4, where pre-PumpSwap pump graduations +/// live. +pub mod raydium_amm_program { + use anchor_lang::prelude::declare_id; + + declare_id!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); +} + +/// The global authority PDA over every AMM v4 vault: `["amm authority"]`. +pub mod raydium_amm_authority { + use anchor_lang::prelude::declare_id; + + declare_id!("5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1"); +} + +/// OpenBook v1. Stored as `market_program` by every orderbook-era AMM v4 +/// pool. All pump graduations have this as an orderbook. +pub mod openbook_program { + use anchor_lang::prelude::declare_id; + + declare_id!("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"); +} + +/// A Raydium source pool must have at least this much LP burned +/// (`lp_amount - lp_mint.supply`, raw units, 9 decimals). +pub const RAYDIUM_MIN_BURNED_LP: u64 = 4_000_000_000_000; + // Orca pub mod whirlpool_program { diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 8a1383a66..716f42ac6 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -6,6 +6,7 @@ pub mod error; pub mod events; pub mod instructions; pub mod pump_amm; +pub mod raydium_amm; pub mod state; pub mod whirlpool; diff --git a/programs/relaunch/src/raydium_amm.rs b/programs/relaunch/src/raydium_amm.rs new file mode 100644 index 000000000..1d6730db4 --- /dev/null +++ b/programs/relaunch/src/raydium_amm.rs @@ -0,0 +1,155 @@ +//! Types for reading Raydium AMM v4 accounts, plus hand-built CPI builders — +//! AMM v4 is a native pre-Anchor program with no crate we can depend on. The +//! V2 swap instructions (tags 16/17) skip the dead orderbook entirely: 8 +//! accounts, pool + vaults only, direction inferred from the source/ +//! destination account mints (verified against live mainnet swaps and a +//! surfpool rehearsal 2026-08-12). +use anchor_lang::prelude::*; +use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; +use anchor_lang::solana_program::program::invoke_signed; + +use crate::error::RelaunchError; +use crate::raydium_amm_program; + +/// AmmInfo is a fixed-size packed struct the AMM casts zero-copy. +/// No discriminator, so the exact length is the shape check. +pub const AMM_INFO_LEN: usize = 752; + +pub const SWAP_BASE_IN_V2_TAG: u8 = 16; +pub const SWAP_BASE_OUT_V2_TAG: u8 = 17; + +/// The subset of AmmInfo that validation and the swap instructions read. +pub struct RaydiumPool { + pub status: u64, // @ 0 + pub coin_vault: Pubkey, // @ 336 + pub pc_vault: Pubkey, // @ 368 + pub coin_mint: Pubkey, // @ 400 (WSOL on pump-migration pools) + pub pc_mint: Pubkey, // @ 432 (the token on pump-migration pools) + pub lp_mint: Pubkey, // @ 464 + pub market_program: Pubkey, // @ 560 (the orderbook fingerprint) + pub lp_amount: u64, // @ 720 (LP ever minted; burns don't decrement) +} + +impl RaydiumPool { + pub fn try_parse(data: &[u8]) -> Result { + require_eq!( + data.len(), + AMM_INFO_LEN, + RelaunchError::SourcePoolNotCanonical + ); + Ok(Self { + status: read_u64(data, 0), + coin_vault: read_pubkey(data, 336), + pc_vault: read_pubkey(data, 368), + coin_mint: read_pubkey(data, 400), + pc_mint: read_pubkey(data, 432), + lp_mint: read_pubkey(data, 464), + market_program: read_pubkey(data, 560), + lp_amount: read_u64(data, 720), + }) + } +} + +fn read_u64(data: &[u8], offset: usize) -> u64 { + u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap()) +} + +fn read_pubkey(data: &[u8], offset: usize) -> Pubkey { + Pubkey::new_from_array(data[offset..offset + 32].try_into().unwrap()) +} + +/// The accounts of AMM v4's V2 swap instructions, in instruction order, +/// identical for both legs. The AMM checks the vaults against pool state and +/// infers direction by matching the user source/destination mints against +/// coin/pc. +pub struct Swap<'info> { + pub token_program: AccountInfo<'info>, + pub amm: AccountInfo<'info>, + pub amm_authority: AccountInfo<'info>, + pub amm_coin_vault: AccountInfo<'info>, + pub amm_pc_vault: AccountInfo<'info>, + pub user_source_token_account: AccountInfo<'info>, + pub user_destination_token_account: AccountInfo<'info>, + pub user_source_owner: AccountInfo<'info>, +} + +/// Exact input, floor on output. The sell leg. +pub fn swap_base_in_v2( + accounts: Swap, + amount_in: u64, + minimum_amount_out: u64, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + invoke_swap( + accounts, + SWAP_BASE_IN_V2_TAG, + amount_in, + minimum_amount_out, + signer_seeds, + ) +} + +/// Exact output, cap on input. Only the needed input is pulled from the +/// source account. The buy leg. +pub fn swap_base_out_v2( + accounts: Swap, + max_amount_in: u64, + amount_out: u64, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + invoke_swap( + accounts, + SWAP_BASE_OUT_V2_TAG, + max_amount_in, + amount_out, + signer_seeds, + ) +} + +fn invoke_swap( + accounts: Swap, + tag: u8, + arg1: u64, + arg2: u64, + signer_seeds: &[&[&[u8]]], +) -> Result<()> { + let mut data = Vec::with_capacity(17); + data.push(tag); + data.extend_from_slice(&arg1.to_le_bytes()); + data.extend_from_slice(&arg2.to_le_bytes()); + + let metas = vec![ + AccountMeta::new_readonly(accounts.token_program.key(), false), + AccountMeta::new(accounts.amm.key(), false), + AccountMeta::new_readonly(accounts.amm_authority.key(), false), + AccountMeta::new(accounts.amm_coin_vault.key(), false), + AccountMeta::new(accounts.amm_pc_vault.key(), false), + AccountMeta::new(accounts.user_source_token_account.key(), false), + AccountMeta::new(accounts.user_destination_token_account.key(), false), + // Readonly signer. Raydium doesn't require the owner writable. + AccountMeta::new_readonly(accounts.user_source_owner.key(), true), + ]; + + let account_infos = [ + accounts.token_program, + accounts.amm, + accounts.amm_authority, + accounts.amm_coin_vault, + accounts.amm_pc_vault, + accounts.user_source_token_account, + accounts.user_destination_token_account, + accounts.user_source_owner, + ]; + + invoke_signed( + &Instruction { + program_id: raydium_amm_program::id(), + accounts: metas, + data, + }, + &account_infos, + signer_seeds, + )?; + + Ok(()) +} diff --git a/tasks.md b/tasks.md new file mode 100644 index 000000000..afae8dc9a --- /dev/null +++ b/tasks.md @@ -0,0 +1,73 @@ +# Relaunch Raydium Venue Implementation Tasks + +## Instructions for Claude + +**READ THIS FIRST:** + +1. Look at this file and find the task marked with `[NEXT]` +2. Read the referenced section in `vibes/relaunch-raydium-implementation-plan.md` for full context (design rationale lives in `vibes/relaunch-raydium-venue.html`) +3. Do ONLY that task - nothing else +4. After completing the task, verify: `./rebuild.sh` after any program/SDK change, then `anchor test --skip-build` (use `.only` on the new test file while iterating, remove it and run the full suite before finishing) +5. If successful, remove the completed task from this file +6. Mark the next task with `[NEXT]` +7. Stop and wait for the user + +**DO NOT:** +- Do multiple tasks at once +- Skip ahead +- Forget to verify + +**Reference:** Full implementation plan is in `vibes/relaunch-raydium-implementation-plan.md` + +--- + +## Tasks + +### Phase 1: Venue-aware initialize_relaunch + +> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 1 — venue-aware initialize_relaunch" + +- [NEXT] 1.1 `initialize_relaunch` venue branch (instruction + SDK + tests) + - `SourceVenue` enum + `source_venue` field appended after `pda_bump`; event field; venue gate on the pump-venue sell/buy; errors appended per plan + - `source_pool_lp_mint: Option>` — required iff Raydium, rejected for PumpSwap sources + - Five-gate Raydium validation: owner + 752 parse, pair-as-set with WSOL-pinned quote, `swap_permission`, `market_program == openbook`, burned-LP floor + - SDK: `sdk/src/relaunch/v0.1/raydiumAmm.ts` + `initializeRelaunch()` owner dispatch + - Tests: full accept/reject matrix per plan Stage 1 (13 new cases; existing pump cases stay green) + +### Phase 2: execute_sell_raydium + +> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 2 — execute_sell_raydium" + +- [ ] 2.1 `execute_sell_raydium` (instruction + SDK + tests) + - Extract `Relaunch::record_sell_outcome` shared tail; pump handler refactored, behavior-identical + - New instruction per plan account list (~11 accounts, no mint accounts); coin/pc vaults pinned locally against the parsed pool + - CPI `swap_base_in_v2(vault amount, min_quote_out)`; measured delta → `Sold`; same `SellExecutedEvent` + - SDK: `executeSellRaydiumIx` + `executeSell()` venue dispatch; 25 bps constant-product floor + - Tests in new `tests/relaunch/unit/executeSellRaydium.test.ts` per plan Stage 2, incl. wrong-venue gating both directions and vault-pinning failure + +### Phase 3: deposit_via_buy_raydium + +> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 3 — deposit_via_buy_raydium" + +- [ ] 3.1 `deposit_via_buy_raydium` (instruction + SDK + tests) + - Transfer `max_quote_in` → CPI `swap_base_out_v2(max_quote_in, base_out)` → refund unspent → `DepositRecord::credit` measured delta + - No volume accumulators, no fee recipients; same `TokensDepositedViaBuyEvent` + - SDK: `depositViaBuyRaydiumIx` + `depositViaBuy()` venue dispatch (wrap logic unchanged) + - Tests in new `tests/relaunch/unit/depositViaBuyRaydium.test.ts` per plan Stage 3, incl. exact-refund assertion and wrong-venue gating both directions + +### Phase 4: Lifecycle, ALT, budgets + +> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 4 — lifecycle, ALT, budgets" + +- [ ] 4.1 Full-lifecycle integration tests + - `tests/integration/relaunch.test.ts`: Raydium-source happy path with money-math identities at each hop; threshold-miss failure path with refunds + - Matrix note: WSOL × classic-SPL only — other combinations impossible for this venue + +- [ ] 4.2 ALT statics + - AMM v4 program + `5Q544…` authority into `scripts/relaunch/createAlt.ts` groups; re-dump the fork fixture + - `altTransactions.test.ts`: Raydium buy as a single legacy tx and as v0 against the extended table + - Must land before the mainnet ALT freeze (follow-ups item 3) + +- [ ] 4.3 Compute budgets for the new builders + - Measure worst-case CU from bankrun logs (surfpool figures as cross-check); worst +20% or drop to the 200k default + - Confirm pump-variant budgets untouched by the shared-tail refactor diff --git a/tests/fixtures/raydium_amm.so b/tests/fixtures/raydium_amm.so new file mode 100644 index 0000000000000000000000000000000000000000..76c0bac8e71f47bd33d5852f7b3661feec6fa59d GIT binary patch literal 1406384 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 z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5J2GnYljYG000002Kigx<12&=7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* q1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*`+)$m_RLEF literal 0 HcmV?d00001 diff --git a/tests/main.test.ts b/tests/main.test.ts index c0443fb14..fe9fff32e 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -52,6 +52,7 @@ import { sha256, } from "@metadaoproject/programs"; import { PUMP_GLOBAL_VOLUME_ACCUMULATOR } from "./relaunch/pumpAmm.js"; +import { RAYDIUM_AMM_PROGRAM_ID } from "./relaunch/raydiumAmm.js"; import { WHIRLPOOLS_CONFIG, WHIRLPOOL_FEE_TIER } from "./relaunch/whirlpool.js"; import { LaunchpadClient as LaunchpadClientV6 } from "@metadaoproject/programs/launchpad/v0.6"; import { LaunchpadClient as LaunchpadClientV8 } from "@metadaoproject/programs/launchpad/v0.8"; @@ -227,6 +228,10 @@ before(async function () { name: "whirlpool", programId: WHIRLPOOL_PROGRAM_ID, }, + { + name: "raydium_amm", + programId: RAYDIUM_AMM_PROGRAM_ID, + }, ], [ { diff --git a/tests/relaunch/raydiumAmm.ts b/tests/relaunch/raydiumAmm.ts new file mode 100644 index 000000000..1d58234f1 --- /dev/null +++ b/tests/relaunch/raydiumAmm.ts @@ -0,0 +1,281 @@ +import { Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { ProgramTestContext } from "solana-bankrun"; +import { writeTokenAccount } from "./pumpAmm.js"; + +// SDK constants arrive with the Stage 1 SDK module; until then they live here. +export const RAYDIUM_AMM_PROGRAM_ID = new PublicKey( + "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", +); +// The global ["amm authority"] PDA (nonce 254) owning every AMM v4 vault. +export const RAYDIUM_AMM_AUTHORITY = new PublicKey( + "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1", +); +export const OPENBOOK_PROGRAM_ID = new PublicKey( + "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX", +); + +const AMM_INFO_LEN = 752; +const POOL_RENT = 6_124_800n; // rent-exempt minimum for 752 bytes +const MINT_RENT = 1_461_600n; // rent-exempt minimum for 82 bytes + +export type WriteRaydiumPoolParams = { + context: ProgramTestContext; + oldMint: PublicKey; + tokenReserve: bigint; + quoteReserve: bigint; + // Which AMM side holds the old token. Pump migrations put the token on the + // pc side with WSOL as coin; "coin" fabricates the flipped orientation. + tokenSide?: "pc" | "coin"; + // Overrides below fabricate non-canonical pools for negative tests. + status?: bigint; + marketProgram?: PublicKey; + lpAmount?: bigint; + lpSupply?: bigint; +}; + +export type RaydiumPool = { + pool: PublicKey; + coinMint: PublicKey; + pcMint: PublicKey; + coinVault: PublicKey; + pcVault: PublicKey; + lpMint: PublicKey; +}; + +// AmmInfo layout (752 bytes, packed, no discriminator). The swap path reads +// status, nonce, the vault/mint fields, swap fees, and need_take_pnl; the +// rest are orderbook-era plumbing whose defaults below copy the live MOBY +// pool (AemYRZmJryzAQ9Z4RLfUBLnPRUY5ecooc94EJvemfti4, 2026-08-12). +// +// offset size field value written +// 0 8 status 6 (SwapOnly) unless overridden +// 8 8 nonce 254 (derives the 5Q544… authority) +// 16 8 order_num 7 +// 24 8 depth 3 +// 32 8 coin_decimals per orientation (WSOL 9, token 6) +// 40 8 pc_decimals per orientation +// 48 8 state 1 +// 56 8 reset_flag 0 +// 64 8 min_size 10_000_000 +// 72 8 vol_max_cut_ratio 500 +// 80 8 amount_wave 5_000_000 +// 88 8 coin_lot_size 10_000_000 +// 96 8 pc_lot_size 10_000_000 +// 104 8 min_price_multiplier 1 +// 112 8 max_price_multiplier 1_000_000_000 +// 120 8 sys_decimal_value 1_000_000_000 +// 128 64 fees 5/10000, 25/10000, 12/100, 25/10000 +// 192 144 state_data zero (need_take_pnl, PnL + swap stats) +// 336 32 coin_vault +// 368 32 pc_vault +// 400 32 coin_mint +// 432 32 pc_mint +// 464 32 lp_mint +// 496 32 open_orders zero (unread by the V2 path) +// 528 32 market zero (unread by the V2 path) +// 560 32 market_program OpenBook unless overridden +// 592 32 target_orders zero (unread by the V2 path) +// 624 64 padding1 zero +// 688 32 amm_owner zero (unread by swaps) +// 720 8 lp_amount ~4_045e9 unless overridden +// 728 8 client_order_id 0 +// 736 8 recent_epoch 0 +// 744 8 padding2 0 +// +// Fabricates the pool plus funded vaults and LP mint directly via setAccount. +// Deliberate rather than initialize2: canonicality is fingerprint-based +// (owner + shape), and real pool creation would drag in OpenBook markets the +// V2 swap path never touches. +export function writeRaydiumPool({ + context, + oldMint, + tokenReserve, + quoteReserve, + tokenSide = "pc", + status = 6n, + marketProgram = OPENBOOK_PROGRAM_ID, + lpAmount = 4_045_000_000_000n, + lpSupply = 2_000_000_000n, +}: WriteRaydiumPoolParams): RaydiumPool { + const pool = Keypair.generate().publicKey; + const coinVault = Keypair.generate().publicKey; + const pcVault = Keypair.generate().publicKey; + const lpMint = Keypair.generate().publicKey; + + const tokenAsPc = tokenSide === "pc"; + const coinMint = tokenAsPc ? token.NATIVE_MINT : oldMint; + const pcMint = tokenAsPc ? oldMint : token.NATIVE_MINT; + const coinReserve = tokenAsPc ? quoteReserve : tokenReserve; + const pcReserve = tokenAsPc ? tokenReserve : quoteReserve; + + const data = Buffer.alloc(AMM_INFO_LEN); + const u64Fields: [number, bigint][] = [ + [0, status], + [8, 254n], // nonce + [16, 7n], // order_num + [24, 3n], // depth + [32, tokenAsPc ? 9n : 6n], // coin_decimals + [40, tokenAsPc ? 6n : 9n], // pc_decimals + [48, 1n], // state + [64, 10_000_000n], // min_size + [72, 500n], // vol_max_cut_ratio + [80, 5_000_000n], // amount_wave + [88, 10_000_000n], // coin_lot_size + [96, 10_000_000n], // pc_lot_size + [104, 1n], // min_price_multiplier + [112, 1_000_000_000n], // max_price_multiplier + [120, 1_000_000_000n], // sys_decimal_value + [128, 5n], // min_separate_numerator + [136, 10_000n], // min_separate_denominator + [144, 25n], // trade_fee_numerator + [152, 10_000n], // trade_fee_denominator + [160, 12n], // pnl_numerator + [168, 100n], // pnl_denominator + [176, 25n], // swap_fee_numerator + [184, 10_000n], // swap_fee_denominator + [720, lpAmount], + ]; + for (const [offset, value] of u64Fields) { + data.writeBigUInt64LE(value, offset); + } + const pubkeyFields: [number, PublicKey][] = [ + [336, coinVault], + [368, pcVault], + [400, coinMint], + [432, pcMint], + [464, lpMint], + [560, marketProgram], + ]; + for (const [offset, key] of pubkeyFields) { + key.toBuffer().copy(data, offset); + } + + context.setAccount(pool, { + data, + owner: RAYDIUM_AMM_PROGRAM_ID, + lamports: Number(POOL_RENT), + executable: false, + }); + + writeTokenAccount(context, { + address: coinVault, + mint: coinMint, + owner: RAYDIUM_AMM_AUTHORITY, + amount: coinReserve, + }); + writeTokenAccount(context, { + address: pcVault, + mint: pcMint, + owner: RAYDIUM_AMM_AUTHORITY, + amount: pcReserve, + }); + + const lpMintData = Buffer.alloc(token.MINT_SIZE); + token.MintLayout.encode( + { + mintAuthorityOption: 1, + mintAuthority: RAYDIUM_AMM_AUTHORITY, + supply: lpSupply, + decimals: 9, + isInitialized: true, + freezeAuthorityOption: 0, + freezeAuthority: PublicKey.default, + }, + lpMintData, + ); + context.setAccount(lpMint, { + data: lpMintData, + owner: token.TOKEN_PROGRAM_ID, + lamports: Number(MINT_RENT), + executable: false, + }); + + return { pool, coinMint, pcMint, coinVault, pcVault, lpMint }; +} + +function swapAccountMetas( + pool: RaydiumPool, + userSourceTokenAccount: PublicKey, + userDestinationTokenAccount: PublicKey, + userSourceOwner: PublicKey, +) { + return [ + { pubkey: token.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + { pubkey: pool.pool, isSigner: false, isWritable: true }, + { pubkey: RAYDIUM_AMM_AUTHORITY, isSigner: false, isWritable: false }, + { pubkey: pool.coinVault, isSigner: false, isWritable: true }, + { pubkey: pool.pcVault, isSigner: false, isWritable: true }, + { pubkey: userSourceTokenAccount, isSigner: false, isWritable: true }, + { pubkey: userDestinationTokenAccount, isSigner: false, isWritable: true }, + { pubkey: userSourceOwner, isSigner: true, isWritable: false }, + ]; +} + +// swap_base_in_v2(amount_in, minimum_amount_out) — tag 16, exact input. +// Direction is inferred from the source/destination account mints. +export function raydiumSwapBaseInV2Ix({ + pool, + userSourceTokenAccount, + userDestinationTokenAccount, + userSourceOwner, + amountIn, + minimumAmountOut, +}: { + pool: RaydiumPool; + userSourceTokenAccount: PublicKey; + userDestinationTokenAccount: PublicKey; + userSourceOwner: PublicKey; + amountIn: bigint; + minimumAmountOut: bigint; +}): TransactionInstruction { + const data = Buffer.alloc(1 + 8 + 8); + data.writeUInt8(16, 0); + data.writeBigUInt64LE(amountIn, 1); + data.writeBigUInt64LE(minimumAmountOut, 9); + + return new TransactionInstruction({ + programId: RAYDIUM_AMM_PROGRAM_ID, + keys: swapAccountMetas( + pool, + userSourceTokenAccount, + userDestinationTokenAccount, + userSourceOwner, + ), + data, + }); +} + +// swap_base_out_v2(max_amount_in, amount_out) — tag 17, exact output; only +// the needed input is pulled from the source account. +export function raydiumSwapBaseOutV2Ix({ + pool, + userSourceTokenAccount, + userDestinationTokenAccount, + userSourceOwner, + maxAmountIn, + amountOut, +}: { + pool: RaydiumPool; + userSourceTokenAccount: PublicKey; + userDestinationTokenAccount: PublicKey; + userSourceOwner: PublicKey; + maxAmountIn: bigint; + amountOut: bigint; +}): TransactionInstruction { + const data = Buffer.alloc(1 + 8 + 8); + data.writeUInt8(17, 0); + data.writeBigUInt64LE(maxAmountIn, 1); + data.writeBigUInt64LE(amountOut, 9); + + return new TransactionInstruction({ + programId: RAYDIUM_AMM_PROGRAM_ID, + keys: swapAccountMetas( + pool, + userSourceTokenAccount, + userDestinationTokenAccount, + userSourceOwner, + ), + data, + }); +} diff --git a/tests/relaunch/unit/venues.test.ts b/tests/relaunch/unit/venues.test.ts index e2d80e406..6448de9b7 100644 --- a/tests/relaunch/unit/venues.test.ts +++ b/tests/relaunch/unit/venues.test.ts @@ -13,6 +13,11 @@ import { writePumpPool, PumpPool, } from "../pumpAmm.js"; +import { + raydiumSwapBaseInV2Ix, + raydiumSwapBaseOutV2Ix, + writeRaydiumPool, +} from "../raydiumAmm.js"; import { FIXTURE_USDC_SWAP_POOL, setupWhirlpool, @@ -44,6 +49,30 @@ async function tokenBalance( ).amount; } +function ceilDiv(a: bigint, b: bigint): bigint { + return (a + b - 1n) / b; +} + +// AMM v4 swap math at the 25 bps flat fee. The fee is +// ceil-rounded off the input and stays in the pool. +function raydiumExactInOutput( + amountIn: bigint, + inReserve: bigint, + outReserve: bigint, +): bigint { + const net = amountIn - ceilDiv(amountIn * 25n, 10_000n); + return (outReserve * net) / (inReserve + net); +} + +function raydiumExactOutInput( + amountOut: bigint, + inReserve: bigint, + outReserve: bigint, +): bigint { + const inBeforeFee = ceilDiv(inReserve * amountOut, outReserve - amountOut); + return ceilDiv(inBeforeFee * 10_000n, 9_975n); +} + export default function suite() { before(function () { this.bankrunProvider = new BankrunProvider(this.context); @@ -311,4 +340,222 @@ export default function suite() { usdcReceived > 95n * 10n ** 6n && usdcReceived < 100n * 10n ** 6n, ); }); + + it("raydium_amm exact-in sell moves exactly the constant-product output at 25 bps", async function () { + const { oldMint, payerOldTokenAccount } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + + const pool = writeRaydiumPool({ + context: this.context, + oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + const wsolAta = await wrapSol(this.bankrunProvider, this.payer, 0n); + + // Token is pc, WSOL is coin: selling tokens swaps pc → coin. + const predictedOut = raydiumExactInOutput( + SELL_AMOUNT, + POOL_BASE_RESERVE, + WSOL_POOL_QUOTE_RESERVE, + ); + + const tokenBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const tx = new Transaction().add( + raydiumSwapBaseInV2Ix({ + pool, + userSourceTokenAccount: payerOldTokenAccount, + userDestinationTokenAccount: wsolAta, + userSourceOwner: this.payer.publicKey, + amountIn: SELL_AMOUNT, + // The floor check is inclusive, so the exact prediction passes — + // the AMM itself asserts our formula. + minimumAmountOut: predictedOut, + }), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + const tokenAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + + assert.equal((tokenBefore - tokenAfter).toString(), SELL_AMOUNT.toString()); + assert.equal((wsolAfter - wsolBefore).toString(), predictedOut.toString()); + + // The fee stays in the pool: the pc vault gains the full input, the coin + // vault pays out exactly the prediction. No fee-recipient transfers. + assert.equal( + (await tokenBalance(this.banksClient, pool.pcVault)).toString(), + (POOL_BASE_RESERVE + SELL_AMOUNT).toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, pool.coinVault)).toString(), + (WSOL_POOL_QUOTE_RESERVE - predictedOut).toString(), + ); + }); + + it("raydium_amm exact-out buy pulls exactly the computed input and leaves the rest untouched", async function () { + const { oldMint, payerOldTokenAccount } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + + const pool = writeRaydiumPool({ + context: this.context, + oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + const maxAmountIn = 2n * 10n ** 9n; + const wsolAta = await wrapSol( + this.bankrunProvider, + this.payer, + maxAmountIn, + ); + + // Buying tokens (pc) with WSOL (coin): coin is the input reserve. + const predictedIn = raydiumExactOutInput( + SELL_AMOUNT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + + const tokenBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const tx = new Transaction().add( + raydiumSwapBaseOutV2Ix({ + pool, + userSourceTokenAccount: wsolAta, + userDestinationTokenAccount: payerOldTokenAccount, + userSourceOwner: this.payer.publicKey, + maxAmountIn, + amountOut: SELL_AMOUNT, + }), + ); + tx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + tx.feePayer = this.payer.publicKey; + tx.sign(this.payer); + await this.banksClient.processTransaction(tx); + + const tokenAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + + assert.equal((tokenAfter - tokenBefore).toString(), SELL_AMOUNT.toString()); + // Only the computed input is pulled; the rest of the allowance stays. + assert.equal((wsolBefore - wsolAfter).toString(), predictedIn.toString()); + assert.isTrue(predictedIn < maxAmountIn); + assert.equal( + (await tokenBalance(this.banksClient, pool.coinVault)).toString(), + (WSOL_POOL_QUOTE_RESERVE + predictedIn).toString(), + ); + }); + + it("raydium_amm flipped-orientation pool swaps correctly in both directions", async function () { + const { oldMint, payerOldTokenAccount } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + + const pool = writeRaydiumPool({ + context: this.context, + oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + tokenSide: "coin", + }); + assert.isTrue(pool.coinMint.equals(oldMint)); + + const wsolAta = await wrapSol(this.bankrunProvider, this.payer, 0n); + + // Direction is inferred from the mints, so the same sell is coin → pc + // on this pool. + const predictedOut = raydiumExactInOutput( + SELL_AMOUNT, + POOL_BASE_RESERVE, + WSOL_POOL_QUOTE_RESERVE, + ); + + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const sellTx = new Transaction().add( + raydiumSwapBaseInV2Ix({ + pool, + userSourceTokenAccount: payerOldTokenAccount, + userDestinationTokenAccount: wsolAta, + userSourceOwner: this.payer.publicKey, + amountIn: SELL_AMOUNT, + minimumAmountOut: predictedOut, + }), + ); + sellTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + sellTx.feePayer = this.payer.publicKey; + sellTx.sign(this.payer); + await this.banksClient.processTransaction(sellTx); + + const wsolAfterSell = await tokenBalance(this.banksClient, wsolAta); + assert.equal( + (wsolAfterSell - wsolBefore).toString(), + predictedOut.toString(), + ); + + // Buy the same tokens back (pc → coin) off the post-sell reserves. + const tokenReserveAfterSell = POOL_BASE_RESERVE + SELL_AMOUNT; + const quoteReserveAfterSell = WSOL_POOL_QUOTE_RESERVE - predictedOut; + const predictedIn = raydiumExactOutInput( + SELL_AMOUNT, + quoteReserveAfterSell, + tokenReserveAfterSell, + ); + + const tokenBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + + const buyTx = new Transaction().add( + raydiumSwapBaseOutV2Ix({ + pool, + userSourceTokenAccount: wsolAta, + userDestinationTokenAccount: payerOldTokenAccount, + userSourceOwner: this.payer.publicKey, + maxAmountIn: wsolAfterSell, + amountOut: SELL_AMOUNT, + }), + ); + buyTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + buyTx.feePayer = this.payer.publicKey; + buyTx.sign(this.payer); + await this.banksClient.processTransaction(buyTx); + + const tokenAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + const wsolAfterBuy = await tokenBalance(this.banksClient, wsolAta); + + assert.equal((tokenAfter - tokenBefore).toString(), SELL_AMOUNT.toString()); + assert.equal( + (wsolAfterSell - wsolAfterBuy).toString(), + predictedIn.toString(), + ); + }); } From 42050d4cdcc38bedffe93643866d1614c450e8ed Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 19:17:32 -0700 Subject: [PATCH 26/31] initialize raydium, pump checks on pump-specific swap ixes, sdk, tests --- programs/relaunch/src/error.rs | 10 + programs/relaunch/src/events.rs | 3 +- .../src/instructions/deposit_via_buy.rs | 7 +- .../relaunch/src/instructions/execute_sell.rs | 7 +- .../src/instructions/initialize_relaunch.rs | 179 ++++++++--- programs/relaunch/src/state/relaunch.rs | 10 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 19 ++ sdk/src/relaunch/v0.1/index.ts | 1 + sdk/src/relaunch/v0.1/raydiumAmm.ts | 64 ++++ sdk/src/relaunch/v0.1/types/relaunch.ts | 132 +++++++++ tasks.md | 13 +- tests/relaunch/raydiumAmm.ts | 27 +- .../relaunch/unit/initializeRelaunch.test.ts | 280 ++++++++++++++++++ 13 files changed, 674 insertions(+), 78 deletions(-) create mode 100644 sdk/src/relaunch/v0.1/raydiumAmm.ts diff --git a/programs/relaunch/src/error.rs b/programs/relaunch/src/error.rs index 555e10856..1a1417a6d 100644 --- a/programs/relaunch/src/error.rs +++ b/programs/relaunch/src/error.rs @@ -54,4 +54,14 @@ pub enum RelaunchError { RelaunchNotComplete, #[msg("Casting overflow. If you're seeing this, please report this")] CastingOverflow, + #[msg("Source pool LP mint must be supplied for Raydium sources only and match the pool's stored LP mint")] + SourcePoolLpMintMismatch, + #[msg("Source pool's burned LP is below the required floor")] + SourcePoolLpNotBurned, + #[msg("Source pool's status does not permit swaps")] + SourcePoolSwapsDisabled, + #[msg("Source pool was not created in the orderbook era")] + SourcePoolWrongEra, + #[msg("Instruction does not match the relaunch's source venue")] + WrongSourceVenue, } diff --git a/programs/relaunch/src/events.rs b/programs/relaunch/src/events.rs index 1cb1805c3..e51fa4228 100644 --- a/programs/relaunch/src/events.rs +++ b/programs/relaunch/src/events.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; -use crate::state::RelaunchState; +use crate::state::{RelaunchState, SourceVenue}; #[derive(AnchorSerialize, AnchorDeserialize)] pub struct CommonFields { @@ -42,6 +42,7 @@ pub struct RelaunchInitializedEvent { pub monthly_spending_limit_members: Vec, pub team_address: Pubkey, pub pda_bump: u8, + pub source_venue: SourceVenue, } #[event] diff --git a/programs/relaunch/src/instructions/deposit_via_buy.rs b/programs/relaunch/src/instructions/deposit_via_buy.rs index 2d748b9a0..1183ef841 100644 --- a/programs/relaunch/src/instructions/deposit_via_buy.rs +++ b/programs/relaunch/src/instructions/deposit_via_buy.rs @@ -6,7 +6,7 @@ use anchor_spl::token_interface; use crate::error::RelaunchError; use crate::events::{CommonFields, TokensDepositedViaBuyEvent}; use crate::pump_amm; -use crate::state::{DepositRecord, Relaunch, RelaunchState}; +use crate::state::{DepositRecord, Relaunch, RelaunchState, SourceVenue}; use crate::{ pump_amm_event_authority, pump_amm_fee_config, pump_amm_global_config, pump_amm_program, pump_fees_program, @@ -154,6 +154,11 @@ impl DepositViaBuy<'_> { RelaunchError::RelaunchNotLive ); + require!( + self.relaunch.source_venue == SourceVenue::PumpSwap, + RelaunchError::WrongSourceVenue + ); + let clock = Clock::get()?; require_gt!( self.relaunch.unix_timestamp_started.unwrap() diff --git a/programs/relaunch/src/instructions/execute_sell.rs b/programs/relaunch/src/instructions/execute_sell.rs index 601baf9d0..cb414abdd 100644 --- a/programs/relaunch/src/instructions/execute_sell.rs +++ b/programs/relaunch/src/instructions/execute_sell.rs @@ -6,7 +6,7 @@ use anchor_spl::token_interface; use crate::error::RelaunchError; use crate::events::{CommonFields, SellExecutedEvent}; use crate::pump_amm; -use crate::state::{Relaunch, RelaunchState}; +use crate::state::{Relaunch, RelaunchState, SourceVenue}; use crate::{ pump_amm_event_authority, pump_amm_fee_config, pump_amm_global_config, pump_amm_program, pump_fees_program, usdc_mint, @@ -121,6 +121,11 @@ impl ExecuteSell<'_> { RelaunchError::RelaunchNotSellPending ); + require!( + self.relaunch.source_venue == SourceVenue::PumpSwap, + RelaunchError::WrongSourceVenue + ); + let clock = Clock::get()?; require_gte!( self.relaunch.unix_timestamp_closed.unwrap() diff --git a/programs/relaunch/src/instructions/initialize_relaunch.rs b/programs/relaunch/src/instructions/initialize_relaunch.rs index 08f8218e2..05061917f 100644 --- a/programs/relaunch/src/instructions/initialize_relaunch.rs +++ b/programs/relaunch/src/instructions/initialize_relaunch.rs @@ -16,10 +16,12 @@ use anchor_spl::token_interface; use crate::error::RelaunchError; use crate::events::{CommonFields, RelaunchInitializedEvent}; use crate::pump_amm; -use crate::state::{Relaunch, RelaunchState}; +use crate::raydium_amm; +use crate::state::{Relaunch, RelaunchState, SourceVenue}; use crate::{ - pump_amm_program, pump_program, usdc_mint, wsol_mint, MAX_SECONDS_FOR_DEPOSITS, - PUMP_POOL_AUTHORITY_SEED, PUMP_POOL_SEED, TOKENS_TO_DEPOSITORS, TOKENS_TO_FUTARCHY_LIQUIDITY, + openbook_program, pump_amm_program, pump_program, raydium_amm_program, usdc_mint, wsol_mint, + MAX_SECONDS_FOR_DEPOSITS, PUMP_POOL_AUTHORITY_SEED, PUMP_POOL_SEED, RAYDIUM_MIN_BURNED_LP, + TOKENS_TO_DEPOSITORS, TOKENS_TO_FUTARCHY_LIQUIDITY, }; #[derive(AnchorSerialize, AnchorDeserialize, Clone)] @@ -73,6 +75,9 @@ pub struct InitializeRelaunch<'info> { pub source_quote_mint: Box>, + /// The source pool's LP mint: required for Raydium sources + pub source_pool_lp_mint: Option>>, + #[account(address = usdc_mint::id())] pub usdc_mint: Box>, @@ -145,18 +150,81 @@ impl InitializeRelaunch<'_> { RelaunchError::FreezeAuthoritySet ); + if *self.source_pool.owner == pump_amm_program::id() { + self.validate_pump_source()?; + } else if *self.source_pool.owner == raydium_amm_program::id() { + self.validate_raydium_source()?; + } else { + return err!(RelaunchError::SourcePoolNotCanonical); + } + + // Old mints may carry only mint-embedded metadata extensions; anything + // else (transfer fees, hooks, ...) is rejected rather than assumed safe. + let old_mint_info = self.old_mint.to_account_info(); + if *old_mint_info.owner == anchor_spl::token_2022::ID { + let old_mint_data = old_mint_info.try_borrow_data()?; + let old_mint_state = StateWithExtensions::::unpack(&old_mint_data)?; + for extension in old_mint_state.get_extension_types()? { + require!( + matches!( + extension, + ExtensionType::MetadataPointer | ExtensionType::TokenMetadata + ), + RelaunchError::ForbiddenOldMintExtension + ); + } + } + + require_gt!(args.threshold_bps, 0, RelaunchError::InvalidThresholdBps); + require_gte!( + 10_000, + args.threshold_bps, + RelaunchError::InvalidThresholdBps + ); + + require_gte!( + MAX_SECONDS_FOR_DEPOSITS, + args.seconds_for_deposits, + RelaunchError::InvalidSecondsForDeposits + ); + + // A zero amount with no members means the DAO launches without a + // spending limit; a config missing either half is invalid. + require!( + (args.monthly_spending_limit_amount == 0) + == args.monthly_spending_limit_members.is_empty(), + RelaunchError::InvalidMonthlySpendingLimit + ); + + require_gte!( + futarchy::MAX_SPENDING_LIMIT_MEMBERS, + args.monthly_spending_limit_members.len(), + RelaunchError::InvalidMonthlySpendingLimitMembers + ); + + let mut sorted_members = args.monthly_spending_limit_members.clone(); + sorted_members.sort(); + let has_duplicates = sorted_members.windows(2).any(|win| win[0] == win[1]); + require!( + !has_duplicates, + RelaunchError::InvalidMonthlySpendingLimitMembers + ); + + Ok(()) + } + + fn validate_pump_source(&self) -> Result<()> { + require!( + self.source_pool_lp_mint.is_none(), + RelaunchError::SourcePoolLpMintMismatch + ); + require!( self.source_quote_mint.key() == wsol_mint::id() || self.source_quote_mint.key() == usdc_mint::id(), RelaunchError::InvalidQuoteMint ); - require_keys_eq!( - *self.source_pool.owner, - pump_amm_program::id(), - RelaunchError::SourcePoolNotCanonical - ); - let pool = pump_amm::PumpSwapPool::try_parse(&self.source_pool.try_borrow_data()?)?; require_eq!(pool.index, 0, RelaunchError::SourcePoolNotCanonical); @@ -202,56 +270,60 @@ impl InitializeRelaunch<'_> { RelaunchError::SourcePoolNotCanonical ); - // Old mints may carry only mint-embedded metadata extensions; anything - // else (transfer fees, hooks, ...) is rejected rather than assumed safe. - let old_mint_info = self.old_mint.to_account_info(); - if *old_mint_info.owner == anchor_spl::token_2022::ID { - let old_mint_data = old_mint_info.try_borrow_data()?; - let old_mint_state = StateWithExtensions::::unpack(&old_mint_data)?; - for extension in old_mint_state.get_extension_types()? { - require!( - matches!( - extension, - ExtensionType::MetadataPointer | ExtensionType::TokenMetadata - ), - RelaunchError::ForbiddenOldMintExtension - ); - } - } + Ok(()) + } - require_gt!(args.threshold_bps, 0, RelaunchError::InvalidThresholdBps); - require_gte!( - 10_000, - args.threshold_bps, - RelaunchError::InvalidThresholdBps + /// No PDA provenance exists on AMM v4, so canonicality is owner + shape, + /// the right pair, swappability, the orderbook-era mark, and a migration's + /// worth of burned LP. + fn validate_raydium_source(&self) -> Result<()> { + require_keys_eq!( + self.source_quote_mint.key(), + wsol_mint::id(), + RelaunchError::InvalidQuoteMint ); - require_gte!( - MAX_SECONDS_FOR_DEPOSITS, - args.seconds_for_deposits, - RelaunchError::InvalidSecondsForDeposits + let pool = raydium_amm::RaydiumPool::try_parse(&self.source_pool.try_borrow_data()?)?; + + // The right pair in either orientation + let expected = (self.old_mint.key(), self.source_quote_mint.key()); + require!( + (pool.coin_mint, pool.pc_mint) == expected + || (pool.pc_mint, pool.coin_mint) == expected, + RelaunchError::SourcePoolNotCanonical ); - // A zero amount with no members means the DAO launches without a - // spending limit; a config missing either half is invalid. + // The AMM's own swap_permission(): 1 (Initialized), 6 (SwapOnly), + // 7 (WaitingTrade). require!( - (args.monthly_spending_limit_amount == 0) - == args.monthly_spending_limit_members.is_empty(), - RelaunchError::InvalidMonthlySpendingLimit + matches!(pool.status, 1 | 6 | 7), + RelaunchError::SourcePoolSwapsDisabled ); - require_gte!( - futarchy::MAX_SPENDING_LIMIT_MEMBERS, - args.monthly_spending_limit_members.len(), - RelaunchError::InvalidMonthlySpendingLimitMembers + // The orderbook fingerprint: pools created since Raydium removed the + // orderbook path store the system program here, so no decoy created + // today can pass. + require_keys_eq!( + pool.market_program, + openbook_program::id(), + RelaunchError::SourcePoolWrongEra ); - let mut sorted_members = args.monthly_spending_limit_members.clone(); - sorted_members.sort(); - let has_duplicates = sorted_members.windows(2).any(|win| win[0] == win[1]); - require!( - !has_duplicates, - RelaunchError::InvalidMonthlySpendingLimitMembers + // burned = lp_amount - supply is monotone: deposits and withdrawals + // move both terms monotonically, so only external burns increase it. + let lp_mint = match &self.source_pool_lp_mint { + Some(lp_mint) => lp_mint, + None => return err!(RelaunchError::SourcePoolLpMintMismatch), + }; + require_keys_eq!( + lp_mint.key(), + pool.lp_mint, + RelaunchError::SourcePoolLpMintMismatch + ); + require_gte!( + pool.lp_amount.saturating_sub(lp_mint.supply), + RAYDIUM_MIN_BURNED_LP, + RelaunchError::SourcePoolLpNotBurned ); Ok(()) @@ -260,6 +332,13 @@ impl InitializeRelaunch<'_> { pub fn handle(ctx: Context, args: InitializeRelaunchArgs) -> Result<()> { let relaunch_key = ctx.accounts.relaunch.key(); + // validate() already required the owner to be one of the two venues. + let source_venue = if *ctx.accounts.source_pool.owner == raydium_amm_program::id() { + SourceVenue::RaydiumAmmV4 + } else { + SourceVenue::PumpSwap + }; + let seeds = &[ b"relaunch_signer", relaunch_key.as_ref(), @@ -352,6 +431,7 @@ impl InitializeRelaunch<'_> { dao_vault: None, seq_num: 0, pda_bump: ctx.bumps.relaunch, + source_venue, }); let clock = Clock::get()?; @@ -377,6 +457,7 @@ impl InitializeRelaunch<'_> { monthly_spending_limit_members: args.monthly_spending_limit_members, team_address: args.team_address, pda_bump: ctx.bumps.relaunch, + source_venue, }); Ok(()) diff --git a/programs/relaunch/src/state/relaunch.rs b/programs/relaunch/src/state/relaunch.rs index bfabd49ea..0e939eb61 100644 --- a/programs/relaunch/src/state/relaunch.rs +++ b/programs/relaunch/src/state/relaunch.rs @@ -12,6 +12,14 @@ pub enum RelaunchState { Failed, } +/// The venue the source pool lives on, deciding how the pool was validated +/// at init and which sell/buy instructions apply. +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, PartialEq, Eq, InitSpace)] +pub enum SourceVenue { + PumpSwap, + RaydiumAmmV4, +} + #[account] #[derive(InitSpace)] pub struct Relaunch { @@ -86,4 +94,6 @@ pub struct Relaunch { pub seq_num: u64, /// The PDA bump. pub pda_bump: u8, + /// The venue of the source pool, set from the pool's owner at init. + pub source_venue: SourceVenue, } diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 798e6d85f..1f5c2064f 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -56,6 +56,7 @@ import { PUMP_AMM_GLOBAL_CONFIG, PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, } from "./pumpAmm.js"; +import { parseRaydiumPool, RAYDIUM_AMM_PROGRAM_ID } from "./raydiumAmm.js"; import { fetchWhirlpool, getWhirlpoolOracleAddr, @@ -105,6 +106,8 @@ export class RelaunchClient { oldMint, oldTokenProgram, sourcePool, + // Required for Raydium sources (the pool's LP mint), null for PumpSwap. + sourcePoolLpMint = null, sourceQuoteMint, tokenName, tokenSymbol, @@ -124,6 +127,7 @@ export class RelaunchClient { oldMint: PublicKey; oldTokenProgram: PublicKey; sourcePool: PublicKey; + sourcePoolLpMint?: PublicKey | null; sourceQuoteMint: PublicKey; tokenName: string; tokenSymbol: string; @@ -183,6 +187,7 @@ export class RelaunchClient { relaunchSigner, oldMint, sourcePool, + sourcePoolLpMint, sourceQuoteMint, usdcMint: MAINNET_USDC, oldTokenVault, @@ -1101,11 +1106,25 @@ export class RelaunchClient { throw new Error(`old mint ${oldMint.toBase58()} does not exist`); } + const sourcePoolAccount = + await this.provider.connection.getAccountInfo(sourcePool); + if (sourcePoolAccount === null) { + throw new Error(`source pool ${sourcePool.toBase58()} does not exist`); + } + // Raydium sources need the pool's LP mint alongside for the burned-LP + // check; PumpSwap sources are validated purely from the pool account. + const sourcePoolLpMint = sourcePoolAccount.owner.equals( + RAYDIUM_AMM_PROGRAM_ID, + ) + ? parseRaydiumPool(sourcePoolAccount.data).lpMint + : null; + const txSignature = await this.initializeRelaunchIx({ newMint, oldMint, oldTokenProgram: oldMintAccount.owner, sourcePool, + sourcePoolLpMint, sourceQuoteMint, tokenName, tokenSymbol, diff --git a/sdk/src/relaunch/v0.1/index.ts b/sdk/src/relaunch/v0.1/index.ts index fb2064adb..9dca2b152 100644 --- a/sdk/src/relaunch/v0.1/index.ts +++ b/sdk/src/relaunch/v0.1/index.ts @@ -1,5 +1,6 @@ export * from "./types/index.js"; export * from "./pda.js"; export * from "./pumpAmm.js"; +export * from "./raydiumAmm.js"; export * from "./whirlpool.js"; export * from "./RelaunchClient.js"; diff --git a/sdk/src/relaunch/v0.1/raydiumAmm.ts b/sdk/src/relaunch/v0.1/raydiumAmm.ts new file mode 100644 index 000000000..573c302cc --- /dev/null +++ b/sdk/src/relaunch/v0.1/raydiumAmm.ts @@ -0,0 +1,64 @@ +import { Connection, PublicKey } from "@solana/web3.js"; + +// Raydium's legacy "Standard" AMM v4, where pre-PumpSwap pump graduations +// live. +export const RAYDIUM_AMM_PROGRAM_ID = new PublicKey( + "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", +); + +// The global authority PDA over every AMM v4 vault: ["amm authority"]. +export const RAYDIUM_AMM_AUTHORITY = new PublicKey( + "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1", +); + +// OpenBook v1. Stored as market_program by every orderbook-era AMM v4 pool. +export const OPENBOOK_PROGRAM_ID = new PublicKey( + "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX", +); + +// AmmInfo is a fixed-size packed struct with no discriminator, so the exact +// length is the shape check. +export const AMM_INFO_LEN = 752; + +export type RaydiumPoolAccount = { + status: bigint; + coinVault: PublicKey; + pcVault: PublicKey; + coinMint: PublicKey; + pcMint: PublicKey; + lpMint: PublicKey; + marketProgram: PublicKey; + lpAmount: bigint; +}; + +// The subset of AmmInfo the client reads, at the same fixed offsets the +// program's RaydiumPool::try_parse uses. +export function parseRaydiumPool(data: Buffer): RaydiumPoolAccount { + if (data.length !== AMM_INFO_LEN) { + throw new Error( + `expected a ${AMM_INFO_LEN}-byte AMM v4 pool account, got ${data.length} bytes`, + ); + } + return { + status: data.readBigUInt64LE(0), + coinVault: new PublicKey(data.subarray(336, 368)), + pcVault: new PublicKey(data.subarray(368, 400)), + coinMint: new PublicKey(data.subarray(400, 432)), + pcMint: new PublicKey(data.subarray(432, 464)), + lpMint: new PublicKey(data.subarray(464, 496)), + marketProgram: new PublicKey(data.subarray(560, 592)), + lpAmount: data.readBigUInt64LE(720), + }; +} + +/** Fetches and parses `pool` as a Raydium AMM v4 pool account. */ +export async function fetchRaydiumPool( + connection: Connection, + pool: PublicKey, +): Promise { + const info = await connection.getAccountInfo(pool); + if (info === null) { + throw new Error(`raydium pool ${pool.toBase58()} does not exist`); + } + return parseRaydiumPool(info.data); +} diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index ee86918fc..01c0b3095 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -44,6 +44,13 @@ export type Relaunch = { isMut: false; isSigner: false; }, + { + name: "sourcePoolLpMint"; + isMut: false; + isSigner: false; + isOptional: true; + docs: ["The source pool's LP mint: required for Raydium sources"]; + }, { name: "usdcMint"; isMut: false; @@ -1230,6 +1237,15 @@ export type Relaunch = { docs: ["The PDA bump."]; type: "u8"; }, + { + name: "sourceVenue"; + docs: [ + "The venue of the source pool, set from the pool's owner at init.", + ]; + type: { + defined: "SourceVenue"; + }; + }, ]; }; }, @@ -1396,6 +1412,24 @@ export type Relaunch = { ]; }; }, + { + name: "SourceVenue"; + docs: [ + "The venue the source pool lives on, deciding how the pool was validated", + "at init and which sell/buy instructions apply.", + ]; + type: { + kind: "enum"; + variants: [ + { + name: "PumpSwap"; + }, + { + name: "RaydiumAmmV4"; + }, + ]; + }; + }, ]; events: [ { @@ -1510,6 +1544,13 @@ export type Relaunch = { type: "u8"; index: false; }, + { + name: "sourceVenue"; + type: { + defined: "SourceVenue"; + }; + index: false; + }, ]; }, { @@ -1988,6 +2029,31 @@ export type Relaunch = { name: "CastingOverflow"; msg: "Casting overflow. If you're seeing this, please report this"; }, + { + code: 6026; + name: "SourcePoolLpMintMismatch"; + msg: "Source pool LP mint must be supplied for Raydium sources only and match the pool's stored LP mint"; + }, + { + code: 6027; + name: "SourcePoolLpNotBurned"; + msg: "Source pool's burned LP is below the required floor"; + }, + { + code: 6028; + name: "SourcePoolSwapsDisabled"; + msg: "Source pool's status does not permit swaps"; + }, + { + code: 6029; + name: "SourcePoolWrongEra"; + msg: "Source pool was not created in the orderbook era"; + }, + { + code: 6030; + name: "WrongSourceVenue"; + msg: "Instruction does not match the relaunch's source venue"; + }, ]; }; @@ -2037,6 +2103,13 @@ export const IDL: Relaunch = { isMut: false, isSigner: false, }, + { + name: "sourcePoolLpMint", + isMut: false, + isSigner: false, + isOptional: true, + docs: ["The source pool's LP mint: required for Raydium sources"], + }, { name: "usdcMint", isMut: false, @@ -3223,6 +3296,15 @@ export const IDL: Relaunch = { docs: ["The PDA bump."], type: "u8", }, + { + name: "sourceVenue", + docs: [ + "The venue of the source pool, set from the pool's owner at init.", + ], + type: { + defined: "SourceVenue", + }, + }, ], }, }, @@ -3389,6 +3471,24 @@ export const IDL: Relaunch = { ], }, }, + { + name: "SourceVenue", + docs: [ + "The venue the source pool lives on, deciding how the pool was validated", + "at init and which sell/buy instructions apply.", + ], + type: { + kind: "enum", + variants: [ + { + name: "PumpSwap", + }, + { + name: "RaydiumAmmV4", + }, + ], + }, + }, ], events: [ { @@ -3503,6 +3603,13 @@ export const IDL: Relaunch = { type: "u8", index: false, }, + { + name: "sourceVenue", + type: { + defined: "SourceVenue", + }, + index: false, + }, ], }, { @@ -3981,5 +4088,30 @@ export const IDL: Relaunch = { name: "CastingOverflow", msg: "Casting overflow. If you're seeing this, please report this", }, + { + code: 6026, + name: "SourcePoolLpMintMismatch", + msg: "Source pool LP mint must be supplied for Raydium sources only and match the pool's stored LP mint", + }, + { + code: 6027, + name: "SourcePoolLpNotBurned", + msg: "Source pool's burned LP is below the required floor", + }, + { + code: 6028, + name: "SourcePoolSwapsDisabled", + msg: "Source pool's status does not permit swaps", + }, + { + code: 6029, + name: "SourcePoolWrongEra", + msg: "Source pool was not created in the orderbook era", + }, + { + code: 6030, + name: "WrongSourceVenue", + msg: "Instruction does not match the relaunch's source venue", + }, ], }; diff --git a/tasks.md b/tasks.md index afae8dc9a..23f976dcc 100644 --- a/tasks.md +++ b/tasks.md @@ -23,22 +23,11 @@ ## Tasks -### Phase 1: Venue-aware initialize_relaunch - -> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 1 — venue-aware initialize_relaunch" - -- [NEXT] 1.1 `initialize_relaunch` venue branch (instruction + SDK + tests) - - `SourceVenue` enum + `source_venue` field appended after `pda_bump`; event field; venue gate on the pump-venue sell/buy; errors appended per plan - - `source_pool_lp_mint: Option>` — required iff Raydium, rejected for PumpSwap sources - - Five-gate Raydium validation: owner + 752 parse, pair-as-set with WSOL-pinned quote, `swap_permission`, `market_program == openbook`, burned-LP floor - - SDK: `sdk/src/relaunch/v0.1/raydiumAmm.ts` + `initializeRelaunch()` owner dispatch - - Tests: full accept/reject matrix per plan Stage 1 (13 new cases; existing pump cases stay green) - ### Phase 2: execute_sell_raydium > Reference: `relaunch-raydium-implementation-plan.md` → "Stage 2 — execute_sell_raydium" -- [ ] 2.1 `execute_sell_raydium` (instruction + SDK + tests) +- [NEXT] 2.1 `execute_sell_raydium` (instruction + SDK + tests) - Extract `Relaunch::record_sell_outcome` shared tail; pump handler refactored, behavior-identical - New instruction per plan account list (~11 accounts, no mint accounts); coin/pc vaults pinned locally against the parsed pool - CPI `swap_base_in_v2(vault amount, min_quote_out)`; measured delta → `Sold`; same `SellExecutedEvent` diff --git a/tests/relaunch/raydiumAmm.ts b/tests/relaunch/raydiumAmm.ts index 1d58234f1..413fe5dc8 100644 --- a/tests/relaunch/raydiumAmm.ts +++ b/tests/relaunch/raydiumAmm.ts @@ -1,19 +1,14 @@ import { Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js"; import * as token from "@solana/spl-token"; import { ProgramTestContext } from "solana-bankrun"; +import { + OPENBOOK_PROGRAM_ID, + RAYDIUM_AMM_AUTHORITY, + RAYDIUM_AMM_PROGRAM_ID, +} from "@metadaoproject/programs"; import { writeTokenAccount } from "./pumpAmm.js"; -// SDK constants arrive with the Stage 1 SDK module; until then they live here. -export const RAYDIUM_AMM_PROGRAM_ID = new PublicKey( - "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", -); -// The global ["amm authority"] PDA (nonce 254) owning every AMM v4 vault. -export const RAYDIUM_AMM_AUTHORITY = new PublicKey( - "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1", -); -export const OPENBOOK_PROGRAM_ID = new PublicKey( - "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX", -); +export { OPENBOOK_PROGRAM_ID, RAYDIUM_AMM_AUTHORITY, RAYDIUM_AMM_PROGRAM_ID }; const AMM_INFO_LEN = 752; const POOL_RENT = 6_124_800n; // rent-exempt minimum for 752 bytes @@ -28,6 +23,8 @@ export type WriteRaydiumPoolParams = { // pc side with WSOL as coin; "coin" fabricates the flipped orientation. tokenSide?: "pc" | "coin"; // Overrides below fabricate non-canonical pools for negative tests. + owner?: PublicKey; + quoteMint?: PublicKey; status?: bigint; marketProgram?: PublicKey; lpAmount?: bigint; @@ -93,6 +90,8 @@ export function writeRaydiumPool({ tokenReserve, quoteReserve, tokenSide = "pc", + owner = RAYDIUM_AMM_PROGRAM_ID, + quoteMint = token.NATIVE_MINT, status = 6n, marketProgram = OPENBOOK_PROGRAM_ID, lpAmount = 4_045_000_000_000n, @@ -104,8 +103,8 @@ export function writeRaydiumPool({ const lpMint = Keypair.generate().publicKey; const tokenAsPc = tokenSide === "pc"; - const coinMint = tokenAsPc ? token.NATIVE_MINT : oldMint; - const pcMint = tokenAsPc ? oldMint : token.NATIVE_MINT; + const coinMint = tokenAsPc ? quoteMint : oldMint; + const pcMint = tokenAsPc ? oldMint : quoteMint; const coinReserve = tokenAsPc ? quoteReserve : tokenReserve; const pcReserve = tokenAsPc ? tokenReserve : quoteReserve; @@ -153,7 +152,7 @@ export function writeRaydiumPool({ context.setAccount(pool, { data, - owner: RAYDIUM_AMM_PROGRAM_ID, + owner, lamports: Number(POOL_RENT), executable: false, }); diff --git a/tests/relaunch/unit/initializeRelaunch.test.ts b/tests/relaunch/unit/initializeRelaunch.test.ts index e0a946530..9f032a6f9 100644 --- a/tests/relaunch/unit/initializeRelaunch.test.ts +++ b/tests/relaunch/unit/initializeRelaunch.test.ts @@ -22,6 +22,7 @@ import { writePumpPool, PumpPool, } from "../pumpAmm.js"; +import { writeRaydiumPool, WriteRaydiumPoolParams } from "../raydiumAmm.js"; type InitializeRelaunchParams = Parameters< RelaunchClient["initializeRelaunchIx"] @@ -191,6 +192,19 @@ export default function suite() { } }; + const writeCanonicalRaydiumPool = function ( + this: Mocha.Context, + overrides: Partial = {}, + ) { + return writeRaydiumPool({ + context: this.context, + oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + ...overrides, + }); + }; + it("initializes a relaunch with valid parameters", async function () { const params = defaultParams.call(this); const { newMint, relaunch, txSignature } = @@ -258,6 +272,7 @@ export default function suite() { assert.isNull(storedRelaunch.dao); assert.isNull(storedRelaunch.daoVault); assert.equal(storedRelaunch.seqNum.toString(), "0"); + assert.isDefined(storedRelaunch.sourceVenue.pumpSwap); const [expectedRelaunch, pdaBump] = PublicKey.findProgramAddressSync( [Buffer.from("relaunch"), newMint.toBuffer()], @@ -683,6 +698,271 @@ export default function suite() { ); }); + it("initializes with a canonical-shaped Raydium pool", async function () { + const raydiumPool = writeCanonicalRaydiumPool.call(this); + const params = defaultParams.call(this); + + const newMint = await initializeWithParams.call(this, { + sourcePool: raydiumPool.pool, + sourcePoolLpMint: raydiumPool.lpMint, + }); + + const relaunch = client.getRelaunchAddress({ newMint }); + const relaunchSigner = client.getRelaunchSignerAddress({ relaunch }); + const storedRelaunch = await client.fetchRelaunch(relaunch); + + assert.isTrue(storedRelaunch.admin.equals(this.payer.publicKey)); + assert.isTrue(storedRelaunch.newMint.equals(newMint)); + assert.isTrue(storedRelaunch.oldMint.equals(oldMint)); + assert.isTrue(storedRelaunch.sourcePool.equals(raydiumPool.pool)); + assert.isTrue(storedRelaunch.sourceQuoteMint.equals(token.NATIVE_MINT)); + assert.isTrue(storedRelaunch.relaunchSigner.equals(relaunchSigner)); + assert.isTrue( + storedRelaunch.oldTokenVault.equals( + token.getAssociatedTokenAddressSync(oldMint, relaunchSigner, true), + ), + ); + assert.isTrue( + storedRelaunch.newTokenVault.equals( + token.getAssociatedTokenAddressSync(newMint, relaunchSigner, true), + ), + ); + assert.isTrue( + storedRelaunch.sourceQuoteVault.equals( + token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + relaunchSigner, + true, + ), + ), + ); + assert.isTrue( + storedRelaunch.usdcVault.equals( + token.getAssociatedTokenAddressSync(MAINNET_USDC, relaunchSigner, true), + ), + ); + assert.equal(storedRelaunch.thresholdBps, params.thresholdBps); + assert.equal( + storedRelaunch.oldSupplySnapshot.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + assert.equal(storedRelaunch.secondsForDeposits, params.secondsForDeposits); + assert.equal(storedRelaunch.gracePeriodSeconds, params.gracePeriodSeconds); + assert.equal( + storedRelaunch.monthlySpendingLimitAmount.toString(), + params.monthlySpendingLimitAmount.toString(), + ); + assert.equal(storedRelaunch.monthlySpendingLimitMembers.length, 1); + assert.isTrue( + storedRelaunch.monthlySpendingLimitMembers[0].equals( + this.payer.publicKey, + ), + ); + assert.isTrue(storedRelaunch.teamAddress.equals(this.payer.publicKey)); + assert.isDefined(storedRelaunch.state.initialized); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + assert.equal(storedRelaunch.quoteRecovered.toString(), "0"); + assert.equal(storedRelaunch.usdcRecovered.toString(), "0"); + assert.isNull(storedRelaunch.unixTimestampStarted); + assert.isNull(storedRelaunch.unixTimestampClosed); + assert.isNull(storedRelaunch.unixTimestampCompleted); + assert.isNull(storedRelaunch.dao); + assert.isNull(storedRelaunch.daoVault); + assert.equal(storedRelaunch.seqNum.toString(), "0"); + assert.isDefined(storedRelaunch.sourceVenue.raydiumAmmV4); + + const [expectedRelaunch, pdaBump] = PublicKey.findProgramAddressSync( + [Buffer.from("relaunch"), newMint.toBuffer()], + client.getProgramId(), + ); + assert.isTrue(relaunch.equals(expectedRelaunch)); + assert.equal(storedRelaunch.pdaBump, pdaBump); + const [, signerBump] = PublicKey.findProgramAddressSync( + [Buffer.from("relaunch_signer"), relaunch.toBuffer()], + client.getProgramId(), + ); + assert.equal(storedRelaunch.relaunchSignerBump, signerBump); + + const rawNewMint = await this.banksClient.getAccount(newMint); + const newMintState = token.unpackMint(newMint, { + ...rawNewMint, + data: Buffer.from(rawNewMint.data), + } as any); + assert.isTrue(newMintState.mintAuthority.equals(relaunchSigner)); + assert.equal(newMintState.supply.toString(), TOTAL_MINTED.toString()); + + const rawVault = await this.banksClient.getAccount( + storedRelaunch.newTokenVault, + ); + const vault = token.unpackAccount(storedRelaunch.newTokenVault, { + ...rawVault, + data: Buffer.from(rawVault.data), + } as any); + assert.equal(vault.amount.toString(), TOTAL_MINTED.toString()); + + const metadata = await this.banksClient.getAccount( + getMetadataAddr(newMint)[0], + ); + assert.isNotNull(metadata); + }); + + it("initializes with a flipped-orientation Raydium pool", async function () { + const raydiumPool = writeCanonicalRaydiumPool.call(this, { + tokenSide: "coin", + }); + + const newMint = await initializeWithParams.call(this, { + sourcePool: raydiumPool.pool, + sourcePoolLpMint: raydiumPool.lpMint, + }); + + const storedRelaunch = await client.getRelaunch({ newMint }); + assert.isDefined(storedRelaunch.sourceVenue.raydiumAmmV4); + }); + + it("initializes when the burned LP floor is met under a large unburned supply", async function () { + // burned = 8,086 − 4,043 = 4,043 LP: organic unburned liquidity on top + // must not trip the floor. + const raydiumPool = writeCanonicalRaydiumPool.call(this, { + lpAmount: 8_086n * 10n ** 9n, + lpSupply: 4_043n * 10n ** 9n, + }); + + const newMint = await initializeWithParams.call(this, { + sourcePool: raydiumPool.pool, + sourcePoolLpMint: raydiumPool.lpMint, + }); + + const storedRelaunch = await client.getRelaunch({ newMint }); + assert.isDefined(storedRelaunch.sourceVenue.raydiumAmmV4); + }); + + it("fails when the source pool is owned by neither pump_amm nor the Raydium AMM", async function () { + const foreignPool = writeCanonicalRaydiumPool.call(this, { + owner: token.TOKEN_PROGRAM_ID, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: foreignPool.pool, sourcePoolLpMint: foreignPool.lpMint }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the Raydium pool data is not 752 bytes", async function () { + const raydiumPool = writeCanonicalRaydiumPool.call(this); + const raw = await this.banksClient.getAccount(raydiumPool.pool); + this.context.setAccount(raydiumPool.pool, { + data: Buffer.from(raw.data.subarray(0, 751)), + owner: raw.owner, + lamports: Number(raw.lamports), + executable: false, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: raydiumPool.pool, sourcePoolLpMint: raydiumPool.lpMint }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the Raydium pool pair does not include the old mint", async function () { + const otherPool = writeCanonicalRaydiumPool.call(this, { + oldMint: Keypair.generate().publicKey, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: otherPool.pool, sourcePoolLpMint: otherPool.lpMint }, + "SourcePoolNotCanonical", + ); + }); + + it("fails when the quote side of the Raydium pool is not WSOL", async function () { + const usdcPool = writeCanonicalRaydiumPool.call(this, { + quoteMint: MAINNET_USDC, + }); + + await expectInitializeToFail.call( + this, + { + sourcePool: usdcPool.pool, + sourcePoolLpMint: usdcPool.lpMint, + sourceQuoteMint: MAINNET_USDC, + }, + "InvalidQuoteMint", + ); + }); + + it("fails when the Raydium pool status disables swaps", async function () { + const disabledPool = writeCanonicalRaydiumPool.call(this, { status: 2n }); + + await expectInitializeToFail.call( + this, + { sourcePool: disabledPool.pool, sourcePoolLpMint: disabledPool.lpMint }, + "SourcePoolSwapsDisabled", + ); + }); + + it("fails when the Raydium pool's market program is the system program", async function () { + // Pools created after Raydium removed the orderbook path store the + // system program as market_program — the wrong-era fingerprint. + const modernPool = writeCanonicalRaydiumPool.call(this, { + marketProgram: SystemProgram.programId, + }); + + await expectInitializeToFail.call( + this, + { sourcePool: modernPool.pool, sourcePoolLpMint: modernPool.lpMint }, + "SourcePoolWrongEra", + ); + }); + + it("fails when the Raydium pool's burned LP is below the floor", async function () { + // burned = 4,045 − 200 = 3,845 LP, under the 4,000 floor. + const shallowBurnPool = writeCanonicalRaydiumPool.call(this, { + lpAmount: 4_045n * 10n ** 9n, + lpSupply: 200n * 10n ** 9n, + }); + + await expectInitializeToFail.call( + this, + { + sourcePool: shallowBurnPool.pool, + sourcePoolLpMint: shallowBurnPool.lpMint, + }, + "SourcePoolLpNotBurned", + ); + }); + + it("fails when the supplied LP mint is not the pool's LP mint", async function () { + const raydiumPool = writeCanonicalRaydiumPool.call(this); + + await expectInitializeToFail.call( + this, + { sourcePool: raydiumPool.pool, sourcePoolLpMint: oldMint }, + "SourcePoolLpMintMismatch", + ); + }); + + it("fails when the LP mint is omitted for a Raydium source", async function () { + const raydiumPool = writeCanonicalRaydiumPool.call(this); + + await expectInitializeToFail.call( + this, + { sourcePool: raydiumPool.pool }, + "SourcePoolLpMintMismatch", + ); + }); + + it("fails when an LP mint is supplied for a PumpSwap source", async function () { + await expectInitializeToFail.call( + this, + { sourcePoolLpMint: oldMint }, + "SourcePoolLpMintMismatch", + ); + }); + it("validates the threshold bounds", async function () { await expectInitializeToFail.call( this, From c454d539e6fced4bc730747e6259fa0bac45e834 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 19:42:27 -0700 Subject: [PATCH 27/31] execute_sell_raydium ix, sdk, tests --- .../src/instructions/execute_sell_raydium.rs | 152 +++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 8 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 99 ++++- sdk/src/relaunch/v0.1/types/relaunch.ts | 188 +++++++++ tasks.md | 13 +- tests/relaunch/main.test.ts | 2 + .../relaunch/unit/executeSellRaydium.test.ts | 385 ++++++++++++++++++ 8 files changed, 832 insertions(+), 17 deletions(-) create mode 100644 programs/relaunch/src/instructions/execute_sell_raydium.rs create mode 100644 tests/relaunch/unit/executeSellRaydium.test.ts diff --git a/programs/relaunch/src/instructions/execute_sell_raydium.rs b/programs/relaunch/src/instructions/execute_sell_raydium.rs new file mode 100644 index 000000000..b85362e87 --- /dev/null +++ b/programs/relaunch/src/instructions/execute_sell_raydium.rs @@ -0,0 +1,152 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Token, TokenAccount}; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, SellExecutedEvent}; +use crate::raydium_amm; +use crate::state::{Relaunch, RelaunchState, SourceVenue}; +use crate::{raydium_amm_authority, raydium_amm_program}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct ExecuteSellRaydiumArgs { + /// The admin's live, client-computed slippage floor on the sell proceeds. + pub min_quote_out: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct ExecuteSellRaydium<'info> { + #[account( + mut, + has_one = admin, + has_one = source_pool, + has_one = relaunch_signer, + has_one = old_token_vault, + has_one = source_quote_vault, + )] + pub relaunch: Box>, + + pub admin: Signer<'info>, + + /// CHECK: the vault authority that signs the sell + pub relaunch_signer: UncheckedAccount<'info>, + + #[account(mut)] + pub old_token_vault: Box>, + + #[account(mut)] + pub source_quote_vault: Box>, + + /// CHECK: fingerprint-validated at init and pinned by has_one; the AMM + /// rechecks its internal consistency. + #[account(mut)] + pub source_pool: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = raydium_amm_authority::id())] + pub amm_authority: UncheckedAccount<'info>, + + /// CHECK: pinned against the pool's stored field in validate() + #[account(mut)] + pub amm_coin_vault: UncheckedAccount<'info>, + + /// CHECK: pinned against the pool's stored field in validate() + #[account(mut)] + pub amm_pc_vault: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = raydium_amm_program::id())] + pub raydium_amm_program: UncheckedAccount<'info>, + + pub token_program: Program<'info, Token>, +} + +impl ExecuteSellRaydium<'_> { + pub fn validate(&self, _args: &ExecuteSellRaydiumArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::SellPending, + RelaunchError::RelaunchNotSellPending + ); + + require!( + self.relaunch.source_venue == SourceVenue::RaydiumAmmV4, + RelaunchError::WrongSourceVenue + ); + + let clock = Clock::get()?; + require_gte!( + self.relaunch.unix_timestamp_closed.unwrap() + + self.relaunch.grace_period_seconds as i64, + clock.unix_timestamp, + RelaunchError::GracePeriodElapsed + ); + + // Pin the vaults to the pool's stored fields. + let pool = raydium_amm::RaydiumPool::try_parse(&self.source_pool.try_borrow_data()?)?; + require_keys_eq!( + self.amm_coin_vault.key(), + pool.coin_vault, + RelaunchError::SourcePoolNotCanonical + ); + require_keys_eq!( + self.amm_pc_vault.key(), + pool.pc_vault, + RelaunchError::SourcePoolNotCanonical + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: ExecuteSellRaydiumArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let base_sold = ctx.accounts.old_token_vault.amount; + let quote_before = ctx.accounts.source_quote_vault.amount; + + raydium_amm::swap_base_in_v2( + raydium_amm::Swap { + token_program: ctx.accounts.token_program.to_account_info(), + amm: ctx.accounts.source_pool.to_account_info(), + amm_authority: ctx.accounts.amm_authority.to_account_info(), + amm_coin_vault: ctx.accounts.amm_coin_vault.to_account_info(), + amm_pc_vault: ctx.accounts.amm_pc_vault.to_account_info(), + user_source_token_account: ctx.accounts.old_token_vault.to_account_info(), + user_destination_token_account: ctx + .accounts + .source_quote_vault + .to_account_info(), + user_source_owner: ctx.accounts.relaunch_signer.to_account_info(), + }, + base_sold, + args.min_quote_out, + signer, + )?; + + ctx.accounts.source_quote_vault.reload()?; + let quote_recovered = ctx.accounts.source_quote_vault.amount - quote_before; + + // Raydium sources are WSOL-quoted, so the sell always lands in Sold. + let relaunch = &mut ctx.accounts.relaunch; + relaunch.quote_recovered = quote_recovered; + relaunch.state = RelaunchState::Sold; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(SellExecutedEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + base_sold, + quote_recovered, + new_state: ctx.accounts.relaunch.state, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index a891008f4..10abe97d2 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -5,6 +5,7 @@ pub mod complete_relaunch; pub mod deposit; pub mod deposit_via_buy; pub mod execute_sell; +pub mod execute_sell_raydium; pub mod execute_usdc_swap; pub mod initialize_relaunch; pub mod mark_failed; @@ -17,6 +18,7 @@ pub use complete_relaunch::*; pub use deposit::*; pub use deposit_via_buy::*; pub use execute_sell::*; +pub use execute_sell_raydium::*; pub use execute_usdc_swap::*; pub use initialize_relaunch::*; pub use mark_failed::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 716f42ac6..9944666e0 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -68,6 +68,14 @@ pub mod relaunch { ExecuteSell::handle(ctx, args) } + #[access_control(ctx.accounts.validate(&args))] + pub fn execute_sell_raydium( + ctx: Context, + args: ExecuteSellRaydiumArgs, + ) -> Result<()> { + ExecuteSellRaydium::handle(ctx, args) + } + #[access_control(ctx.accounts.validate(&args))] pub fn execute_usdc_swap( ctx: Context, diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 1f5c2064f..2c9a71d64 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -56,7 +56,12 @@ import { PUMP_AMM_GLOBAL_CONFIG, PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR, } from "./pumpAmm.js"; -import { parseRaydiumPool, RAYDIUM_AMM_PROGRAM_ID } from "./raydiumAmm.js"; +import { + fetchRaydiumPool, + parseRaydiumPool, + RAYDIUM_AMM_AUTHORITY, + RAYDIUM_AMM_PROGRAM_ID, +} from "./raydiumAmm.js"; import { fetchWhirlpool, getWhirlpoolOracleAddr, @@ -469,6 +474,55 @@ export class RelaunchClient { ]); } + executeSellRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint, + sourcePool, + ammCoinVault, + ammPcVault, + minQuoteOut, + admin = this.provider.publicKey, + }: { + relaunch: PublicKey; + oldMint: PublicKey; + sourceQuoteMint: PublicKey; + sourcePool: PublicKey; + ammCoinVault: PublicKey; + ammPcVault: PublicKey; + minQuoteOut: BN; + admin?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + ); + const sourceQuoteVault = getAssociatedTokenAddressSync( + sourceQuoteMint, + relaunchSigner, + true, + ); + + return this.relaunchProgram.methods + .executeSellRaydium({ minQuoteOut }) + .accounts({ + relaunch, + admin, + relaunchSigner, + oldTokenVault, + sourceQuoteVault, + sourcePool, + ammAuthority: RAYDIUM_AMM_AUTHORITY, + ammCoinVault, + ammPcVault, + raydiumAmmProgram: RAYDIUM_AMM_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + }); + } + executeUsdcSwapIx({ relaunch, whirlpoolWsolVault, @@ -904,10 +958,12 @@ export class RelaunchClient { } // Sells the whole old-token vault as the admin (the provider wallet), - // deriving the pump account set from the stored relaunch, its pool, and - // pump's global config. When minQuoteOut is not given, it is computed live - // from the pool reserves: the constant-product output of the sell minus - // slippageBps (which must also cover pump's swap fees). + // dispatching on the relaunch's stored source venue and deriving the + // venue's account set from the stored relaunch and its pool. When + // minQuoteOut is not given, it is computed live from the pool reserves: + // the constant-product output of the sell minus slippageBps. Pump's swap + // fees are not modeled, so slippageBps must also cover them; Raydium's + // flat 25 bps fee is exact, so slippageBps only covers price movement. async executeSell({ relaunch, minQuoteOut, @@ -922,6 +978,39 @@ export class RelaunchClient { throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); } + if (storedRelaunch.sourceVenue.raydiumAmmV4 !== undefined) { + const pool = await fetchRaydiumPool( + this.provider.connection, + storedRelaunch.sourcePool, + ); + + if (minQuoteOut === undefined) { + const tokenIsCoin = pool.coinMint.equals(storedRelaunch.oldMint); + const [baseIn, tokenReserve, quoteReserve] = await Promise.all( + [ + storedRelaunch.oldTokenVault, + tokenIsCoin ? pool.coinVault : pool.pcVault, + tokenIsCoin ? pool.pcVault : pool.coinVault, + ].map((address) => this.fetchTokenBalance(address)), + ); + // The 25 bps fee is ceil-rounded off the input and stays in the pool. + const netIn = baseIn - (baseIn * 25n + 9_999n) / 10_000n; + const grossOut = (quoteReserve * netIn) / (tokenReserve + netIn); + const floor = (grossOut * (10_000n - BigInt(slippageBps))) / 10_000n; + minQuoteOut = new BN(floor.toString()); + } + + return this.executeSellRaydiumIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + sourceQuoteMint: storedRelaunch.sourceQuoteMint, + sourcePool: storedRelaunch.sourcePool, + ammCoinVault: pool.coinVault, + ammPcVault: pool.pcVault, + minQuoteOut, + }).rpc(); + } + const oldMintAccount = await this.provider.connection.getAccountInfo( storedRelaunch.oldMint, ); diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 01c0b3095..4bafc63e2 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -599,6 +599,85 @@ export type Relaunch = { }, ]; }, + { + name: "executeSellRaydium"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "admin"; + isMut: false; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourcePool"; + isMut: true; + isSigner: false; + docs: ["rechecks its internal consistency."]; + }, + { + name: "ammAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "ammCoinVault"; + isMut: true; + isSigner: false; + }, + { + name: "ammPcVault"; + isMut: true; + isSigner: false; + }, + { + name: "raydiumAmmProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "ExecuteSellRaydiumArgs"; + }; + }, + ]; + }, { name: "executeUsdcSwap"; accounts: [ @@ -1307,6 +1386,21 @@ export type Relaunch = { ]; }; }, + { + name: "ExecuteSellRaydiumArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "minQuoteOut"; + docs: [ + "The admin's live, client-computed slippage floor on the sell proceeds.", + ]; + type: "u64"; + }, + ]; + }; + }, { name: "ExecuteSellArgs"; type: { @@ -2658,6 +2752,85 @@ export const IDL: Relaunch = { }, ], }, + { + name: "executeSellRaydium", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "admin", + isMut: false, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "sourcePool", + isMut: true, + isSigner: false, + docs: ["rechecks its internal consistency."], + }, + { + name: "ammAuthority", + isMut: false, + isSigner: false, + }, + { + name: "ammCoinVault", + isMut: true, + isSigner: false, + }, + { + name: "ammPcVault", + isMut: true, + isSigner: false, + }, + { + name: "raydiumAmmProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "ExecuteSellRaydiumArgs", + }, + }, + ], + }, { name: "executeUsdcSwap", accounts: [ @@ -3366,6 +3539,21 @@ export const IDL: Relaunch = { ], }, }, + { + name: "ExecuteSellRaydiumArgs", + type: { + kind: "struct", + fields: [ + { + name: "minQuoteOut", + docs: [ + "The admin's live, client-computed slippage floor on the sell proceeds.", + ], + type: "u64", + }, + ], + }, + }, { name: "ExecuteSellArgs", type: { diff --git a/tasks.md b/tasks.md index 23f976dcc..00a2a9313 100644 --- a/tasks.md +++ b/tasks.md @@ -23,22 +23,11 @@ ## Tasks -### Phase 2: execute_sell_raydium - -> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 2 — execute_sell_raydium" - -- [NEXT] 2.1 `execute_sell_raydium` (instruction + SDK + tests) - - Extract `Relaunch::record_sell_outcome` shared tail; pump handler refactored, behavior-identical - - New instruction per plan account list (~11 accounts, no mint accounts); coin/pc vaults pinned locally against the parsed pool - - CPI `swap_base_in_v2(vault amount, min_quote_out)`; measured delta → `Sold`; same `SellExecutedEvent` - - SDK: `executeSellRaydiumIx` + `executeSell()` venue dispatch; 25 bps constant-product floor - - Tests in new `tests/relaunch/unit/executeSellRaydium.test.ts` per plan Stage 2, incl. wrong-venue gating both directions and vault-pinning failure - ### Phase 3: deposit_via_buy_raydium > Reference: `relaunch-raydium-implementation-plan.md` → "Stage 3 — deposit_via_buy_raydium" -- [ ] 3.1 `deposit_via_buy_raydium` (instruction + SDK + tests) +- [NEXT] 3.1 `deposit_via_buy_raydium` (instruction + SDK + tests) - Transfer `max_quote_in` → CPI `swap_base_out_v2(max_quote_in, base_out)` → refund unspent → `DepositRecord::credit` measured delta - No volume accumulators, no fee recipients; same `TokensDepositedViaBuyEvent` - SDK: `depositViaBuyRaydiumIx` + `depositViaBuy()` venue dispatch (wrap logic unchanged) diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 0c3ab1379..56bc95e8e 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -6,6 +6,7 @@ import deposit from "./unit/deposit.test.js"; import depositViaBuy from "./unit/depositViaBuy.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; +import executeSellRaydium from "./unit/executeSellRaydium.test.js"; import executeUsdcSwap from "./unit/executeUsdcSwap.test.js"; import completeRelaunch from "./unit/completeRelaunch.test.js"; import claim from "./unit/claim.test.js"; @@ -31,6 +32,7 @@ export default function suite() { describe("#deposit_via_buy", depositViaBuy); describe("#close_deposits", closeDeposits); describe("#execute_sell", executeSell); + describe("#execute_sell_raydium", executeSellRaydium); describe("#execute_usdc_swap", executeUsdcSwap); describe("#complete_relaunch", completeRelaunch); describe("#claim", claim); diff --git a/tests/relaunch/unit/executeSellRaydium.test.ts b/tests/relaunch/unit/executeSellRaydium.test.ts new file mode 100644 index 000000000..155682b74 --- /dev/null +++ b/tests/relaunch/unit/executeSellRaydium.test.ts @@ -0,0 +1,385 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BanksClient } from "solana-bankrun"; +import { getPumpFeeRecipients, RelaunchClient } from "@metadaoproject/programs"; +import { setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; +import { writePumpPool, PumpPool } from "../pumpAmm.js"; +import { writeRaydiumPool, RaydiumPool } from "../raydiumAmm.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const DEFAULT_THRESHOLD_BPS = 1000; +// 10% of the 1B-token default supply = 100M tokens. +const DEPOSIT_AMOUNT = DEFAULT_OLD_SUPPLY / 10n; + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount(address, { + ...raw, + data: Buffer.from(raw.data), + } as any).amount; +} + +function ceilDiv(a: bigint, b: bigint): bigint { + return (a + b - 1n) / b; +} + +// The exact output of selling the deposited amount at AMM v4's flat 25 bps +// fee (ceil-rounded off the input, kept in the pool). +function predictedQuoteOut(): bigint { + const net = DEPOSIT_AMOUNT - ceilDiv(DEPOSIT_AMOUNT * 25n, 10_000n); + return (WSOL_POOL_QUOTE_RESERVE * net) / (POOL_BASE_RESERVE + net); +} + +export default function suite() { + let client: RelaunchClient; + + before(function () { + client = this.relaunch; + }); + + const runToSellPending = async function ( + this: Mocha.Context, + { + oldMint, + sourcePool, + close, + }: { oldMint: PublicKey; sourcePool: PublicKey; close: boolean }, + ): Promise { + const { relaunch } = await client.initializeRelaunch({ + oldMint, + sourcePool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: DEFAULT_THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + amount: new BN(DEPOSIT_AMOUNT.toString()), + }) + .rpc(); + await this.advanceBySeconds(ONE_WEEK); + if (close) { + await client.closeDepositsIx({ relaunch }).rpc(); + } + + return relaunch; + }; + + const setupRaydiumSellPending = async function ( + this: Mocha.Context, + { close = true }: { close?: boolean } = {}, + ): Promise<{ relaunch: PublicKey; pool: RaydiumPool; oldMint: PublicKey }> { + const { oldMint } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = writeRaydiumPool({ + context: this.context, + oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + const relaunch = await runToSellPending.call(this, { + oldMint, + sourcePool: pool.pool, + close, + }); + return { relaunch, pool, oldMint }; + }; + + const setupPumpSellPending = async function ( + this: Mocha.Context, + ): Promise<{ relaunch: PublicKey; pool: PumpPool; oldMint: PublicKey }> { + const { oldMint } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: token.TOKEN_PROGRAM_ID, + }); + const relaunch = await runToSellPending.call(this, { + oldMint, + sourcePool: pool.pool, + close: true, + }); + return { relaunch, pool, oldMint }; + }; + + it("sells the old-token vault into the Raydium pool and lands in Sold with exact constant-product proceeds", async function () { + const { relaunch, pool } = await setupRaydiumSellPending.call(this); + + await client.executeSell({ relaunch }); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(oldVaultBalance.toString(), "0"); + + // Default orientation puts the token on the pc side; the full input, + // fee included, lands in the pool. + const poolTokenBalance = await tokenBalance(this.banksClient, pool.pcVault); + assert.equal( + poolTokenBalance.toString(), + (POOL_BASE_RESERVE + DEPOSIT_AMOUNT).toString(), + ); + + const predicted = predictedQuoteOut(); + assert.equal( + storedRelaunch.quoteRecovered.toString(), + predicted.toString(), + ); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal(quoteVaultBalance.toString(), predicted.toString()); + + const poolQuoteBalance = await tokenBalance( + this.banksClient, + pool.coinVault, + ); + assert.equal( + poolQuoteBalance.toString(), + (WSOL_POOL_QUOTE_RESERVE - predicted).toString(), + ); + + assert.equal(storedRelaunch.usdcRecovered.toString(), "0"); + assert.equal(storedRelaunch.seqNum.toString(), "4"); + }); + + it("fails when min_quote_out is above the achievable proceeds, leaving state unchanged", async function () { + const { relaunch } = await setupRaydiumSellPending.call(this); + + try { + // The whole quote reserve is unreachable output for any sell. + await client.executeSell({ + relaunch, + minQuoteOut: new BN(WSOL_POOL_QUOTE_RESERVE.toString()), + }); + assert.fail("Should have thrown error"); + } catch (e) {} + + let storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + assert.equal(storedRelaunch.quoteRecovered.toString(), "0"); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(oldVaultBalance.toString(), DEPOSIT_AMOUNT.toString()); + + // The same sell with a live floor succeeds, so only the floor differed. + await client.executeSell({ relaunch }); + storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sold); + }); + + it("fails when a non-admin executes the sell", async function () { + const { relaunch, pool, oldMint } = + await setupRaydiumSellPending.call(this); + const nonAdmin = Keypair.generate(); + + try { + await client + .executeSellRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.coinVault, + ammPcVault: pool.pcVault, + minQuoteOut: new BN(0), + admin: nonAdmin.publicKey, + }) + .signers([nonAdmin]) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "ConstraintHasOne"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails before deposits close", async function () { + const { relaunch } = await setupRaydiumSellPending.call(this, { + close: false, + }); + + try { + await client.executeSell({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + }); + + it("fails when the sell has already been executed", async function () { + const { relaunch } = await setupRaydiumSellPending.call(this); + + await client.executeSell({ relaunch }); + + try { + await client.executeSell({ relaunch, minQuoteOut: new BN(1) }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotSellPending"); + } + }); + + it("fails after the grace period elapses", async function () { + const { relaunch } = await setupRaydiumSellPending.call(this); + await this.advanceBySeconds(ONE_DAY + 1); + + try { + await client.executeSell({ relaunch }); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "GracePeriodElapsed"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + + const oldVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(oldVaultBalance.toString(), DEPOSIT_AMOUNT.toString()); + }); + + it("fails when the pump-venue execute_sell is called on a Raydium-source relaunch", async function () { + const { relaunch, pool, oldMint } = + await setupRaydiumSellPending.call(this); + + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.connection); + + try { + // The pump-specific accounts are unchecked until the CPI, so arbitrary + // stand-ins get the instruction as far as the venue gate. + await client + .executeSellIx({ + relaunch, + oldMint, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.pcVault, + poolQuoteTokenAccount: pool.coinVault, + coinCreator: Keypair.generate().publicKey, + protocolFeeRecipient, + buybackFeeRecipient, + minQuoteOut: new BN(0), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "WrongSourceVenue"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails when execute_sell_raydium is called on a PumpSwap-source relaunch", async function () { + const { relaunch, pool, oldMint } = await setupPumpSellPending.call(this); + + try { + await client + .executeSellRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.poolQuoteTokenAccount, + ammPcVault: pool.poolBaseTokenAccount, + minQuoteOut: new BN(0), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "WrongSourceVenue"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.sellPending); + }); + + it("fails when the passed coin/pc vault accounts do not match the pool's stored vaults", async function () { + const { relaunch, pool, oldMint } = + await setupRaydiumSellPending.call(this); + + // Swapped vaults: the coin-vault pin fails first. + try { + await client + .executeSellRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.pcVault, + ammPcVault: pool.coinVault, + minQuoteOut: new BN(0), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "SourcePoolNotCanonical"); + } + + // A foreign token account in the pc slot. + const storedRelaunch = await client.fetchRelaunch(relaunch); + try { + await client + .executeSellRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.coinVault, + ammPcVault: storedRelaunch.sourceQuoteVault, + minQuoteOut: new BN(0), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "SourcePoolNotCanonical"); + } + + assert.isDefined((await client.fetchRelaunch(relaunch)).state.sellPending); + }); +} From 6f083017d029a9d1659bb0c3caf9ee759fe668db Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 20:10:55 -0700 Subject: [PATCH 28/31] deposit_via_buy_radium ix, sdk, tests --- .../src/instructions/deposit_via_buy.rs | 6 +- .../instructions/deposit_via_buy_raydium.rs | 229 +++++++ programs/relaunch/src/instructions/mod.rs | 2 + programs/relaunch/src/lib.rs | 8 + sdk/src/relaunch/v0.1/RelaunchClient.ts | 207 +++++-- sdk/src/relaunch/v0.1/types/relaunch.ts | 256 ++++++++ tasks.md | 12 +- tests/relaunch/main.test.ts | 2 + .../unit/depositViaBuyRaydium.test.ts | 574 ++++++++++++++++++ 9 files changed, 1238 insertions(+), 58 deletions(-) create mode 100644 programs/relaunch/src/instructions/deposit_via_buy_raydium.rs create mode 100644 tests/relaunch/unit/depositViaBuyRaydium.test.ts diff --git a/programs/relaunch/src/instructions/deposit_via_buy.rs b/programs/relaunch/src/instructions/deposit_via_buy.rs index 1183ef841..fe4d6615d 100644 --- a/programs/relaunch/src/instructions/deposit_via_buy.rs +++ b/programs/relaunch/src/instructions/deposit_via_buy.rs @@ -66,9 +66,6 @@ pub struct DepositViaBuy<'info> { #[account(mut)] pub source_quote_vault: Box>, - // token::* over associated_token::* deliberately: the depositor may fund - // the buy from any quote account they own (e.g. an ephemeral WSOL - // account); the refund returns to the same account. #[account( mut, token::mint = source_quote_mint, @@ -76,8 +73,7 @@ pub struct DepositViaBuy<'info> { )] pub depositor_quote_account: Box>, - /// CHECK: fingerprint-validated at init and pinned by has_one; pump_amm - /// rechecks its internal consistency. + /// CHECK: fingerprint-validated at init and pinned by has_one #[account(mut)] pub source_pool: UncheckedAccount<'info>, diff --git a/programs/relaunch/src/instructions/deposit_via_buy_raydium.rs b/programs/relaunch/src/instructions/deposit_via_buy_raydium.rs new file mode 100644 index 000000000..56671b787 --- /dev/null +++ b/programs/relaunch/src/instructions/deposit_via_buy_raydium.rs @@ -0,0 +1,229 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{self, Mint, Token, TokenAccount}; + +use crate::error::RelaunchError; +use crate::events::{CommonFields, TokensDepositedViaBuyEvent}; +use crate::raydium_amm; +use crate::state::{DepositRecord, Relaunch, RelaunchState, SourceVenue}; +use crate::{raydium_amm_authority, raydium_amm_program}; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct DepositViaBuyRaydiumArgs { + /// The exact amount of old tokens to buy off the source pool + /// (swap_base_out_v2 is exact-output). + pub base_out: u64, + /// The depositor's live slippage cap on the quote spent, inclusive of + /// the AMM's 25 bps fee. + pub max_quote_in: u64, +} + +#[event_cpi] +#[derive(Accounts)] +pub struct DepositViaBuyRaydium<'info> { + #[account( + mut, + has_one = source_quote_mint, + has_one = source_pool, + has_one = relaunch_signer, + has_one = old_token_vault, + has_one = source_quote_vault, + )] + pub relaunch: Box>, + + #[account( + init_if_needed, + payer = payer, + space = 8 + DepositRecord::INIT_SPACE, + seeds = [b"deposit_record", relaunch.key().as_ref(), depositor.key().as_ref()], + bump + )] + pub deposit_record: Box>, + + pub depositor: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: the vault authority that signs the buy and the refund + pub relaunch_signer: UncheckedAccount<'info>, + + pub source_quote_mint: Box>, + + #[account(mut)] + pub old_token_vault: Box>, + + #[account(mut)] + pub source_quote_vault: Box>, + + #[account( + mut, + token::mint = source_quote_mint, + token::authority = depositor, + )] + pub depositor_quote_account: Box>, + + /// CHECK: fingerprint-validated at init and pinned by has_one + #[account(mut)] + pub source_pool: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = raydium_amm_authority::id())] + pub amm_authority: UncheckedAccount<'info>, + + /// CHECK: pinned against the pool's stored field in validate() + #[account(mut)] + pub amm_coin_vault: UncheckedAccount<'info>, + + /// CHECK: pinned against the pool's stored field in validate() + #[account(mut)] + pub amm_pc_vault: UncheckedAccount<'info>, + + /// CHECK: fixed address + #[account(address = raydium_amm_program::id())] + pub raydium_amm_program: UncheckedAccount<'info>, + + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, +} + +impl DepositViaBuyRaydium<'_> { + pub fn validate(&self, args: &DepositViaBuyRaydiumArgs) -> Result<()> { + require!( + self.relaunch.state == RelaunchState::Live, + RelaunchError::RelaunchNotLive + ); + + require!( + self.relaunch.source_venue == SourceVenue::RaydiumAmmV4, + RelaunchError::WrongSourceVenue + ); + + let clock = Clock::get()?; + require_gt!( + self.relaunch.unix_timestamp_started.unwrap() + + self.relaunch.seconds_for_deposits as i64, + clock.unix_timestamp, + RelaunchError::DepositWindowClosed + ); + + require_gt!(args.base_out, 0, RelaunchError::InvalidAmount); + require_gt!(args.max_quote_in, 0, RelaunchError::InvalidAmount); + + require_gte!( + self.depositor_quote_account.amount, + args.max_quote_in, + RelaunchError::InsufficientFunds + ); + + // Pin the vaults to the pool's stored fields. + let pool = raydium_amm::RaydiumPool::try_parse(&self.source_pool.try_borrow_data()?)?; + require_keys_eq!( + self.amm_coin_vault.key(), + pool.coin_vault, + RelaunchError::SourcePoolNotCanonical + ); + require_keys_eq!( + self.amm_pc_vault.key(), + pool.pc_vault, + RelaunchError::SourcePoolNotCanonical + ); + + Ok(()) + } + + pub fn handle(ctx: Context, args: DepositViaBuyRaydiumArgs) -> Result<()> { + let relaunch_key = ctx.accounts.relaunch.key(); + + let seeds = &[ + b"relaunch_signer", + relaunch_key.as_ref(), + &[ctx.accounts.relaunch.relaunch_signer_bump], + ]; + let signer = &[&seeds[..]]; + + let old_before = ctx.accounts.old_token_vault.amount; + let quote_vault_before = ctx.accounts.source_quote_vault.amount; + + token::transfer_checked( + CpiContext::new( + ctx.accounts.token_program.to_account_info(), + token::TransferChecked { + from: ctx.accounts.depositor_quote_account.to_account_info(), + mint: ctx.accounts.source_quote_mint.to_account_info(), + to: ctx.accounts.source_quote_vault.to_account_info(), + authority: ctx.accounts.depositor.to_account_info(), + }, + ), + args.max_quote_in, + ctx.accounts.source_quote_mint.decimals, + )?; + + // Exact-out: the CPI pulls only the input the buy needs from the + // quote vault, leaving the rest for the refund below. + raydium_amm::swap_base_out_v2( + raydium_amm::Swap { + token_program: ctx.accounts.token_program.to_account_info(), + amm: ctx.accounts.source_pool.to_account_info(), + amm_authority: ctx.accounts.amm_authority.to_account_info(), + amm_coin_vault: ctx.accounts.amm_coin_vault.to_account_info(), + amm_pc_vault: ctx.accounts.amm_pc_vault.to_account_info(), + user_source_token_account: ctx.accounts.source_quote_vault.to_account_info(), + user_destination_token_account: ctx.accounts.old_token_vault.to_account_info(), + user_source_owner: ctx.accounts.relaunch_signer.to_account_info(), + }, + args.max_quote_in, + args.base_out, + signer, + )?; + + ctx.accounts.old_token_vault.reload()?; + ctx.accounts.source_quote_vault.reload()?; + + let tokens_bought = ctx.accounts.old_token_vault.amount - old_before; + let quote_refund = ctx.accounts.source_quote_vault.amount - quote_vault_before; + let quote_spent = args.max_quote_in - quote_refund; + + if quote_refund > 0 { + token::transfer_checked( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + token::TransferChecked { + from: ctx.accounts.source_quote_vault.to_account_info(), + mint: ctx.accounts.source_quote_mint.to_account_info(), + to: ctx.accounts.depositor_quote_account.to_account_info(), + authority: ctx.accounts.relaunch_signer.to_account_info(), + }, + signer, + ), + quote_refund, + ctx.accounts.source_quote_mint.decimals, + )?; + } + + ctx.accounts.deposit_record.credit( + relaunch_key, + ctx.accounts.depositor.key(), + tokens_bought, + ctx.bumps.deposit_record, + ); + + let relaunch = &mut ctx.accounts.relaunch; + relaunch.total_deposited += tokens_bought; + relaunch.seq_num += 1; + + let clock = Clock::get()?; + emit_cpi!(TokensDepositedViaBuyEvent { + common: CommonFields::new(&clock, ctx.accounts.relaunch.seq_num), + relaunch: relaunch_key, + depositor: ctx.accounts.depositor.key(), + deposit_record: ctx.accounts.deposit_record.key(), + amount: tokens_bought, + quote_spent, + total_deposited: ctx.accounts.relaunch.total_deposited, + total_deposited_by_depositor: ctx.accounts.deposit_record.amount_deposited, + deposit_record_seq_num: ctx.accounts.deposit_record.seq_num, + }); + + Ok(()) + } +} diff --git a/programs/relaunch/src/instructions/mod.rs b/programs/relaunch/src/instructions/mod.rs index 10abe97d2..594248797 100644 --- a/programs/relaunch/src/instructions/mod.rs +++ b/programs/relaunch/src/instructions/mod.rs @@ -4,6 +4,7 @@ pub mod close_deposits; pub mod complete_relaunch; pub mod deposit; pub mod deposit_via_buy; +pub mod deposit_via_buy_raydium; pub mod execute_sell; pub mod execute_sell_raydium; pub mod execute_usdc_swap; @@ -17,6 +18,7 @@ pub use close_deposits::*; pub use complete_relaunch::*; pub use deposit::*; pub use deposit_via_buy::*; +pub use deposit_via_buy_raydium::*; pub use execute_sell::*; pub use execute_sell_raydium::*; pub use execute_usdc_swap::*; diff --git a/programs/relaunch/src/lib.rs b/programs/relaunch/src/lib.rs index 9944666e0..e4513406b 100644 --- a/programs/relaunch/src/lib.rs +++ b/programs/relaunch/src/lib.rs @@ -58,6 +58,14 @@ pub mod relaunch { DepositViaBuy::handle(ctx, args) } + #[access_control(ctx.accounts.validate(&args))] + pub fn deposit_via_buy_raydium( + ctx: Context, + args: DepositViaBuyRaydiumArgs, + ) -> Result<()> { + DepositViaBuyRaydium::handle(ctx, args) + } + #[access_control(ctx.accounts.validate())] pub fn close_deposits(ctx: Context) -> Result<()> { CloseDeposits::handle(ctx) diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 2c9a71d64..414278de5 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -76,6 +76,10 @@ export type CreateRelaunchClientParams = { relaunchProgramId?: PublicKey; }; +function ceilDiv(a: bigint, b: bigint): bigint { + return (a + b - 1n) / b; +} + export class RelaunchClient { public readonly provider: AnchorProvider; public readonly relaunchProgram: Program; @@ -384,6 +388,73 @@ export class RelaunchClient { ]); } + depositViaBuyRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint, + sourcePool, + ammCoinVault, + ammPcVault, + baseOut, + maxQuoteIn, + depositor = this.provider.publicKey, + payer = this.provider.publicKey, + depositorQuoteAccount = getAssociatedTokenAddressSync( + sourceQuoteMint, + depositor, + ), + }: { + relaunch: PublicKey; + oldMint: PublicKey; + sourceQuoteMint: PublicKey; + sourcePool: PublicKey; + ammCoinVault: PublicKey; + ammPcVault: PublicKey; + baseOut: BN; + maxQuoteIn: BN; + depositor?: PublicKey; + payer?: PublicKey; + depositorQuoteAccount?: PublicKey; + }) { + const relaunchSigner = this.getRelaunchSignerAddress({ relaunch }); + const depositRecord = this.getDepositRecordAddress({ + relaunch, + depositor, + }); + + const oldTokenVault = getAssociatedTokenAddressSync( + oldMint, + relaunchSigner, + true, + ); + const sourceQuoteVault = getAssociatedTokenAddressSync( + sourceQuoteMint, + relaunchSigner, + true, + ); + + return this.relaunchProgram.methods + .depositViaBuyRaydium({ baseOut, maxQuoteIn }) + .accounts({ + relaunch, + depositRecord, + depositor, + payer, + relaunchSigner, + sourceQuoteMint, + oldTokenVault, + sourceQuoteVault, + depositorQuoteAccount, + sourcePool, + ammAuthority: RAYDIUM_AMM_AUTHORITY, + ammCoinVault, + ammPcVault, + raydiumAmmProgram: RAYDIUM_AMM_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }); + } + executeSellIx({ relaunch, oldMint, @@ -846,15 +917,54 @@ export class RelaunchClient { }).rpc(); } + // Tops the provider wallet's WSOL ATA up to maxQuoteIn, wrapping any + // shortfall from SOL in a separate preparatory transaction + private async wrapWsolShortfall(maxQuoteIn: BN): Promise { + const wsolAta = getAssociatedTokenAddressSync( + NATIVE_MINT, + this.provider.publicKey, + ); + let wsolAtaAccount: AccountInfo | null = null; + try { + wsolAtaAccount = await this.provider.connection.getAccountInfo(wsolAta); + } catch { + // anchor-bankrun's connection proxy throws for missing accounts + // instead of returning null. + } + const wsolBalance = + wsolAtaAccount === null + ? 0n + : AccountLayout.decode(wsolAtaAccount.data).amount; + const shortfall = BigInt(maxQuoteIn.toString()) - wsolBalance; + if (shortfall > 0n) { + await this.provider.sendAndConfirm!( + new Transaction().add( + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + wsolAta, + this.provider.publicKey, + NATIVE_MINT, + ), + SystemProgram.transfer({ + fromPubkey: this.provider.publicKey, + toPubkey: wsolAta, + lamports: Number(shortfall), + }), + createSyncNativeInstruction(wsolAta), + ), + ); + } + } + // Buys baseOut old tokens off the source pool as the provider wallet and - // credits them as a deposit, deriving the pump account set like - // executeSell. When maxQuoteIn is not given, it is computed live from the - // pool reserves: the constant-product input for the exact output plus - // slippageBps (which must also cover pump's swap fees). For WSOL-quoted - // pools, any shortfall in the depositor's WSOL ATA is wrapped from SOL in - // a separate preparatory transaction — the buy transaction sits within a - // few bytes of the 1232-byte legacy size limit and cannot absorb the wrap - // instructions for Token-2022 old mints. + // credits them as a deposit, dispatching on the relaunch's stored source + // venue and deriving the venue's account set like executeSell. When + // maxQuoteIn is not given, it is computed live from the pool reserves: the + // constant-product input for the exact output plus slippageBps. Pump's + // swap fees are not modeled, so slippageBps must also cover them; + // Raydium's flat 25 bps fee is modeled exactly, so slippageBps only covers + // price movement. For WSOL-quoted pools, any shortfall in the depositor's + // WSOL ATA is wrapped from SOL in a separate preparatory transaction. async depositViaBuy({ relaunch, baseOut, @@ -871,6 +981,52 @@ export class RelaunchClient { throw new Error(`relaunch ${relaunch.toBase58()} does not exist`); } + if (storedRelaunch.sourceVenue.raydiumAmmV4 !== undefined) { + const pool = await fetchRaydiumPool( + this.provider.connection, + storedRelaunch.sourcePool, + ); + + if (maxQuoteIn === undefined) { + const tokenIsCoin = pool.coinMint.equals(storedRelaunch.oldMint); + const [tokenReserve, quoteReserve] = await Promise.all( + [ + tokenIsCoin ? pool.coinVault : pool.pcVault, + tokenIsCoin ? pool.pcVault : pool.coinVault, + ].map((address) => this.fetchTokenBalance(address)), + ); + const baseOutBig = BigInt(baseOut.toString()); + if (baseOutBig >= tokenReserve) { + throw new Error( + `baseOut ${baseOutBig} exceeds the pool's token reserve ${tokenReserve}`, + ); + } + // The exact-out input is ceil-rounded, with the 25 bps fee + // ceil-rounded on top of it (the fee stays in the pool). + const inBeforeFee = ceilDiv( + quoteReserve * baseOutBig, + tokenReserve - baseOutBig, + ); + const grossIn = ceilDiv(inBeforeFee * 10_000n, 9_975n); + const cap = (grossIn * (10_000n + BigInt(slippageBps))) / 10_000n; + maxQuoteIn = new BN(cap.toString()); + } + + // Raydium sources are WSOL-quoted by construction. + await this.wrapWsolShortfall(maxQuoteIn); + + return this.depositViaBuyRaydiumIx({ + relaunch, + oldMint: storedRelaunch.oldMint, + sourceQuoteMint: storedRelaunch.sourceQuoteMint, + sourcePool: storedRelaunch.sourcePool, + ammCoinVault: pool.coinVault, + ammPcVault: pool.pcVault, + baseOut, + maxQuoteIn, + }).rpc(); + } + const oldMintAccount = await this.provider.connection.getAccountInfo( storedRelaunch.oldMint, ); @@ -905,40 +1061,7 @@ export class RelaunchClient { } if (storedRelaunch.sourceQuoteMint.equals(NATIVE_MINT)) { - const wsolAta = getAssociatedTokenAddressSync( - NATIVE_MINT, - this.provider.publicKey, - ); - let wsolAtaAccount: AccountInfo | null = null; - try { - wsolAtaAccount = await this.provider.connection.getAccountInfo(wsolAta); - } catch { - // anchor-bankrun's connection proxy throws for missing accounts - // instead of returning null. - } - const wsolBalance = - wsolAtaAccount === null - ? 0n - : AccountLayout.decode(wsolAtaAccount.data).amount; - const shortfall = BigInt(maxQuoteIn.toString()) - wsolBalance; - if (shortfall > 0n) { - await this.provider.sendAndConfirm!( - new Transaction().add( - createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, - wsolAta, - this.provider.publicKey, - NATIVE_MINT, - ), - SystemProgram.transfer({ - fromPubkey: this.provider.publicKey, - toPubkey: wsolAta, - lamports: Number(shortfall), - }), - createSyncNativeInstruction(wsolAta), - ), - ); - } + await this.wrapWsolShortfall(maxQuoteIn); } return this.depositViaBuyIx({ diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 4bafc63e2..15d0b6bef 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -423,6 +423,110 @@ export type Relaunch = { }, ]; }, + { + name: "depositViaBuyRaydium"; + accounts: [ + { + name: "relaunch"; + isMut: true; + isSigner: false; + }, + { + name: "depositRecord"; + isMut: true; + isSigner: false; + }, + { + name: "depositor"; + isMut: false; + isSigner: true; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "relaunchSigner"; + isMut: false; + isSigner: false; + }, + { + name: "sourceQuoteMint"; + isMut: false; + isSigner: false; + }, + { + name: "oldTokenVault"; + isMut: true; + isSigner: false; + }, + { + name: "sourceQuoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "depositorQuoteAccount"; + isMut: true; + isSigner: false; + }, + { + name: "sourcePool"; + isMut: true; + isSigner: false; + docs: ["rechecks its internal consistency."]; + }, + { + name: "ammAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "ammCoinVault"; + isMut: true; + isSigner: false; + }, + { + name: "ammPcVault"; + isMut: true; + isSigner: false; + }, + { + name: "raydiumAmmProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "eventAuthority"; + isMut: false; + isSigner: false; + }, + { + name: "program"; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: "args"; + type: { + defined: "DepositViaBuyRaydiumArgs"; + }; + }, + ]; + }, { name: "closeDeposits"; accounts: [ @@ -1350,6 +1454,30 @@ export type Relaunch = { ]; }; }, + { + name: "DepositViaBuyRaydiumArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "baseOut"; + docs: [ + "The exact amount of old tokens to buy off the source pool", + "(swap_base_out_v2 is exact-output).", + ]; + type: "u64"; + }, + { + name: "maxQuoteIn"; + docs: [ + "The depositor's live slippage cap on the quote spent, inclusive of", + "the AMM's 25 bps fee.", + ]; + type: "u64"; + }, + ]; + }; + }, { name: "DepositViaBuyArgs"; type: { @@ -2576,6 +2704,110 @@ export const IDL: Relaunch = { }, ], }, + { + name: "depositViaBuyRaydium", + accounts: [ + { + name: "relaunch", + isMut: true, + isSigner: false, + }, + { + name: "depositRecord", + isMut: true, + isSigner: false, + }, + { + name: "depositor", + isMut: false, + isSigner: true, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "relaunchSigner", + isMut: false, + isSigner: false, + }, + { + name: "sourceQuoteMint", + isMut: false, + isSigner: false, + }, + { + name: "oldTokenVault", + isMut: true, + isSigner: false, + }, + { + name: "sourceQuoteVault", + isMut: true, + isSigner: false, + }, + { + name: "depositorQuoteAccount", + isMut: true, + isSigner: false, + }, + { + name: "sourcePool", + isMut: true, + isSigner: false, + docs: ["rechecks its internal consistency."], + }, + { + name: "ammAuthority", + isMut: false, + isSigner: false, + }, + { + name: "ammCoinVault", + isMut: true, + isSigner: false, + }, + { + name: "ammPcVault", + isMut: true, + isSigner: false, + }, + { + name: "raydiumAmmProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "eventAuthority", + isMut: false, + isSigner: false, + }, + { + name: "program", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "DepositViaBuyRaydiumArgs", + }, + }, + ], + }, { name: "closeDeposits", accounts: [ @@ -3503,6 +3735,30 @@ export const IDL: Relaunch = { ], }, }, + { + name: "DepositViaBuyRaydiumArgs", + type: { + kind: "struct", + fields: [ + { + name: "baseOut", + docs: [ + "The exact amount of old tokens to buy off the source pool", + "(swap_base_out_v2 is exact-output).", + ], + type: "u64", + }, + { + name: "maxQuoteIn", + docs: [ + "The depositor's live slippage cap on the quote spent, inclusive of", + "the AMM's 25 bps fee.", + ], + type: "u64", + }, + ], + }, + }, { name: "DepositViaBuyArgs", type: { diff --git a/tasks.md b/tasks.md index 00a2a9313..4afc9de8c 100644 --- a/tasks.md +++ b/tasks.md @@ -23,21 +23,11 @@ ## Tasks -### Phase 3: deposit_via_buy_raydium - -> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 3 — deposit_via_buy_raydium" - -- [NEXT] 3.1 `deposit_via_buy_raydium` (instruction + SDK + tests) - - Transfer `max_quote_in` → CPI `swap_base_out_v2(max_quote_in, base_out)` → refund unspent → `DepositRecord::credit` measured delta - - No volume accumulators, no fee recipients; same `TokensDepositedViaBuyEvent` - - SDK: `depositViaBuyRaydiumIx` + `depositViaBuy()` venue dispatch (wrap logic unchanged) - - Tests in new `tests/relaunch/unit/depositViaBuyRaydium.test.ts` per plan Stage 3, incl. exact-refund assertion and wrong-venue gating both directions - ### Phase 4: Lifecycle, ALT, budgets > Reference: `relaunch-raydium-implementation-plan.md` → "Stage 4 — lifecycle, ALT, budgets" -- [ ] 4.1 Full-lifecycle integration tests +- [NEXT] 4.1 Full-lifecycle integration tests - `tests/integration/relaunch.test.ts`: Raydium-source happy path with money-math identities at each hop; threshold-miss failure path with refunds - Matrix note: WSOL × classic-SPL only — other combinations impossible for this venue diff --git a/tests/relaunch/main.test.ts b/tests/relaunch/main.test.ts index 56bc95e8e..ec14b4362 100644 --- a/tests/relaunch/main.test.ts +++ b/tests/relaunch/main.test.ts @@ -4,6 +4,7 @@ import initializeRelaunch from "./unit/initializeRelaunch.test.js"; import startDeposits from "./unit/startDeposits.test.js"; import deposit from "./unit/deposit.test.js"; import depositViaBuy from "./unit/depositViaBuy.test.js"; +import depositViaBuyRaydium from "./unit/depositViaBuyRaydium.test.js"; import closeDeposits from "./unit/closeDeposits.test.js"; import executeSell from "./unit/executeSell.test.js"; import executeSellRaydium from "./unit/executeSellRaydium.test.js"; @@ -30,6 +31,7 @@ export default function suite() { describe("#start_deposits", startDeposits); describe("#deposit", deposit); describe("#deposit_via_buy", depositViaBuy); + describe("#deposit_via_buy_raydium", depositViaBuyRaydium); describe("#close_deposits", closeDeposits); describe("#execute_sell", executeSell); describe("#execute_sell_raydium", executeSellRaydium); diff --git a/tests/relaunch/unit/depositViaBuyRaydium.test.ts b/tests/relaunch/unit/depositViaBuyRaydium.test.ts new file mode 100644 index 000000000..8adfe4089 --- /dev/null +++ b/tests/relaunch/unit/depositViaBuyRaydium.test.ts @@ -0,0 +1,574 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; +import { BN } from "bn.js"; +import { BanksClient } from "solana-bankrun"; +import { + getDepositRecordAddr, + getPumpFeeRecipients, + RelaunchClient, +} from "@metadaoproject/programs"; +import { setupRelaunch } from "../utils.js"; +import { writePumpPool } from "../pumpAmm.js"; +import { writeRaydiumPool, RaydiumPool } from "../raydiumAmm.js"; +import { wrapSol } from "../whirlpool.js"; + +const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens +const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL + +const ONE_WEEK = 60 * 60 * 24 * 7; +const ONE_DAY = 60 * 60 * 24; + +const BASE_OUT = 10_000n * 10n ** 6n; // 10k old tokens + +async function tokenBalance( + banksClient: BanksClient, + address: PublicKey, +): Promise { + const raw = await banksClient.getAccount(address); + if (!raw) return 0n; + return token.unpackAccount(address, { + ...raw, + data: Buffer.from(raw.data), + } as any).amount; +} + +function ceilDiv(a: bigint, b: bigint): bigint { + return (a + b - 1n) / b; +} + +// The exact input the AMM pulls for an exact-output buy: the constant-product +// input, ceil-rounded, with the 25 bps fee ceil-rounded on top of it (the fee +// stays in the pool). +function raydiumExactOutInput( + amountOut: bigint, + inReserve: bigint, + outReserve: bigint, +): bigint { + const inBeforeFee = ceilDiv(inReserve * amountOut, outReserve - amountOut); + return ceilDiv(inBeforeFee * 10_000n, 9_975n); +} + +export default function suite() { + let client: RelaunchClient; + + before(function () { + client = this.relaunch; + }); + + const setupLiveRelaunch = async function ( + this: Mocha.Context, + { start = true }: { start?: boolean } = {}, + ): Promise<{ + relaunch: PublicKey; + pool: RaydiumPool; + oldMint: PublicKey; + oldTokenProgram: PublicKey; + payerOldTokenAccount: PublicKey; + }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = writeRaydiumPool({ + context: this.context, + oldMint: setup.oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + + if (start) { + await client.startDepositsIx({ relaunch }).rpc(); + } + + return { ...setup, pool, relaunch }; + }; + + const depositViaBuyRaydiumIx = ({ + relaunch, + pool, + oldMint, + baseOut, + maxQuoteIn, + }: { + relaunch: PublicKey; + pool: RaydiumPool; + oldMint: PublicKey; + baseOut: bigint; + maxQuoteIn: bigint; + }) => + client.depositViaBuyRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.coinVault, + ammPcVault: pool.pcVault, + baseOut: new BN(baseOut.toString()), + maxQuoteIn: new BN(maxQuoteIn.toString()), + }); + + it("buys exact base_out off the Raydium pool, credits it, and refunds exactly the unspent quote", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + const wsolAta = await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + const maxQuoteIn = 2n * 10n ** 9n; + + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn, + }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), BASE_OUT.toString()); + assert.equal(storedRelaunch.seqNum.toString(), "2"); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.relaunch.equals(relaunch)); + assert.isTrue(record.depositor.equals(this.payer.publicKey)); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + assert.isFalse(record.claimed); + assert.equal(record.seqNum.toString(), "0"); + const [, recordBump] = getDepositRecordAddr({ + programId: client.getProgramId(), + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.pdaBump, recordBump); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // Default orientation puts the token on the pc side, WSOL on coin. + const poolTokenBalance = await tokenBalance(this.banksClient, pool.pcVault); + assert.equal( + poolTokenBalance.toString(), + (POOL_BASE_RESERVE - BASE_OUT).toString(), + ); + + // The exact refund: the buy consumed exactly the exact-out input and + // everything above it returned to the depositor. + const predictedIn = raydiumExactOutInput( + BASE_OUT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + assert.equal((wsolBefore - wsolAfter).toString(), predictedIn.toString()); + + // The fee stays in the pool: the coin vault gains the full input. + const poolQuoteBalance = await tokenBalance( + this.banksClient, + pool.coinVault, + ); + assert.equal( + poolQuoteBalance.toString(), + (WSOL_POOL_QUOTE_RESERVE + predictedIn).toString(), + ); + + const quoteVaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.sourceQuoteVault, + ); + assert.equal(quoteVaultBalance.toString(), "0"); + }); + + it("computes max_quote_in from pool reserves when omitted", async function () { + const { relaunch } = await setupLiveRelaunch.call(this); + // Pre-fund past any computable cap so the wrap path stays out of the + // picture and the spend is a clean delta. + const wsolAta = await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BASE_OUT.toString()), + }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const { oldTokenVault } = await client.fetchRelaunch(relaunch); + const vaultBalance = await tokenBalance(this.banksClient, oldTokenVault); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The computed cap is the exact-out input plus the default 100 bps of + // slippage; the CPI pulls exactly the formula input and the cushion + // never leaves the depositor. + const predictedIn = raydiumExactOutInput( + BASE_OUT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + assert.equal((wsolBefore - wsolAfter).toString(), predictedIn.toString()); + }); + + it("wraps the depositor's SOL shortfall", async function () { + const { relaunch } = await setupLiveRelaunch.call(this); + + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + this.payer.publicKey, + ); + // Whatever WSOL is left over from earlier tests, this max_quote_in + // forces a 2-SOL shortfall that the convenience method must wrap. + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + const maxQuoteIn = wsolBefore + 2n * 10n ** 9n; + + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN(maxQuoteIn.toString()), + }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const { oldTokenVault } = await client.fetchRelaunch(relaunch); + const vaultBalance = await tokenBalance(this.banksClient, oldTokenVault); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The wrap topped the ATA up to exactly max_quote_in before the buy + // spent from it, so the remainder is max_quote_in minus the exact-out + // input. + const predictedIn = raydiumExactOutInput( + BASE_OUT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + assert.equal((maxQuoteIn - wsolAfter).toString(), predictedIn.toString()); + }); + + it("accumulates buy-deposits and direct deposits in the same record", async function () { + const { relaunch, pool, oldMint, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + await client + .depositIx({ + relaunch, + oldMint, + oldTokenProgram, + amount: new BN(100_000_000), // 100 tokens + }) + .rpc(); + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: 50n * 10n ** 6n, // 50 tokens + maxQuoteIn: 10n ** 9n, + }).rpc(); + // A second buy exercises the existing-record branch. + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: 25n * 10n ** 6n, // 25 tokens + maxQuoteIn: 10n ** 9n, + }).rpc(); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.equal(record.amountDeposited.toString(), "175000000"); + assert.equal(record.seqNum.toString(), "2"); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "175000000"); + assert.equal(storedRelaunch.seqNum.toString(), "4"); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(vaultBalance.toString(), "175000000"); + }); + + it("fails when max_quote_in is too tight", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + const wsolAta = await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + + const predictedIn = raydiumExactOutInput( + BASE_OUT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + try { + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn: predictedIn - 1n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) {} + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + + const depositRecord = client.getDepositRecordAddress({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isNull(await this.banksClient.getAccount(depositRecord)); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(vaultBalance.toString(), "0"); + + // The whole transaction reverted, including the quote pull. + const wsolAfter = await tokenBalance(this.banksClient, wsolAta); + assert.equal(wsolAfter.toString(), wsolBefore.toString()); + }); + + it("fails to buy zero tokens", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: 0n, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InvalidAmount"); + } + }); + + it("fails with a zero max_quote_in", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn: 0n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "InvalidAmount"); + } + }); + + it("fails before deposits start", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this, { + start: false, + }); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "RelaunchNotLive"); + } + }); + + it("fails after the deposit window closes", async function () { + const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + await this.advanceBySeconds(ONE_WEEK); + + try { + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn: 10n ** 9n, + }).rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "DepositWindowClosed"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + }); + + it("refunds buy-credited tokens as old tokens after the relaunch fails", async function () { + const { relaunch, pool, oldMint, payerOldTokenAccount } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 2n * 10n ** 9n); + + await depositViaBuyRaydiumIx({ + relaunch, + pool, + oldMint, + baseOut: BASE_OUT, + maxQuoteIn: 2n * 10n ** 9n, + }).rpc(); + + // 10k tokens misses the 10% threshold of the 1B supply. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.isDefined(storedRelaunch.state.failed); + + const oldBalanceBefore = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + + await client.claimRefund({ relaunch }); + + const record = await client.getDepositRecord({ + relaunch, + depositor: this.payer.publicKey, + }); + assert.isTrue(record.claimed); + + // The buy is not unwound: the depositor is refunded the old tokens the + // buy acquired. + const oldBalanceAfter = await tokenBalance( + this.banksClient, + payerOldTokenAccount, + ); + assert.equal( + (oldBalanceAfter - oldBalanceBefore).toString(), + BASE_OUT.toString(), + ); + + const vaultBalance = await tokenBalance( + this.banksClient, + storedRelaunch.oldTokenVault, + ); + assert.equal(vaultBalance.toString(), "0"); + }); + + it("fails when the pump-venue deposit_via_buy is called on a Raydium-source relaunch", async function () { + const { relaunch, pool, oldMint, oldTokenProgram } = + await setupLiveRelaunch.call(this); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + const { protocolFeeRecipient, buybackFeeRecipient } = + await getPumpFeeRecipients(this.connection); + + try { + // The pump-specific accounts are unchecked until the CPI, so arbitrary + // stand-ins get the instruction as far as the venue gate. + await client + .depositViaBuyIx({ + relaunch, + oldMint, + oldTokenProgram, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + poolBaseTokenAccount: pool.pcVault, + poolQuoteTokenAccount: pool.coinVault, + coinCreator: Keypair.generate().publicKey, + protocolFeeRecipient, + buybackFeeRecipient, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN((10n ** 9n).toString()), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "WrongSourceVenue"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + }); + + it("fails when deposit_via_buy_raydium is called on a PumpSwap-source relaunch", async function () { + const { oldMint } = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = await writePumpPool({ + context: this.context, + baseMint: oldMint, + quoteMint: token.NATIVE_MINT, + baseReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + baseTokenProgram: token.TOKEN_PROGRAM_ID, + }); + const { relaunch } = await client.initializeRelaunch({ + oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + await client.startDepositsIx({ relaunch }).rpc(); + await wrapSol(client.provider, this.payer, 10n ** 9n); + + try { + await client + .depositViaBuyRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.poolQuoteTokenAccount, + ammPcVault: pool.poolBaseTokenAccount, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN((10n ** 9n).toString()), + }) + .rpc(); + assert.fail("Should have thrown error"); + } catch (e) { + assert.include(e.message, "WrongSourceVenue"); + } + + const storedRelaunch = await client.fetchRelaunch(relaunch); + assert.equal(storedRelaunch.totalDeposited.toString(), "0"); + }); +} From 2ec6763b5db20ffaa265afd62b42a82985a2c941 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 20:56:15 -0700 Subject: [PATCH 29/31] relaunch integration tests - raydium ver --- tests/integration/relaunch.test.ts | 457 ++++++++++++++++++++++------- 1 file changed, 347 insertions(+), 110 deletions(-) diff --git a/tests/integration/relaunch.test.ts b/tests/integration/relaunch.test.ts index 7d4fd67a8..f5f42d2aa 100644 --- a/tests/integration/relaunch.test.ts +++ b/tests/integration/relaunch.test.ts @@ -18,7 +18,12 @@ import { DEFAULT_OLD_SUPPLY, } from "../relaunch/utils.js"; import { writePumpPool } from "../relaunch/pumpAmm.js"; -import { ensureWhirlpool, WhirlpoolFixture } from "../relaunch/whirlpool.js"; +import { writeRaydiumPool } from "../relaunch/raydiumAmm.js"; +import { + ensureWhirlpool, + WhirlpoolFixture, + wrapSol, +} from "../relaunch/whirlpool.js"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens // Small enough that the sell proceeds (~5 SOL) swap through the whirlpool @@ -58,6 +63,36 @@ async function tokenBalance( ).amount; } +function ceilDiv(a: bigint, b: bigint): bigint { + return (a + b - 1n) / b; +} + +// Raydium AMM v4 money math at its flat 25 bps fee, which stays in the pool. +// The exact input pulled for an exact-output buy: the constant-product input, +// ceil-rounded, with the fee ceil-rounded on top of it. +function raydiumBuyIn( + amountOut: bigint, + quoteReserve: bigint, + tokenReserve: bigint, +): bigint { + const inBeforeFee = ceilDiv( + quoteReserve * amountOut, + tokenReserve - amountOut, + ); + return ceilDiv(inBeforeFee * 10_000n, 9_975n); +} + +// The exact output of an exact-in sell: the fee is ceil-rounded off the +// input, the remainder swaps at constant product. +function raydiumSellOut( + amountIn: bigint, + tokenReserve: bigint, + quoteReserve: bigint, +): bigint { + const net = amountIn - ceilDiv(amountIn * 25n, 10_000n); + return (quoteReserve * net) / (tokenReserve + net); +} + export default function suite() { let client: RelaunchClient; let futarchyClient: FutarchyClient; @@ -117,6 +152,38 @@ export default function suite() { return { setup, pool, relaunch, newMint }; }; + // Raydium sources are WSOL-quoted classic-SPL by construction, so unlike + // the pump helper there is no matrix to parameterize. + const initializeLiveRaydiumRelaunch = async function (this: Mocha.Context) { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = writeRaydiumPool({ + context: this.context, + oldMint: setup.oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + const { relaunch, newMint } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: THRESHOLD_BPS, + teamAddress: this.payer.publicKey, + }); + + await client.startDepositsIx({ relaunch }).rpc(); + + return { setup, pool, relaunch, newMint }; + }; + // Creates the depositor's ATA and funds it with old tokens from the payer. const fundDepositor = async function ( this: Mocha.Context, @@ -176,114 +243,27 @@ export default function suite() { await builder.rpc(); }; - const runHappyPath = async function ( + // Drives a sold relaunch through the venue-invariant tail — the Orca leg + // for WSOL-quoted sources, completion, and the pro-rata claims — asserting + // identities that hold for every source venue. + const runCompletionTail = async function ( this: Mocha.Context, { - quoteMint, - oldTokenProgram, - }: { quoteMint: PublicKey; oldTokenProgram: PublicKey }, + relaunch, + newMint, + alice, + bob, + isWsol, + }: { + relaunch: PublicKey; + newMint: PublicKey; + alice: Keypair; + bob: Keypair; + isWsol: boolean; + }, ) { - const isWsol = quoteMint.equals(token.NATIVE_MINT); - const { setup, pool, relaunch, newMint } = - await initializeLiveRelaunch.call(this, { quoteMint, oldTokenProgram }); - let stored = await client.fetchRelaunch(relaunch); - assert.isDefined(stored.state.live); - assert.equal( - stored.oldSupplySnapshot.toString(), - DEFAULT_OLD_SUPPLY.toString(), - ); - assert.equal( - (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), - (TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY).toString(), - ); - - // Mixed entry: two direct depositors interleaved with a bought deposit. - const alice = Keypair.generate(); - const bob = Keypair.generate(); - await fundDepositor.call(this, setup, alice.publicKey, ALICE_DEPOSIT); - await fundDepositor.call(this, setup, bob.publicKey, BOB_DEPOSIT); - - await deposit.call(this, relaunch, setup, ALICE_DEPOSIT, alice); - await client.depositViaBuy({ - relaunch, - baseOut: new BN(BUY_DEPOSIT.toString()), - maxQuoteIn: new BN((isWsol ? 10n ** 9n : 2_000n * 10n ** 6n).toString()), - }); - await deposit.call(this, relaunch, setup, BOB_DEPOSIT, bob); - - stored = await client.fetchRelaunch(relaunch); - assert.equal(stored.totalDeposited.toString(), THRESHOLD_AMOUNT.toString()); - assert.equal( - ( - await tokenBalance( - this.banksClient, - stored.oldTokenVault, - oldTokenProgram, - ) - ).toString(), - THRESHOLD_AMOUNT.toString(), - ); - const expectedDeposits: [PublicKey, bigint][] = [ - [alice.publicKey, ALICE_DEPOSIT], - [bob.publicKey, BOB_DEPOSIT], - [this.payer.publicKey, BUY_DEPOSIT], - ]; - for (const [depositor, amount] of expectedDeposits) { - const record = await client.getDepositRecord({ relaunch, depositor }); - assert.equal(record.amountDeposited.toString(), amount.toString()); - } - - // The deposits land exactly on the threshold, so closing moves to - // SellPending. - await this.advanceBySeconds(ONE_WEEK); - await client.closeDepositsIx({ relaunch }).rpc(); - stored = await client.fetchRelaunch(relaunch); - assert.isDefined(stored.state.sellPending); - - const poolBaseBefore = await tokenBalance( - this.banksClient, - pool.poolBaseTokenAccount, - oldTokenProgram, - ); - const poolQuoteBefore = await tokenBalance( - this.banksClient, - pool.poolQuoteTokenAccount, - ); - - // 100 bps of slippage can exactly match pump's fee, leaving no rounding - // margin; give the sell floor explicit headroom. - await client.executeSell({ relaunch, slippageBps: 200 }); - - stored = await client.fetchRelaunch(relaunch); const quoteRecovered = BigInt(stored.quoteRecovered.toString()); - assert.equal( - ( - await tokenBalance( - this.banksClient, - stored.oldTokenVault, - oldTokenProgram, - ) - ).toString(), - "0", - ); - assert.equal( - ( - await tokenBalance( - this.banksClient, - pool.poolBaseTokenAccount, - oldTokenProgram, - ) - ).toString(), - (poolBaseBefore + THRESHOLD_AMOUNT).toString(), - ); - - // The proceeds are the constant-product output minus pump's fees. - const grossQuoteOut = - (poolQuoteBefore * THRESHOLD_AMOUNT) / - (poolBaseBefore + THRESHOLD_AMOUNT); - assert.isTrue(quoteRecovered > (grossQuoteOut * 97n) / 100n); - assert.isTrue(quoteRecovered <= grossQuoteOut); if (isWsol) { assert.isDefined(stored.state.sold); @@ -460,6 +440,124 @@ export default function suite() { assert.equal(stored.seqNum.toString(), isWsol ? "11" : "10"); }; + const runHappyPath = async function ( + this: Mocha.Context, + { + quoteMint, + oldTokenProgram, + }: { quoteMint: PublicKey; oldTokenProgram: PublicKey }, + ) { + const isWsol = quoteMint.equals(token.NATIVE_MINT); + const { setup, pool, relaunch, newMint } = + await initializeLiveRelaunch.call(this, { quoteMint, oldTokenProgram }); + + let stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.live); + assert.equal( + stored.oldSupplySnapshot.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), + (TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY).toString(), + ); + + // Mixed entry: two direct depositors interleaved with a bought deposit. + const alice = Keypair.generate(); + const bob = Keypair.generate(); + await fundDepositor.call(this, setup, alice.publicKey, ALICE_DEPOSIT); + await fundDepositor.call(this, setup, bob.publicKey, BOB_DEPOSIT); + + await deposit.call(this, relaunch, setup, ALICE_DEPOSIT, alice); + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BUY_DEPOSIT.toString()), + maxQuoteIn: new BN((isWsol ? 10n ** 9n : 2_000n * 10n ** 6n).toString()), + }); + await deposit.call(this, relaunch, setup, BOB_DEPOSIT, bob); + + stored = await client.fetchRelaunch(relaunch); + assert.equal(stored.totalDeposited.toString(), THRESHOLD_AMOUNT.toString()); + assert.equal( + ( + await tokenBalance( + this.banksClient, + stored.oldTokenVault, + oldTokenProgram, + ) + ).toString(), + THRESHOLD_AMOUNT.toString(), + ); + const expectedDeposits: [PublicKey, bigint][] = [ + [alice.publicKey, ALICE_DEPOSIT], + [bob.publicKey, BOB_DEPOSIT], + [this.payer.publicKey, BUY_DEPOSIT], + ]; + for (const [depositor, amount] of expectedDeposits) { + const record = await client.getDepositRecord({ relaunch, depositor }); + assert.equal(record.amountDeposited.toString(), amount.toString()); + } + + // The deposits land exactly on the threshold, so closing moves to + // SellPending. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.sellPending); + + const poolBaseBefore = await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ); + const poolQuoteBefore = await tokenBalance( + this.banksClient, + pool.poolQuoteTokenAccount, + ); + + // 100 bps of slippage can exactly match pump's fee, leaving no rounding + // margin; give the sell floor explicit headroom. + await client.executeSell({ relaunch, slippageBps: 200 }); + + stored = await client.fetchRelaunch(relaunch); + const quoteRecovered = BigInt(stored.quoteRecovered.toString()); + assert.equal( + ( + await tokenBalance( + this.banksClient, + stored.oldTokenVault, + oldTokenProgram, + ) + ).toString(), + "0", + ); + assert.equal( + ( + await tokenBalance( + this.banksClient, + pool.poolBaseTokenAccount, + oldTokenProgram, + ) + ).toString(), + (poolBaseBefore + THRESHOLD_AMOUNT).toString(), + ); + + // The proceeds are the constant-product output minus pump's fees. + const grossQuoteOut = + (poolQuoteBefore * THRESHOLD_AMOUNT) / + (poolBaseBefore + THRESHOLD_AMOUNT); + assert.isTrue(quoteRecovered > (grossQuoteOut * 97n) / 100n); + assert.isTrue(quoteRecovered <= grossQuoteOut); + + await runCompletionTail.call(this, { + relaunch, + newMint, + alice, + bob, + isWsol, + }); + }; + describe("happy path", function () { it("relaunches a classic-SPL token from a WSOL-quoted pool", async function () { await runHappyPath.call(this, { @@ -490,12 +588,13 @@ export default function suite() { }); }); - it("misses the threshold and refunds every deposit exactly", async function () { - const { setup, relaunch } = await initializeLiveRelaunch.call(this, { - quoteMint: token.NATIVE_MINT, - oldTokenProgram: token.TOKEN_PROGRAM_ID, - }); - + // Two direct depositors and a bought deposit that together miss the + // threshold, then exact refunds for all three — buy-credited tokens refund + // as old tokens just like direct ones, whatever the source venue. + const runThresholdMiss = async function ( + this: Mocha.Context, + { setup, relaunch }: { setup: RelaunchSetup; relaunch: PublicKey }, + ) { const alice = Keypair.generate(); const bob = Keypair.generate(); const aliceDeposit = 30_000_000n * 10n ** 6n; @@ -564,6 +663,14 @@ export default function suite() { (await tokenBalance(this.banksClient, stored.oldTokenVault)).toString(), "0", ); + }; + + it("misses the threshold and refunds every deposit exactly", async function () { + const { setup, relaunch } = await initializeLiveRelaunch.call(this, { + quoteMint: token.NATIVE_MINT, + oldTokenProgram: token.TOKEN_PROGRAM_ID, + }); + await runThresholdMiss.call(this, { setup, relaunch }); }); it("marks a stalled sell Failed after the grace period and refunds exactly", async function () { @@ -637,4 +744,134 @@ export default function suite() { "0", ); }); + + describe("Raydium source", function () { + it("relaunches through an AMM v4 pool with exact money math at each hop", async function () { + const { setup, pool, relaunch, newMint } = + await initializeLiveRaydiumRelaunch.call(this); + + let stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.live); + assert.isDefined(stored.sourceVenue.raydiumAmmV4); + assert.equal( + stored.oldSupplySnapshot.toString(), + DEFAULT_OLD_SUPPLY.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.newTokenVault)).toString(), + (TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY).toString(), + ); + + // Mixed entry: two direct depositors interleaved with a bought deposit. + const alice = Keypair.generate(); + const bob = Keypair.generate(); + await fundDepositor.call(this, setup, alice.publicKey, ALICE_DEPOSIT); + await fundDepositor.call(this, setup, bob.publicKey, BOB_DEPOSIT); + + await deposit.call(this, relaunch, setup, ALICE_DEPOSIT, alice); + + // Pre-fund the WSOL ATA past max_quote_in so no shortfall wrap runs + // and the buy's spend is a clean delta. + const wsolAta = await wrapSol(client.provider, this.payer, 10n ** 9n); + const wsolBefore = await tokenBalance(this.banksClient, wsolAta); + await client.depositViaBuy({ + relaunch, + baseOut: new BN(BUY_DEPOSIT.toString()), + maxQuoteIn: new BN((10n ** 9n).toString()), + }); + + // Buy identity: the CPI pulled exactly the exact-out input from the + // depositor, and the full input (fee included) landed in the pool. + // Default orientation puts the token on the pc side, WSOL on coin. + const buyIn = raydiumBuyIn( + BUY_DEPOSIT, + WSOL_POOL_QUOTE_RESERVE, + POOL_BASE_RESERVE, + ); + assert.equal( + ( + wsolBefore - (await tokenBalance(this.banksClient, wsolAta)) + ).toString(), + buyIn.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, pool.pcVault)).toString(), + (POOL_BASE_RESERVE - BUY_DEPOSIT).toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, pool.coinVault)).toString(), + (WSOL_POOL_QUOTE_RESERVE + buyIn).toString(), + ); + + await deposit.call(this, relaunch, setup, BOB_DEPOSIT, bob); + + stored = await client.fetchRelaunch(relaunch); + assert.equal( + stored.totalDeposited.toString(), + THRESHOLD_AMOUNT.toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, stored.oldTokenVault)).toString(), + THRESHOLD_AMOUNT.toString(), + ); + const expectedDeposits: [PublicKey, bigint][] = [ + [alice.publicKey, ALICE_DEPOSIT], + [bob.publicKey, BOB_DEPOSIT], + [this.payer.publicKey, BUY_DEPOSIT], + ]; + for (const [depositor, amount] of expectedDeposits) { + const record = await client.getDepositRecord({ relaunch, depositor }); + assert.equal(record.amountDeposited.toString(), amount.toString()); + } + + // The deposits land exactly on the threshold, so closing moves to + // SellPending. + await this.advanceBySeconds(ONE_WEEK); + await client.closeDepositsIx({ relaunch }).rpc(); + stored = await client.fetchRelaunch(relaunch); + assert.isDefined(stored.state.sellPending); + + // Raydium's flat 25 bps fee is modeled exactly, so the SDK's default + // slippage floor needs no fee headroom. + await client.executeSell({ relaunch }); + + // Sell identity: the whole vault sold off the buy-shifted reserves at + // the exact constant-product output, fee left in the pool. + const sellTokenReserve = POOL_BASE_RESERVE - BUY_DEPOSIT; + const sellQuoteReserve = WSOL_POOL_QUOTE_RESERVE + buyIn; + const predictedOut = raydiumSellOut( + THRESHOLD_AMOUNT, + sellTokenReserve, + sellQuoteReserve, + ); + stored = await client.fetchRelaunch(relaunch); + assert.equal(stored.quoteRecovered.toString(), predictedOut.toString()); + assert.equal( + (await tokenBalance(this.banksClient, stored.oldTokenVault)).toString(), + "0", + ); + assert.equal( + (await tokenBalance(this.banksClient, pool.pcVault)).toString(), + (sellTokenReserve + THRESHOLD_AMOUNT).toString(), + ); + assert.equal( + (await tokenBalance(this.banksClient, pool.coinVault)).toString(), + (sellQuoteReserve - predictedOut).toString(), + ); + + await runCompletionTail.call(this, { + relaunch, + newMint, + alice, + bob, + isWsol: true, + }); + }); + + it("misses the threshold and refunds direct- and buy-credited deposits exactly", async function () { + const { setup, relaunch } = + await initializeLiveRaydiumRelaunch.call(this); + await runThresholdMiss.call(this, { setup, relaunch }); + }); + }); } From 4e2090dcc67c7cc7b701e00b8d28f0f5d499e581 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 21:16:23 -0700 Subject: [PATCH 30/31] add raydium to alt --- scripts/relaunch/createAlt.ts | 27 +-- sdk/src/constants.ts | 7 +- sdk/src/relaunch/v0.1/types/relaunch.ts | 4 - tasks.md | 11 +- tests/fixtures/relaunch-global-alt | Bin 5880 -> 5944 bytes tests/main.test.ts | 2 +- tests/relaunch/unit/altTransactions.test.ts | 178 ++++++++++++++++++++ 7 files changed, 198 insertions(+), 31 deletions(-) diff --git a/scripts/relaunch/createAlt.ts b/scripts/relaunch/createAlt.ts index 0b7d12f73..0c06e2028 100644 --- a/scripts/relaunch/createAlt.ts +++ b/scripts/relaunch/createAlt.ts @@ -1,11 +1,5 @@ // Creates the relaunch SDK's global frozen address lookup table. // -// The address list mirrors vibes/relaunch-alt-contents.html: groups A-G are -// protocol statics, group H is the whirlpool tick-array band (32 arrays below -// the current price up through tick 0 = $1,000/SOL). Groups D/E/H are -// re-resolved from mainnet at run time, so run `plan` and eyeball the list -// right before creating. -// // Usage (env: ANCHOR_PROVIDER_URL, ANCHOR_WALLET, PRIORITY_FEE_MICRO_LAMPORTS): // yarn relaunch-create-alt # plan: print the list, no txs // yarn relaunch-create-alt create # create table + extend + verify @@ -43,6 +37,8 @@ import { MPL_TOKEN_METADATA_PROGRAM_ID, PUMP_AMM_PROGRAM_ID, PUMP_FEES_PROGRAM_ID, + RAYDIUM_AMM_AUTHORITY, + RAYDIUM_AMM_PROGRAM_ID, RELAUNCH_V0_1_PROGRAM_ID, SQUADS_PROGRAM_CONFIG, SQUADS_PROGRAM_CONFIG_TREASURY, @@ -95,7 +91,7 @@ async function buildAddressList(connection: Connection): Promise { const entries: Entry[] = []; const push = (label: string, key: PublicKey) => entries.push({ label, key }); - // A. Relaunch protocol + // Relaunch protocol push("relaunch program", RELAUNCH_V0_1_PROGRAM_ID); push( "relaunch event authority", @@ -105,7 +101,7 @@ async function buildAddressList(connection: Connection): Promise { )[0], ); - // B. Core programs, sysvars, mints + // Core programs, sysvars, mints push("system program", SystemProgram.programId); push("token program", TOKEN_PROGRAM_ID); push("token-2022 program", TOKEN_2022_PROGRAM_ID); @@ -116,7 +112,7 @@ async function buildAddressList(connection: Connection): Promise { push("WSOL mint", NATIVE_MINT); push("USDC mint", MAINNET_USDC); - // C. pump statics + // pump statics push("pump_amm program", PUMP_AMM_PROGRAM_ID); push("pump fees program", PUMP_FEES_PROGRAM_ID); push("pump global config", PUMP_AMM_GLOBAL_CONFIG); @@ -124,7 +120,7 @@ async function buildAddressList(connection: Connection): Promise { push("pump fee config", PUMP_AMM_FEE_CONFIG); push("pump global volume accumulator", PUMP_AMM_GLOBAL_VOLUME_ACCUMULATOR); - // D/E. fee recipients + their quote-mint ATAs (config-order snapshot) + // fee recipients + their quote-mint ATAs (config-order snapshot) const pushRecipients = (kind: string, recipients: PublicKey[]) => { recipients.forEach((recipient, i) => { push(`${kind} fee recipient #${i + 1}`, recipient); @@ -141,14 +137,14 @@ async function buildAddressList(connection: Connection): Promise { pushRecipients("protocol", protocolFeeRecipients); pushRecipients("buyback", buybackFeeRecipients); - // F. whirlpool statics + // whirlpool statics push("whirlpool program", WHIRLPOOL_PROGRAM_ID); push("USDC_SWAP_POOL", USDC_SWAP_POOL); push("USDC_SWAP_POOL WSOL vault", pool.tokenVaultA); push("USDC_SWAP_POOL USDC vault", pool.tokenVaultB); push("USDC_SWAP_POOL oracle", getWhirlpoolOracleAddr(USDC_SWAP_POOL)); - // G. futarchy + squads statics + // futarchy + squads statics push("futarchy program", FUTARCHY_V0_6_PROGRAM_ID); push( "futarchy event authority", @@ -161,7 +157,7 @@ async function buildAddressList(connection: Connection): Promise { push("squads program config", SQUADS_PROGRAM_CONFIG); push("squads program config treasury", SQUADS_PROGRAM_CONFIG_TREASURY); - // H. whirlpool tick-array band + // whirlpool tick-array band const span = 88 * pool.tickSpacing; const currentStart = Math.floor(pool.tickCurrentIndex / span) * span; if (currentStart > TICK_BAND_TOP_START) { @@ -180,6 +176,11 @@ async function buildAddressList(connection: Connection): Promise { ); } + // raydium statics — the AMM v4 venue's two global keys (every other + // Raydium-side account is per-pool) + push("raydium amm v4 program", RAYDIUM_AMM_PROGRAM_ID); + push("raydium amm authority", RAYDIUM_AMM_AUTHORITY); + const seen = new Set(); for (const { label, key } of entries) { const b58 = key.toBase58(); diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index 3dbe5ec3e..260fc8a15 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -73,12 +73,13 @@ export const RELAUNCH_V0_1_PROGRAM_ID = new PublicKey( ); // The relaunch global frozen address lookup table (contents documented in -// vibes/relaunch-alt-contents.html, created by `yarn relaunch-create-alt`). -// PLACEHOLDER: this is the table from the 2026-08-11 surfpool dry run, which +// vibes/relaunch-alt-contents.html plus the two Raydium AMM v4 statics, +// created by `yarn relaunch-create-alt`). +// PLACEHOLDER: this is the table from the 2026-08-12 surfpool dry run, which // tests/fixtures/relaunch-global-alt mirrors byte-for-byte. Replace with the // real table address once it is created and frozen on mainnet. export const RELAUNCH_V0_1_GLOBAL_ALT = new PublicKey( - "4Wer9Wo1me9kzQewyzc4bEKw3qgVjUFuagcs65mujdoS", + "HXH8J1qEhfXWoAgsFhstHgcLkRXqvheoiNuRbXM3VS13", ); export const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey( diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index 15d0b6bef..f2c92974f 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -300,7 +300,6 @@ export type Relaunch = { name: "sourcePool"; isMut: true; isSigner: false; - docs: ["rechecks its internal consistency."]; }, { name: "pumpGlobalConfig"; @@ -475,7 +474,6 @@ export type Relaunch = { name: "sourcePool"; isMut: true; isSigner: false; - docs: ["rechecks its internal consistency."]; }, { name: "ammAuthority"; @@ -2581,7 +2579,6 @@ export const IDL: Relaunch = { name: "sourcePool", isMut: true, isSigner: false, - docs: ["rechecks its internal consistency."], }, { name: "pumpGlobalConfig", @@ -2756,7 +2753,6 @@ export const IDL: Relaunch = { name: "sourcePool", isMut: true, isSigner: false, - docs: ["rechecks its internal consistency."], }, { name: "ammAuthority", diff --git a/tasks.md b/tasks.md index 4afc9de8c..775fa2b39 100644 --- a/tasks.md +++ b/tasks.md @@ -27,15 +27,6 @@ > Reference: `relaunch-raydium-implementation-plan.md` → "Stage 4 — lifecycle, ALT, budgets" -- [NEXT] 4.1 Full-lifecycle integration tests - - `tests/integration/relaunch.test.ts`: Raydium-source happy path with money-math identities at each hop; threshold-miss failure path with refunds - - Matrix note: WSOL × classic-SPL only — other combinations impossible for this venue - -- [ ] 4.2 ALT statics - - AMM v4 program + `5Q544…` authority into `scripts/relaunch/createAlt.ts` groups; re-dump the fork fixture - - `altTransactions.test.ts`: Raydium buy as a single legacy tx and as v0 against the extended table - - Must land before the mainnet ALT freeze (follow-ups item 3) - -- [ ] 4.3 Compute budgets for the new builders +- [NEXT] 4.3 Compute budgets for the new builders - Measure worst-case CU from bankrun logs (surfpool figures as cross-check); worst +20% or drop to the 200k default - Confirm pump-variant budgets untouched by the shared-tail refactor diff --git a/tests/fixtures/relaunch-global-alt b/tests/fixtures/relaunch-global-alt index 1bc656bd70604b9885b0c7609de1f048501e66a4..cb90e6386fe55ef8730e1f67c8540fc0b3292f31 100644 GIT binary patch delta 72 zcmV-O0Js16Ex0bQ_!dA**-6AU0>eKbcaZHCqf$(vxtCn=A)=`a|LlGEwu;R`SFl(Q eF~$7kN@7?oy!qFT=UGFbgrk$QP&SPsiIWI8_awXk delta 7 Ocmdm?_d|EX4>14_#RJL! diff --git a/tests/main.test.ts b/tests/main.test.ts index fe9fff32e..6031d4399 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -345,7 +345,7 @@ before(async function () { data: fs.readFileSync("./tests/fixtures/relaunch-global-alt"), executable: false, owner: AddressLookupTableProgram.programId, - lamports: 41_815_680, + lamports: 42_261_120, }, }, ], diff --git a/tests/relaunch/unit/altTransactions.test.ts b/tests/relaunch/unit/altTransactions.test.ts index dc35dc25b..757ae9aed 100644 --- a/tests/relaunch/unit/altTransactions.test.ts +++ b/tests/relaunch/unit/altTransactions.test.ts @@ -18,11 +18,14 @@ import { BN } from "bn.js"; import { BanksClient } from "solana-bankrun"; import { getPumpFeeRecipients, + RAYDIUM_AMM_AUTHORITY, + RAYDIUM_AMM_PROGRAM_ID, RELAUNCH_V0_1_GLOBAL_ALT, RelaunchClient, } from "@metadaoproject/programs"; import { buildV0Tx, setupRelaunch, DEFAULT_OLD_SUPPLY } from "../utils.js"; import { writePumpPool, PumpPool } from "../pumpAmm.js"; +import { writeRaydiumPool, RaydiumPool } from "../raydiumAmm.js"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL @@ -294,6 +297,181 @@ export default function suite() { ); }); + const setupLiveRaydiumRelaunch = async function ( + this: Mocha.Context, + ): Promise<{ + relaunch: PublicKey; + pool: RaydiumPool; + oldMint: PublicKey; + }> { + const setup = await setupRelaunch({ + banksClient: this.banksClient, + payer: this.payer, + }); + const pool = writeRaydiumPool({ + context: this.context, + oldMint: setup.oldMint, + tokenReserve: POOL_BASE_RESERVE, + quoteReserve: WSOL_POOL_QUOTE_RESERVE, + }); + + const { relaunch } = await client.initializeRelaunch({ + oldMint: setup.oldMint, + sourcePool: pool.pool, + sourceQuoteMint: token.NATIVE_MINT, + tokenName: "Relaunched", + tokenSymbol: "RLNCH", + tokenUri: "https://example.com/rlnch.json", + secondsForDeposits: ONE_WEEK, + gracePeriodSeconds: ONE_DAY, + thresholdBps: 1000, + teamAddress: this.payer.publicKey, + }); + await client.startDepositsIx({ relaunch }).rpc(); + + return { oldMint: setup.oldMint, pool, relaunch }; + }; + + // The Raydium counterpart of wrapBuyUnwrapIxs: same wrap choreography, no + // pump fee-recipient or volume-accumulator accounts. + const wrapBuyUnwrapRaydiumIxs = async ({ + relaunch, + pool, + oldMint, + depositor, + payer, + }: { + relaunch: PublicKey; + pool: RaydiumPool; + oldMint: PublicKey; + depositor: PublicKey; + payer: PublicKey; + }) => { + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + depositor, + ); + return [ + ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }), + token.createAssociatedTokenAccountIdempotentInstruction( + depositor, + wsolAta, + depositor, + token.NATIVE_MINT, + ), + SystemProgram.transfer({ + fromPubkey: depositor, + toPubkey: wsolAta, + lamports: Number(MAX_QUOTE_IN), + }), + token.createSyncNativeInstruction(wsolAta), + await client + .depositViaBuyRaydiumIx({ + relaunch, + oldMint, + sourceQuoteMint: token.NATIVE_MINT, + sourcePool: pool.pool, + ammCoinVault: pool.coinVault, + ammPcVault: pool.pcVault, + baseOut: new BN(BASE_OUT.toString()), + maxQuoteIn: new BN(MAX_QUOTE_IN.toString()), + depositor, + payer, + }) + .instruction(), + token.createCloseAccountInstruction(wsolAta, depositor, depositor), + ]; + }; + + it("deposit_via_buy_raydium: the wrap-buy-unwrap flow fits a single legacy transaction", async function () { + const { relaunch, pool, oldMint } = + await setupLiveRaydiumRelaunch.call(this); + const depositor = Keypair.generate(); + await fundSol.call(this, depositor.publicKey, 5n * 10n ** 9n); + + const ixs = await wrapBuyUnwrapRaydiumIxs({ + relaunch, + pool, + oldMint, + depositor: depositor.publicKey, + payer: depositor.publicKey, + }); + + // No fee-recipient tail and no volume accumulator: unlike the pump + // variant above, the Raydium buy fits the 1232-byte legacy limit without + // the lookup table. + const legacyTx = new Transaction().add(...ixs); + legacyTx.recentBlockhash = (await this.banksClient.getLatestBlockhash())[0]; + legacyTx.feePayer = depositor.publicKey; + legacyTx.sign(depositor); + assert.isAtMost(legacyTx.serialize().length, 1232); + await this.banksClient.processTransaction(legacyTx); + + const record = await client.getDepositRecord({ + relaunch, + depositor: depositor.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const { oldTokenVault } = await client.fetchRelaunch(relaunch); + const vaultBalance = await tokenBalance(this.banksClient, oldTokenVault); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + + // The refund unwrapped: the WSOL ATA closed at the end of the flow. + const wsolAta = token.getAssociatedTokenAddressSync( + token.NATIVE_MINT, + depositor.publicKey, + ); + assert.isNull(await this.banksClient.getAccount(wsolAta)); + }); + + it("deposit_via_buy_raydium: built as a v0 transaction against the extended table", async function () { + const { relaunch, pool, oldMint } = + await setupLiveRaydiumRelaunch.call(this); + const depositor = Keypair.generate(); + await fundSol.call(this, depositor.publicKey, 5n * 10n ** 9n); + + // The extension that landed with the Raydium venue: both AMM v4 statics + // are entries of the frozen table. + const tableKeys = globalAlt.state.addresses.map((k) => k.toBase58()); + assert.include(tableKeys, RAYDIUM_AMM_PROGRAM_ID.toBase58()); + assert.include(tableKeys, RAYDIUM_AMM_AUTHORITY.toBase58()); + + const ixs = await wrapBuyUnwrapRaydiumIxs({ + relaunch, + pool, + oldMint, + depositor: depositor.publicKey, + payer: depositor.publicKey, + }); + const tx = await buildV0Tx({ + banksClient: this.banksClient, + payerKey: depositor.publicKey, + instructions: ixs, + signers: [depositor], + tables: [globalAlt], + }); + assert.isAtMost(tx.serialize().length, 1232); + + // Both statics resolved through the table instead of riding as static + // message keys. + const staticKeys = tx.message.staticAccountKeys.map((k) => k.toBase58()); + assert.notInclude(staticKeys, RAYDIUM_AMM_PROGRAM_ID.toBase58()); + assert.notInclude(staticKeys, RAYDIUM_AMM_AUTHORITY.toBase58()); + + await this.banksClient.processTransaction(tx); + + const record = await client.getDepositRecord({ + relaunch, + depositor: depositor.publicKey, + }); + assert.equal(record.amountDeposited.toString(), BASE_OUT.toString()); + + const { oldTokenVault } = await client.fetchRelaunch(relaunch); + const vaultBalance = await tokenBalance(this.banksClient, oldTokenVault); + assert.equal(vaultBalance.toString(), BASE_OUT.toString()); + }); + it("execute_sell: built as a v0 transaction with the ALT", async function () { const { relaunch, pool, oldMint } = await setupLiveRelaunch.call(this); // 100M tokens meets the 10% threshold, so closing lands in SellPending. From feeaf79d9757c921b656ce135353d4b9e8d683e8 Mon Sep 17 00:00:00 2001 From: Pileks Date: Wed, 12 Aug 2026 21:55:07 -0700 Subject: [PATCH 31/31] 12.5/12.5 depositors/LP ratio --- programs/relaunch/src/constants.rs | 9 ++-- .../src/instructions/complete_relaunch.rs | 48 ++++--------------- sdk/src/relaunch/v0.1/RelaunchClient.ts | 5 -- sdk/src/relaunch/v0.1/types/relaunch.ts | 10 ---- tasks.md | 32 ------------- tests/integration/relaunch.test.ts | 34 ++++++------- tests/relaunch/unit/claim.test.ts | 14 +++--- tests/relaunch/unit/completeRelaunch.test.ts | 34 ++++++------- .../relaunch/unit/initializeRelaunch.test.ts | 2 +- 9 files changed, 51 insertions(+), 137 deletions(-) delete mode 100644 tasks.md diff --git a/programs/relaunch/src/constants.rs b/programs/relaunch/src/constants.rs index 71abaf0b9..352cb8959 100644 --- a/programs/relaunch/src/constants.rs +++ b/programs/relaunch/src/constants.rs @@ -2,10 +2,11 @@ pub const TOKEN_SCALE: u64 = 1_000_000; pub const PRICE_SCALE: u128 = 1_000_000_000_000; -/// 10M tokens with 6 decimals, distributed pro-rata to depositors. -pub const TOKENS_TO_DEPOSITORS: u64 = 10_000_000 * TOKEN_SCALE; -/// 2M tokens with 6 decimals, paired with recovered USDC in the futarchy AMM. -pub const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 2_000_000 * TOKEN_SCALE; +/// 12.5M tokens with 6 decimals, distributed pro-rata to depositors. +pub const TOKENS_TO_DEPOSITORS: u64 = 12_500_000 * TOKEN_SCALE; +/// 12.5M tokens with 6 decimals, paired with all recovered USDC in the +/// futarchy AMM. +pub const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 12_500_000 * TOKEN_SCALE; /// 1.5M tokens with 6 decimals, staked to create a proposal (launchpad's value). pub const PROPOSAL_MIN_STAKE_TOKENS: u64 = 1_500_000 * TOKEN_SCALE; diff --git a/programs/relaunch/src/instructions/complete_relaunch.rs b/programs/relaunch/src/instructions/complete_relaunch.rs index a7a9ae0f6..1c2e1c1f2 100644 --- a/programs/relaunch/src/instructions/complete_relaunch.rs +++ b/programs/relaunch/src/instructions/complete_relaunch.rs @@ -5,7 +5,7 @@ use anchor_spl::metadata::{ UpdateMetadataAccountsV2, }; use anchor_spl::token::{ - self, spl_token::instruction::AuthorityType, Mint, SetAuthority, Token, TokenAccount, Transfer, + self, spl_token::instruction::AuthorityType, Mint, SetAuthority, Token, TokenAccount, }; use futarchy::program::Futarchy; @@ -17,8 +17,7 @@ use crate::error::RelaunchError; use crate::events::{CommonFields, RelaunchCompletedEvent}; use crate::state::{Relaunch, RelaunchState}; use crate::{ - usdc_mint, PRICE_SCALE, PROPOSAL_MIN_STAKE_TOKENS, TOKENS_TO_DEPOSITORS, - TOKENS_TO_FUTARCHY_LIQUIDITY, + usdc_mint, PRICE_SCALE, PROPOSAL_MIN_STAKE_TOKENS, TOKENS_TO_FUTARCHY_LIQUIDITY, }; #[event_cpi] @@ -101,7 +100,7 @@ pub struct CompleteRelaunch<'info> { )] pub squads_multisig: UncheckedAccount<'info>, - /// CHECK: the DAO treasury that receives the remaining USDC and both + /// CHECK: the DAO treasury that receives the mint and metadata /// authorities. #[account( seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig.key().as_ref(), squads_multisig_program::SEED_VAULT, 0_u8.to_le_bytes().as_ref()], @@ -131,14 +130,6 @@ pub struct CompleteRelaunch<'info> { #[account(mut)] pub squads_program_config_treasury: UncheckedAccount<'info>, - #[account( - init_if_needed, - payer = payer, - associated_token::mint = usdc_mint, - associated_token::authority = squads_multisig_vault, - )] - pub treasury_usdc_account: Box>, - pub futarchy_program: Program<'info, Futarchy>, /// CHECK: the futarchy program's event-CPI authority PDA. #[account(seeds = [b"__event_authority"], bump, seeds::program = futarchy_program)] @@ -171,13 +162,15 @@ impl CompleteRelaunch<'_> { let signer = &[&seeds[..]]; let usdc_recovered = ctx.accounts.relaunch.usdc_recovered; - let price_1e12 = (usdc_recovered as u128 * PRICE_SCALE) / (TOKENS_TO_DEPOSITORS as u128); - let usdc_to_lp = usdc_recovered / 5; + // LP the vault's live balance rather than the recorded proceeds so + // stray donations end up in the pool instead of stranding. + let usdc_to_lp = ctx.accounts.usdc_vault.amount; + let price_1e12 = + (usdc_to_lp as u128 * PRICE_SCALE) / (TOKENS_TO_FUTARCHY_LIQUIDITY as u128); ctx.accounts.initialize_dao(price_1e12, signer)?; ctx.accounts .provide_futarchy_amm_liquidity(usdc_to_lp, signer)?; - let usdc_to_treasury = ctx.accounts.send_remaining_usdc_to_dao(signer)?; ctx.accounts.transfer_mint_authority_to_dao(signer)?; ctx.accounts.transfer_metadata_authority_to_dao(signer)?; @@ -197,7 +190,7 @@ impl CompleteRelaunch<'_> { usdc_recovered, twap_initial_observation: price_1e12, usdc_to_lp, - usdc_to_treasury, + usdc_to_treasury: 0, }); Ok(()) @@ -290,29 +283,6 @@ impl CompleteRelaunch<'_> { ) } - /// Sweeps everything left in the USDC vault to the DAO treasury. - #[inline(never)] - fn send_remaining_usdc_to_dao(&mut self, signer: &[&[&[u8]]]) -> Result { - // Ensure the vault is updated before reading its amount. - self.usdc_vault.reload()?; - let usdc_to_treasury = self.usdc_vault.amount; - - token::transfer( - CpiContext::new_with_signer( - self.token_program.to_account_info(), - Transfer { - from: self.usdc_vault.to_account_info(), - to: self.treasury_usdc_account.to_account_info(), - authority: self.relaunch_signer.to_account_info(), - }, - signer, - ), - usdc_to_treasury, - )?; - - Ok(usdc_to_treasury) - } - #[inline(never)] fn transfer_mint_authority_to_dao(&self, signer: &[&[&[u8]]]) -> Result<()> { token::set_authority( diff --git a/sdk/src/relaunch/v0.1/RelaunchClient.ts b/sdk/src/relaunch/v0.1/RelaunchClient.ts index 414278de5..4d82a4265 100644 --- a/sdk/src/relaunch/v0.1/RelaunchClient.ts +++ b/sdk/src/relaunch/v0.1/RelaunchClient.ts @@ -752,11 +752,6 @@ export class RelaunchClient { spendingLimit, squadsProgramConfig: SQUADS_PROGRAM_CONFIG, squadsProgramConfigTreasury: SQUADS_PROGRAM_CONFIG_TREASURY, - treasuryUsdcAccount: getAssociatedTokenAddressSync( - MAINNET_USDC, - multisigVault, - true, - ), futarchyProgram: FUTARCHY_V0_6_PROGRAM_ID, futarchyEventAuthority, squadsProgram: SQUADS_PROGRAM_ID, diff --git a/sdk/src/relaunch/v0.1/types/relaunch.ts b/sdk/src/relaunch/v0.1/types/relaunch.ts index f2c92974f..2489c53a3 100644 --- a/sdk/src/relaunch/v0.1/types/relaunch.ts +++ b/sdk/src/relaunch/v0.1/types/relaunch.ts @@ -990,11 +990,6 @@ export type Relaunch = { isMut: true; isSigner: false; }, - { - name: "treasuryUsdcAccount"; - isMut: true; - isSigner: false; - }, { name: "futarchyProgram"; isMut: false; @@ -3269,11 +3264,6 @@ export const IDL: Relaunch = { isMut: true, isSigner: false, }, - { - name: "treasuryUsdcAccount", - isMut: true, - isSigner: false, - }, { name: "futarchyProgram", isMut: false, diff --git a/tasks.md b/tasks.md deleted file mode 100644 index 775fa2b39..000000000 --- a/tasks.md +++ /dev/null @@ -1,32 +0,0 @@ -# Relaunch Raydium Venue Implementation Tasks - -## Instructions for Claude - -**READ THIS FIRST:** - -1. Look at this file and find the task marked with `[NEXT]` -2. Read the referenced section in `vibes/relaunch-raydium-implementation-plan.md` for full context (design rationale lives in `vibes/relaunch-raydium-venue.html`) -3. Do ONLY that task - nothing else -4. After completing the task, verify: `./rebuild.sh` after any program/SDK change, then `anchor test --skip-build` (use `.only` on the new test file while iterating, remove it and run the full suite before finishing) -5. If successful, remove the completed task from this file -6. Mark the next task with `[NEXT]` -7. Stop and wait for the user - -**DO NOT:** -- Do multiple tasks at once -- Skip ahead -- Forget to verify - -**Reference:** Full implementation plan is in `vibes/relaunch-raydium-implementation-plan.md` - ---- - -## Tasks - -### Phase 4: Lifecycle, ALT, budgets - -> Reference: `relaunch-raydium-implementation-plan.md` → "Stage 4 — lifecycle, ALT, budgets" - -- [NEXT] 4.3 Compute budgets for the new builders - - Measure worst-case CU from bankrun logs (surfpool figures as cross-check); worst +20% or drop to the 200k default - - Confirm pump-variant budgets untouched by the shared-tail refactor diff --git a/tests/integration/relaunch.test.ts b/tests/integration/relaunch.test.ts index f5f42d2aa..a0485a758 100644 --- a/tests/integration/relaunch.test.ts +++ b/tests/integration/relaunch.test.ts @@ -31,8 +31,8 @@ const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 5n * 10n ** 9n; // 5 SOL const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC -const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; -const TOKENS_TO_FUTARCHY_LIQUIDITY = 2_000_000n * 10n ** 6n; +const TOKENS_TO_DEPOSITORS = 12_500_000n * 10n ** 6n; +const TOKENS_TO_FUTARCHY_LIQUIDITY = 12_500_000n * 10n ** 6n; const PRICE_SCALE = 10n ** 12n; const ONE_WEEK = 60 * 60 * 24 * 7; @@ -351,10 +351,11 @@ export default function suite() { assert.ok(storedDao.baseMint.equals(newMint)); assert.ok(storedDao.quoteMint.equals(MAINNET_USDC)); - // Price identity: the TWAP opens at the raise valued over the depositor - // bucket, and the AMM opens at exactly that ratio — the full 2M - // liquidity bucket against raise/5. - const expectedTwap = (usdcRecovered * PRICE_SCALE) / TOKENS_TO_DEPOSITORS; + // Price identity: the TWAP opens at the raise valued over the liquidity + // bucket, and the AMM opens at exactly that ratio — the full 12.5M + // liquidity bucket against the whole raise. + const expectedTwap = + (usdcRecovered * PRICE_SCALE) / TOKENS_TO_FUTARCHY_LIQUIDITY; assert.equal( storedDao.twapInitialObservation.toString(), expectedTwap.toString(), @@ -364,7 +365,6 @@ export default function suite() { (expectedTwap / 20n).toString(), ); - const usdcToLp = usdcRecovered / 5n; const spot = storedDao.amm.state.spot.spot; const baseReserves = BigInt(spot.baseReserves.toString()); const quoteReserves = BigInt(spot.quoteReserves.toString()); @@ -372,22 +372,18 @@ export default function suite() { baseReserves.toString(), TOKENS_TO_FUTARCHY_LIQUIDITY.toString(), ); - assert.equal(quoteReserves.toString(), usdcToLp.toString()); + assert.equal(quoteReserves.toString(), usdcRecovered.toString()); assert.equal( ((quoteReserves * PRICE_SCALE) / baseReserves).toString(), expectedTwap.toString(), ); - // Treasury remainder: whatever the LP seed left goes to the Squads - // vault, conserving every raw unit of the raise. + // The whole raise seeds the AMM; the Squads vault gets no USDC. const treasuryBalance = await tokenBalance( this.banksClient, token.getAssociatedTokenAddressSync(MAINNET_USDC, stored.daoVault, true), ); - assert.equal( - treasuryBalance.toString(), - (usdcRecovered - usdcToLp).toString(), - ); + assert.equal(treasuryBalance.toString(), "0"); assert.equal( (await tokenBalance(this.banksClient, stored.usdcVault)).toString(), "0", @@ -400,8 +396,8 @@ export default function suite() { const mint = await this.getMint(newMint); assert.ok(mint.mintAuthority.equals(stored.daoVault)); - // Claims: 10M × 60M/100M, 10M × 39.99M/100M, 10M × 10k/100M — every - // share divides exactly, so the vault empties with zero dust. + // Claims: 12.5M × 60M/100M, 12.5M × 39.99M/100M, 12.5M × 10k/100M — + // every share divides exactly, so the vault empties with zero dust. await client .claimIx({ relaunch, newMint, depositor: alice.publicKey }) .rpc(); @@ -409,9 +405,9 @@ export default function suite() { await client.claimIx({ relaunch, newMint }).rpc(); const expectedClaims: [PublicKey, bigint][] = [ - [alice.publicKey, 6_000_000n * 10n ** 6n], - [bob.publicKey, 3_999_000n * 10n ** 6n], - [this.payer.publicKey, 1_000n * 10n ** 6n], + [alice.publicKey, 7_500_000n * 10n ** 6n], + [bob.publicKey, 4_998_750n * 10n ** 6n], + [this.payer.publicKey, 1_250n * 10n ** 6n], ]; for (const [owner, amount] of expectedClaims) { assert.equal( diff --git a/tests/relaunch/unit/claim.test.ts b/tests/relaunch/unit/claim.test.ts index 46d39cadb..9aa33d478 100644 --- a/tests/relaunch/unit/claim.test.ts +++ b/tests/relaunch/unit/claim.test.ts @@ -15,7 +15,7 @@ import { writePumpPool } from "../pumpAmm.js"; const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC -const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; +const TOKENS_TO_DEPOSITORS = 12_500_000n * 10n ** 6n; const ONE_WEEK = 60 * 60 * 24 * 7; const ONE_DAY = 60 * 60 * 24; @@ -170,18 +170,18 @@ export default function suite() { await client.claimIx({ relaunch, newMint, depositor: bob.publicKey }).rpc(); await client.claimIx({ relaunch, newMint }).rpc(); - // 10M × 25/200, 10M × 75/200, 10M × 100/200. + // 12.5M × 25/200, 12.5M × 75/200, 12.5M × 100/200. assert.equal( (await newTokenBalance.call(this, alice.publicKey)).toString(), - "1250000000000", + "1562500000000", ); assert.equal( (await newTokenBalance.call(this, bob.publicKey)).toString(), - "3750000000000", + "4687500000000", ); assert.equal( (await newTokenBalance.call(this, this.payer.publicKey)).toString(), - "5000000000000", + "6250000000000", ); const record = await client.getDepositRecord({ @@ -263,10 +263,10 @@ export default function suite() { .rpc(); await client.claimIx({ relaunch, newMint }).rpc(); - // Equal deposits, equal shares: 10M × 50k/100k each. + // Equal deposits, equal shares: 12.5M × 50k/100k each. const aliceClaimed = await newTokenBalance.call(this, alice.publicKey); const payerClaimed = await newTokenBalance.call(this, this.payer.publicKey); - assert.equal(aliceClaimed.toString(), "5000000000000"); + assert.equal(aliceClaimed.toString(), "6250000000000"); assert.equal(payerClaimed.toString(), aliceClaimed.toString()); }); diff --git a/tests/relaunch/unit/completeRelaunch.test.ts b/tests/relaunch/unit/completeRelaunch.test.ts index 719bbf633..2fee5a077 100644 --- a/tests/relaunch/unit/completeRelaunch.test.ts +++ b/tests/relaunch/unit/completeRelaunch.test.ts @@ -30,8 +30,8 @@ const POOL_BASE_RESERVE = 1_000_000n * 10n ** 6n; // 1M old tokens const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC -const TOKENS_TO_DEPOSITORS = 10_000_000n * 10n ** 6n; -const TOKENS_TO_FUTARCHY_LIQUIDITY = 2_000_000n * 10n ** 6n; +const TOKENS_TO_DEPOSITORS = 12_500_000n * 10n ** 6n; +const TOKENS_TO_FUTARCHY_LIQUIDITY = 12_500_000n * 10n ** 6n; const PROPOSAL_MIN_STAKE_TOKENS = 1_500_000n * 10n ** 6n; const PRICE_SCALE = 10n ** 12n; @@ -160,7 +160,6 @@ export default function suite() { let storedRelaunch = await client.fetchRelaunch(relaunch); const usdcRecovered = BigInt(storedRelaunch.usdcRecovered.toString()); - const usdcToLp = usdcRecovered / 5n; await client.completeRelaunch({ relaunch }); @@ -184,7 +183,8 @@ export default function suite() { assert.ok(storedDao.squadsMultisig.equals(multisigPda)); assert.ok(storedDao.squadsMultisigVault.equals(multisigVault)); - const expectedTwap = (usdcRecovered * PRICE_SCALE) / TOKENS_TO_DEPOSITORS; + const expectedTwap = + (usdcRecovered * PRICE_SCALE) / TOKENS_TO_FUTARCHY_LIQUIDITY; assert.equal( storedDao.twapInitialObservation.toString(), expectedTwap.toString(), @@ -219,8 +219,8 @@ export default function suite() { }); assert.isNotNull(await this.banksClient.getAccount(spendingLimit)); - // The AMM holds the full 2M bucket against raise/5, so its open ratio is - // exactly the TWAP's initial observation. + // The AMM holds the full 12.5M bucket against the whole raise, so its + // open ratio is exactly the TWAP's initial observation. const spot = storedDao.amm.state.spot.spot; const baseReserves = BigInt(spot.baseReserves.toString()); const quoteReserves = BigInt(spot.quoteReserves.toString()); @@ -228,7 +228,7 @@ export default function suite() { baseReserves.toString(), TOKENS_TO_FUTARCHY_LIQUIDITY.toString(), ); - assert.equal(quoteReserves.toString(), usdcToLp.toString()); + assert.equal(quoteReserves.toString(), usdcRecovered.toString()); assert.equal( ((quoteReserves * PRICE_SCALE) / baseReserves).toString(), expectedTwap.toString(), @@ -246,7 +246,7 @@ export default function suite() { this.banksClient, token.getAssociatedTokenAddressSync(MAINNET_USDC, dao, true), ); - assert.equal(ammQuoteVaultBalance.toString(), usdcToLp.toString()); + assert.equal(ammQuoteVaultBalance.toString(), usdcRecovered.toString()); const [ammPosition] = PublicKey.findProgramAddressSync( [Buffer.from("amm_position"), dao.toBuffer(), multisigVault.toBuffer()], @@ -261,10 +261,7 @@ export default function suite() { this.banksClient, token.getAssociatedTokenAddressSync(MAINNET_USDC, multisigVault, true), ); - assert.equal( - treasuryBalance.toString(), - (usdcRecovered - usdcToLp).toString(), - ); + assert.equal(treasuryBalance.toString(), "0"); const usdcVaultBalance = await tokenBalance( this.banksClient, @@ -343,9 +340,9 @@ export default function suite() { assert.isDefined(storedRelaunch.state.complete); }); - it("conserves every USDC raw unit across the LP/treasury split", async function () { - // 7% of the supply at a 5% threshold, so the recovered amount lands on a - // different residue than the happy path's. + it("sends every recovered USDC raw unit to the AMM", async function () { + // 7% of the supply at a 5% threshold, so a different recovered amount + // than the happy path's. const { relaunch } = await setupSwappedRelaunch.call(this, { depositAmount: (DEFAULT_OLD_SUPPLY * 7n) / 100n, thresholdBps: 500, @@ -361,7 +358,7 @@ export default function suite() { const quoteReserves = BigInt( storedDao.amm.state.spot.spot.quoteReserves.toString(), ); - assert.equal(quoteReserves.toString(), (usdcRecovered / 5n).toString()); + assert.equal(quoteReserves.toString(), usdcRecovered.toString()); const treasuryBalance = await tokenBalance( this.banksClient, @@ -371,10 +368,7 @@ export default function suite() { true, ), ); - assert.equal( - (quoteReserves + treasuryBalance).toString(), - usdcRecovered.toString(), - ); + assert.equal(treasuryBalance.toString(), "0"); const usdcVaultBalance = await tokenBalance( this.banksClient, diff --git a/tests/relaunch/unit/initializeRelaunch.test.ts b/tests/relaunch/unit/initializeRelaunch.test.ts index 9f032a6f9..7cab2339c 100644 --- a/tests/relaunch/unit/initializeRelaunch.test.ts +++ b/tests/relaunch/unit/initializeRelaunch.test.ts @@ -33,7 +33,7 @@ const WSOL_POOL_QUOTE_RESERVE = 100n * 10n ** 9n; // 100 SOL const USDC_POOL_QUOTE_RESERVE = 100_000n * 10n ** 6n; // 100k USDC // TOKENS_TO_DEPOSITORS + TOKENS_TO_FUTARCHY_LIQUIDITY -const TOTAL_MINTED = 12_000_000n * 10n ** 6n; +const TOTAL_MINTED = 25_000_000n * 10n ** 6n; const ONE_WEEK = 60 * 60 * 24 * 7; const ONE_DAY = 60 * 60 * 24;