diff --git a/crates/cli/src/cli/simnet/mod.rs b/crates/cli/src/cli/simnet/mod.rs index 3fc0d024f..397e9ebcf 100644 --- a/crates/cli/src/cli/simnet/mod.rs +++ b/crates/cli/src/cli/simnet/mod.rs @@ -188,6 +188,7 @@ pub async fn handle_start_local_surfnet_command( let explorer_handle = match start_studio_and_scenario_server( studio_binding_address, sanitized_config.clone(), + config.simnets[0].remote_rpc_url.clone(), subgraph_events_tx.clone(), ctx, !cmd.runtime.no_studio, diff --git a/crates/cli/src/http/mod.rs b/crates/cli/src/http/mod.rs index 40dc4d9fe..ff215cadd 100644 --- a/crates/cli/src/http/mod.rs +++ b/crates/cli/src/http/mod.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, error::Error as StdError, + str::FromStr, sync::{Arc, RwLock}, thread::JoinHandle, time::Duration, @@ -25,7 +26,20 @@ use rmcp_actix_web::transport::StreamableHttpService; #[cfg(feature = "explorer")] use rust_embed::RustEmbed; use serde::{Deserialize, Serialize}; -use surfpool_core::scenarios::TemplateRegistry; +use solana_commitment_config::CommitmentConfig; +use solana_pubkey::Pubkey; +use surfpool_core::{ + scenarios::{ + TemplateRegistry, + pump_graduation::{ + PumpGraduationPreparation, build_pump_graduation_scenario, pump_graduation_addresses, + }, + pump_swap_price_shock::{ + PumpSwapPriceShockPreparation, build_pump_swap_price_shock_scenario, + }, + }, + surfnet::remote::SurfnetRemoteClient, +}; use surfpool_mcp::Surfpool; use surfpool_studio_ui::serve_studio_static_files; use surfpool_types::{ @@ -45,6 +59,8 @@ pub struct Asset; fn configure_api(cfg: &mut web::ServiceConfig) { cfg.service(get_config) .service(get_scenario_templates) + .service(post_pump_graduation_scenario) + .service(post_pump_swap_price_shock_scenario) .service(post_scenarios) .service(get_scenarios) .service(delete_scenario) @@ -54,14 +70,163 @@ fn configure_api(cfg: &mut web::ServiceConfig) { .service(web::scope("/v1").default_service(web::route().to(api_not_found))); } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PumpGraduationScenarioRequest { + token_mint: String, +} + +#[derive(Clone)] +struct PumpScenarioDataSource(Option); + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PumpSwapPriceShockScenarioRequest { + token_mint: String, + virtual_quote_reserves: String, +} + +async fn prepare_pump_graduation_mint( + remote: &SurfnetRemoteClient, + token_mint: Pubkey, +) -> Result { + let addresses = pump_graduation_addresses(&token_mint); + let accounts = remote + .get_multiple_accounts( + &[ + token_mint, + addresses.bonding_curve, + addresses.curve_vault, + addresses.canonical_pool, + addresses.global, + ], + CommitmentConfig::confirmed(), + ) + .await + .map_err(actix_web::error::ErrorBadGateway)?; + let mint_account = accounts[0] + .account() + .ok_or_else(|| actix_web::error::ErrorBadRequest("Token mint not found"))?; + let curve_account = accounts[1] + .account() + .ok_or_else(|| actix_web::error::ErrorBadRequest("Pump bonding curve not found"))?; + let curve_vault_account = accounts[2] + .account() + .ok_or_else(|| actix_web::error::ErrorBadRequest("Token-2022 curve vault not found"))?; + let global_account = accounts[4] + .account() + .ok_or_else(|| actix_web::error::ErrorBadGateway("Pump Global account not found"))?; + + build_pump_graduation_scenario( + token_mint, + mint_account, + curve_account, + curve_vault_account, + accounts[3].account(), + global_account, + ) + .map_err(|error| actix_web::error::ErrorBadRequest(error.to_string())) +} + +#[post("/v1/scenarios/pump-graduation")] +async fn post_pump_graduation_scenario( + request: web::Json, + source: Data, + data: Data>, +) -> Result { + let token_mint = Pubkey::from_str(request.token_mint.trim()) + .map_err(|_| actix_web::error::ErrorBadRequest("Invalid token mint"))?; + let remote = source.0.as_ref().ok_or_else(|| { + actix_web::error::ErrorServiceUnavailable( + "Pump graduation requires an online datasource connection", + ) + })?; + let preparation = prepare_pump_graduation_mint(remote, token_mint).await?; + let scenario_id = preparation.scenario.id.clone(); + + data.write() + .map_err(|_| actix_web::error::ErrorInternalServerError("Failed to acquire write lock"))? + .scenarios + .push(preparation.scenario); + + Ok(HttpResponse::Ok().json(serde_json::json!({ + "id": scenario_id, + "tokenMint": preparation.token_mint.to_string(), + "completingBuyAmount": preparation.completing_buy_amount, + "migrationReserve": preparation.migration_reserve, + "addresses": { + "bondingCurve": preparation.addresses.bonding_curve.to_string(), + "curveVault": preparation.addresses.curve_vault.to_string(), + "canonicalPool": preparation.addresses.canonical_pool.to_string(), + }, + }))) +} + +async fn prepare_pump_swap_price_shock( + remote: &SurfnetRemoteClient, + token_mint: Pubkey, + virtual_quote_reserves: u64, +) -> Result { + let canonical_pool = pump_graduation_addresses(&token_mint).canonical_pool; + let accounts = remote + .get_multiple_accounts(&[canonical_pool], CommitmentConfig::confirmed()) + .await + .map_err(actix_web::error::ErrorBadGateway)?; + let canonical_pool_account = accounts[0] + .account() + .ok_or_else(|| actix_web::error::ErrorBadRequest("Canonical PumpSwap pool not found"))?; + + build_pump_swap_price_shock_scenario(token_mint, canonical_pool_account, virtual_quote_reserves) + .map_err(|error| actix_web::error::ErrorBadRequest(error.to_string())) +} + +#[post("/v1/scenarios/pump-swap-price-shock")] +async fn post_pump_swap_price_shock_scenario( + request: web::Json, + source: Data, + data: Data>, +) -> Result { + let token_mint = Pubkey::from_str(request.token_mint.trim()) + .map_err(|_| actix_web::error::ErrorBadRequest("Invalid token mint"))?; + let virtual_quote_reserves = request + .virtual_quote_reserves + .trim() + .parse::() + .map_err(|_| actix_web::error::ErrorBadRequest("Invalid virtual quote reserves"))?; + let remote = source.0.as_ref().ok_or_else(|| { + actix_web::error::ErrorServiceUnavailable( + "PumpSwap price shock requires an online datasource connection", + ) + })?; + let preparation = + prepare_pump_swap_price_shock(remote, token_mint, virtual_quote_reserves).await?; + let scenario_id = preparation.scenario.id.clone(); + + data.write() + .map_err(|_| actix_web::error::ErrorInternalServerError("Failed to acquire write lock"))? + .scenarios + .push(preparation.scenario); + + Ok(HttpResponse::Ok().json(serde_json::json!({ + "id": scenario_id, + "tokenMint": preparation.token_mint.to_string(), + "canonicalPool": preparation.canonical_pool.to_string(), + "virtualQuoteReserves": preparation.virtual_quote_reserves.to_string(), + }))) +} + pub async fn start_studio_and_scenario_server( network_binding: String, config: SanitizedConfig, + remote_rpc_url: Option, subgraph_events_tx: Sender, ctx: &Context, enable_studio: bool, ) -> Result> { let config_wrapped = Data::new(RwLock::new(config.clone())); + let pump_scenario_data_source = Data::new(PumpScenarioDataSource( + remote_rpc_url.and_then(SurfnetRemoteClient::new_unsafe), + )); // Initialize template registry and load templates let template_registry_wrapped = Data::new(RwLock::new(TemplateRegistry::new())); @@ -78,6 +243,7 @@ pub async fn start_studio_and_scenario_server( let server = HttpServer::new(move || { let mut app = App::new() .app_data(config_wrapped.clone()) + .app_data(pump_scenario_data_source.clone()) .app_data(template_registry_wrapped.clone()) .app_data(loaded_scenarios.clone()) .wrap( @@ -388,6 +554,66 @@ mod tests { assert_eq!(stored[0].name, "first", "the stored scenario is untouched"); } + #[actix_web::test] + async fn pump_graduation_rejects_an_invalid_mint_before_fetching_accounts() { + let app = test::init_service( + App::new() + .app_data(Data::new(RwLock::new(SanitizedConfig::default()))) + .app_data(Data::new(RwLock::new(LoadedScenarios::new()))) + .app_data(Data::new(PumpScenarioDataSource(None))) + .configure(configure_api), + ) + .await; + let request = test::TestRequest::post() + .uri("/v1/scenarios/pump-graduation") + .set_json(serde_json::json!({ "tokenMint": "not-a-mint" })) + .to_request(); + + let response = test::call_service(&app, request).await; + + assert_eq!(response.status(), 400); + + let missing_mint_request = test::TestRequest::post() + .uri("/v1/scenarios/pump-graduation") + .set_json(serde_json::json!({})) + .to_request(); + let missing_mint_response = test::call_service(&app, missing_mint_request).await; + + assert_eq!(missing_mint_response.status(), 400); + } + + #[actix_web::test] + async fn pump_swap_price_shock_rejects_invalid_inputs_before_fetching_accounts() { + let app = test::init_service( + App::new() + .app_data(Data::new(RwLock::new(SanitizedConfig::default()))) + .app_data(Data::new(RwLock::new(LoadedScenarios::new()))) + .app_data(Data::new(PumpScenarioDataSource(None))) + .configure(configure_api), + ) + .await; + let invalid_mint = test::TestRequest::post() + .uri("/v1/scenarios/pump-swap-price-shock") + .set_json(serde_json::json!({ + "tokenMint": "not-a-mint", + "virtualQuoteReserves": "15000000000000", + })) + .to_request(); + let invalid_reserves = test::TestRequest::post() + .uri("/v1/scenarios/pump-swap-price-shock") + .set_json(serde_json::json!({ + "tokenMint": "7LSsEoJGhLeZzGvDofTdNg7M3JttxQqGWNLo6vWMpump", + "virtualQuoteReserves": "not-a-number", + })) + .to_request(); + + assert_eq!(test::call_service(&app, invalid_mint).await.status(), 400); + assert_eq!( + test::call_service(&app, invalid_reserves).await.status(), + 400 + ); + } + #[actix_web::test] async fn unknown_v1_paths_return_json_404_instead_of_spa_fallback() { let loaded_scenarios = Data::new(RwLock::new(LoadedScenarios::new())); diff --git a/crates/core/src/rpc/surfnet_cheatcodes.rs b/crates/core/src/rpc/surfnet_cheatcodes.rs index 154057c51..679451c6f 100644 --- a/crates/core/src/rpc/surfnet_cheatcodes.rs +++ b/crates/core/src/rpc/surfnet_cheatcodes.rs @@ -1625,6 +1625,12 @@ impl SurfnetCheatcodes for SurfnetCheatcodesRpc { Box::pin(async move { let confidential = update.confidential.clone(); + let amount_only = update.amount.is_some() + && update.delegate.is_none() + && update.state.is_none() + && update.delegated_amount.is_none() + && update.close_authority.is_none() + && confidential.is_none(); if confidential.is_some() && token_program_id != spl_token_2022_interface::id() { return Err(Error::invalid_params( @@ -1684,10 +1690,11 @@ impl SurfnetCheatcodes for SurfnetCheatcodesRpc { ) .await?; - let mut token_account_data = TokenAccount::unpack(token_account.expected_data()) - .map_err(|e| { - Error::invalid_params(format!("Failed to unpack token account data: {}", e)) - })?; + let mut token_account_data = + TokenAccount::unpack_for_program(token_account.expected_data(), &token_program_id) + .map_err(|e| { + Error::invalid_params(format!("Failed to unpack token account data: {}", e)) + })?; update.apply(&mut token_account_data)?; @@ -1712,8 +1719,30 @@ impl SurfnetCheatcodes for SurfnetCheatcodesRpc { .minimum_balance_for_rent_exemption(data.len()) }); (data, rent) + } else if amount_only { + ( + token_account_data + .patch_amount_preserving_extensions(token_account.expected_data()) + .map_err(|e| { + Error::invalid_params(format!( + "Failed to patch token account amount: {}", + e + )) + })?, + initial_lamports, + ) } else { - (token_account_data.pack_into_vec(), initial_lamports) + ( + token_account_data + .pack_into_preserving_extensions(token_account.expected_data()) + .map_err(|e| { + Error::invalid_params(format!( + "Failed to pack token account data: {}", + e + )) + })?, + initial_lamports, + ) }; token_account.apply_update(|account| { diff --git a/crates/core/src/scenarios/README.md b/crates/core/src/scenarios/README.md index 4368f2b85..10950e578 100644 --- a/crates/core/src/scenarios/README.md +++ b/crates/core/src/scenarios/README.md @@ -18,6 +18,8 @@ Protocols that are natively supported by Surfpool will have their IDLs included - **Switchboard On-Demand** - On-demand oracle with QuoteAccount override template - **Kamino v1.x** – Lending protocol with Reserve liquidity, risk config, and Obligation health override templates - **Drift v2** - Perp and spot markets, user state, and global state +- **Pump v1** - Bonding curve launchpad with curve reserve and global config override templates +- **PumpSwap v1** - Constant-product AMM with pool state and global config override templates, including canonical pool derivation for migrated pump.fun coins For custom protocols, an IDL can be registered at runtime using the [`surfnet_registerIdl`](https://docs.surfpool.run/rpc/cheatcodes#surfnet-registeridl) RPC cheatcode. diff --git a/crates/core/src/scenarios/mod.rs b/crates/core/src/scenarios/mod.rs index b258bb5b7..fe28e4368 100644 --- a/crates/core/src/scenarios/mod.rs +++ b/crates/core/src/scenarios/mod.rs @@ -1,3 +1,5 @@ +pub mod pump_graduation; +pub mod pump_swap_price_shock; pub mod registry; pub use registry::TemplateRegistry; diff --git a/crates/core/src/scenarios/protocols/pump-amm/README.md b/crates/core/src/scenarios/protocols/pump-amm/README.md new file mode 100644 index 000000000..b9b442f57 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump-amm/README.md @@ -0,0 +1,60 @@ +# PumpSwap (pump-amm) + +The AMM a pump.fun coin trades on after its bonding curve completes and migrates. For the +bonding-curve side and the full lifecycle, see [`../pump/README.md`](../pump/README.md). + +Program: `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`. The IDL is copied verbatim from +pump-public-docs (`idl/pump_amm.json`). + +## Templates + +| Template | Account | Selected by | Use for | +| ------------------------- | -------------- | ------------- | --------------------------------------------------------------------------------- | +| `pump-amm-pool-state` | `Pool` | pool address | any pool, including non-canonical or non-WSOL ones | +| `pump-amm-canonical-pool` | `Pool` | coin mint | the canonical WSOL pool of a migrated coin, derived so you don't need its address | +| `pump-amm-global-config` | `GlobalConfig` | — (singleton) | pool fees and disable flags | + +## Field reference + +What each overridable field means and what overriding it lets you model. + +### `Pool` + +| Field | Meaning | Override it to | +| ------------------------ | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `lp_supply` | Total LP token supply before user burns and lock-ups | model LP state | +| `coin_creator` | Pubkey accruing the coin-creator fee for this pool | point creator fees at a key you control | +| `virtual_quote_reserves` | Appended quote reserves added to the quote vault when quoting (0 on every pool today) | shift the effective quote (reprice) without touching any vault balance | + +The price-setting reserves live in the pool's token accounts (`pool_base_token_account` / +`pool_quote_token_account`), not the `Pool` account - move those with the spl-token template. + +### `GlobalConfig` (singleton, `["global_config"]`) + +| Field | Meaning | Override it to | +| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------- | +| `lp_fee_basis_points`, `protocol_fee_basis_points`, `coin_creator_fee_basis_points` | Legacy flat fees; live trades read the fee program's `FeeConfig` | legacy - won't change what a swap charges | +| `disable_flags` | Bitmask disabling individual instructions (0 = everything enabled) | disable specific instructions to test error paths | + +## Pricing + +PumpSwap is a constant-product AMM. The reserves that set the price live in the pool's two +token accounts, not in the `Pool` account. Effective quote reserves are the quote vault +balance plus `Pool.virtual_quote_reserves` (which is 0 on every pool today). + +Two ways to move the price: + +- override `virtual_quote_reserves` on the pool — shifts the effective quote without + touching any balance; +- override the vault balances with the spl-token template — the vault addresses are in the + `Pool` account's `pool_base_token_account` / `pool_quote_token_account` fields. + +## Notes + +- The canonical template only works for coins that migrated to PumpSwap (roughly March 2025 + onward). Coins that graduated earlier went to Raydium and have no canonical pool — use + `pump-amm-pool-state` with the pool address for those. +- Fees on a live trade come from the external fee program's `FeeConfig`, not from the + basis-point fields on `GlobalConfig` (those are legacy). Overriding them here won't change + what a swap charges. +- Always set `fetchBeforeUse: true` so the fields you don't override keep their live values. diff --git a/crates/core/src/scenarios/protocols/pump-amm/v1/idl.json b/crates/core/src/scenarios/protocols/pump-amm/v1/idl.json new file mode 100644 index 000000000..a654b6f92 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump-amm/v1/idl.json @@ -0,0 +1,7390 @@ +{ + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "metadata": { + "name": "pump_amm", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "admin_set_coin_creator", + "docs": [ + "Overrides the coin creator for a canonical pump pool" + ], + "discriminator": [ + 242, + 40, + 117, + 145, + 73, + 96, + 105, + 104 + ], + "accounts": [ + { + "name": "admin_set_coin_creator_authority", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config" + }, + { + "name": "pool", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "coin_creator", + "type": "pubkey" + } + ] + }, + { + "name": "admin_update_token_incentives", + "discriminator": [ + 209, + 11, + 115, + 87, + 213, + 23, + 124, + 204 + ], + "accounts": [ + { + "name": "admin", + "writable": true, + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "global_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "token_supply_per_day", + "type": "u64" + } + ] + }, + { + "name": "boost_buy_and_burn", + "discriminator": [ + 105, + 68, + 6, + 175, + 0, + 7, + 35, + 162 + ], + "accounts": [ + { + "name": "pool" + }, + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "base_mint", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "boost_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 111, + 115, + 116, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ] + } + }, + { + "name": "boost_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "boost_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "quote_amount_in", + "type": "u64" + }, + { + "name": "min_base_amount_burned", + "type": "u64" + } + ] + }, + { + "name": "buy", + "docs": [ + "For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].", + "If provided and valid, the ATA will be initialized if needed." + ], + "discriminator": [ + 102, + 6, + 61, + 18, + 1, + 218, + 235, + 234 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "global_config" + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "protocol_fee_recipient" + }, + { + "name": "protocol_fee_recipient_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "protocol_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "coin_creator_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool.coin_creator", + "account": "Pool" + } + ] + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "base_amount_out", + "type": "u64" + }, + { + "name": "max_quote_amount_in", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "buy_exact_quote_in", + "docs": [ + "Given a budget of spendable_quote_in, buy at least min_base_amount_out", + "Fees will be deducted from spendable_quote_in", + "", + "f(quote) = tokens, where tokens >= min_base_amount_out", + "", + "Make sure the payer has enough SOL to cover creation of the following accounts (unless already created):", + "- protocol_fee_recipient_token_account: rent.minimum_balance(TokenAccount::LEN)", + "- coin_creator_vault_ata: rent.minimum_balance(TokenAccount::LEN)", + "- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)", + "", + "For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].", + "If provided and valid, the ATA will be initialized if needed." + ], + "discriminator": [ + 198, + 46, + 21, + 82, + 180, + 217, + 232, + 112 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "global_config" + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "protocol_fee_recipient" + }, + { + "name": "protocol_fee_recipient_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "protocol_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "coin_creator_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool.coin_creator", + "account": "Pool" + } + ] + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "spendable_quote_in", + "type": "u64" + }, + { + "name": "min_base_amount_out", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "claim_cashback", + "discriminator": [ + 37, + 58, + 35, + 126, + 190, + 53, + 228, + 197 + ], + "accounts": [ + { + "name": "user", + "writable": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "user_volume_accumulator_wsol_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "user_wsol_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + } + ], + "args": [] + }, + { + "name": "claim_token_incentives", + "discriminator": [ + 16, + 4, + 71, + 28, + 204, + 1, + 40, + 27 + ], + "accounts": [ + { + "name": "user" + }, + { + "name": "user_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "mint", + "relations": [ + "global_volume_accumulator" + ] + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "payer", + "writable": true, + "signer": true + } + ], + "args": [] + }, + { + "name": "close_user_volume_accumulator", + "discriminator": [ + 249, + 69, + 164, + 218, + 150, + 103, + 84, + 138 + ], + "accounts": [ + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "collect_coin_creator_fee", + "discriminator": [ + 160, + 57, + 89, + 42, + 181, + 139, + 43, + 66 + ], + "accounts": [ + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "coin_creator" + }, + { + "name": "coin_creator_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ] + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "coin_creator_token_account", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "create_config", + "discriminator": [ + 201, + 207, + 243, + 114, + 75, + 111, + 47, + 189 + ], + "accounts": [ + { + "name": "admin", + "writable": true, + "signer": true, + "address": "8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8" + }, + { + "name": "global_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "admin_set_coin_creator_authority", + "type": "pubkey" + } + ] + }, + { + "name": "create_pool", + "discriminator": [ + 233, + 146, + 209, + 142, + 207, + 104, + 64, + 188 + ], + "accounts": [ + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108 + ] + }, + { + "kind": "arg", + "path": "index" + }, + { + "kind": "account", + "path": "creator" + }, + { + "kind": "account", + "path": "base_mint" + }, + { + "kind": "account", + "path": "quote_mint" + } + ] + } + }, + { + "name": "global_config" + }, + { + "name": "creator", + "writable": true, + "signer": true + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "lp_mint", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ] + } + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "user_pool_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator" + }, + { + "kind": "account", + "path": "token_2022_program" + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "pool_base_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "pool_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "index", + "type": "u16" + }, + { + "name": "base_amount_in", + "type": "u64" + }, + { + "name": "quote_amount_in", + "type": "u64" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "deposit", + "discriminator": [ + 242, + 35, + 198, + 137, + 82, + 225, + 242, + 182 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "global_config" + }, + { + "name": "user", + "signer": true + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "lp_mint", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "user_pool_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lp_token_amount_out", + "type": "u64" + }, + { + "name": "max_base_amount_in", + "type": "u64" + }, + { + "name": "max_quote_amount_in", + "type": "u64" + } + ] + }, + { + "name": "disable", + "discriminator": [ + 185, + 173, + 187, + 90, + 216, + 15, + 238, + 233 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "disable_create_pool", + "type": "bool" + }, + { + "name": "disable_deposit", + "type": "bool" + }, + { + "name": "disable_withdraw", + "type": "bool" + }, + { + "name": "disable_buy", + "type": "bool" + }, + { + "name": "disable_sell", + "type": "bool" + } + ] + }, + { + "name": "extend_account", + "discriminator": [ + 234, + 102, + 194, + 203, + 150, + 72, + 62, + 229 + ], + "accounts": [ + { + "name": "account", + "writable": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "init_boost", + "discriminator": [ + 140, + 233, + 33, + 94, + 132, + 90, + 194, + 143 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "creator", + "writable": true, + "signer": true + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "pool_base_token_account", + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "boost_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 111, + 115, + 116, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ] + } + }, + { + "name": "boost_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "boost_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "quote_token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "init_user_volume_accumulator", + "discriminator": [ + 94, + 6, + 202, + 115, + 255, + 96, + 232, + 183 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "user" + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "migrate_pool_coin_creator", + "docs": [ + "Migrate Pool Coin Creator to Sharing Config" + ], + "discriminator": [ + 208, + 8, + 159, + 4, + 74, + 175, + 16, + 58 + ], + "accounts": [ + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108 + ] + }, + { + "kind": "account", + "path": "pool.index", + "account": "Pool" + }, + { + "kind": "account", + "path": "pool.creator", + "account": "Pool" + }, + { + "kind": "account", + "path": "pool.base_mint", + "account": "Pool" + }, + { + "kind": "account", + "path": "pool.quote_mint", + "account": "Pool" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "pool.base_mint", + "account": "Pool" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "sell", + "discriminator": [ + 51, + 230, + 133, + 164, + 1, + 127, + 131, + 173 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "global_config" + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "protocol_fee_recipient" + }, + { + "name": "protocol_fee_recipient_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "protocol_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "coin_creator_vault_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool.coin_creator", + "account": "Pool" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "base_amount_in", + "type": "u64" + }, + { + "name": "min_quote_amount_out", + "type": "u64" + } + ] + }, + { + "name": "set_boost_authority", + "discriminator": [ + 227, + 149, + 76, + 42, + 130, + 39, + 234, + 205 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "boost_authority" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "set_coin_creator", + "docs": [ + "Sets Pool::coin_creator from Metaplex metadata creator or BondingCurve::creator" + ], + "discriminator": [ + 210, + 149, + 128, + 45, + 188, + 58, + 78, + 175 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "metadata", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + }, + { + "kind": "account", + "path": "pool.base_mint", + "account": "Pool" + } + ], + "program": { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + } + } + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "pool.base_mint", + "account": "Pool" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "set_reserved_fee_recipients", + "discriminator": [ + 111, + 172, + 162, + 232, + 114, + 89, + 213, + 142 + ], + "accounts": [ + { + "name": "global_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ] + } + }, + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "whitelist_pda", + "type": "pubkey" + } + ] + }, + { + "name": "sync_user_volume_accumulator", + "discriminator": [ + 86, + 31, + 192, + 87, + 163, + 87, + 79, + 238 + ], + "accounts": [ + { + "name": "user" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "toggle_boost", + "discriminator": [ + 117, + 161, + 160, + 74, + 223, + 137, + 118, + 99 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_cashback_enabled", + "discriminator": [ + 115, + 103, + 224, + 255, + 189, + 89, + 86, + 195 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_mayhem_mode", + "discriminator": [ + 1, + 9, + 111, + 208, + 100, + 31, + 255, + 163 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "transfer_creator_fees_to_pump", + "docs": [ + "Transfer creator fees to pump creator vault", + "If coin creator fees are currently below rent.minimum_balance(TokenAccount::LEN)", + "The transfer will be skipped" + ], + "discriminator": [ + 139, + 52, + 134, + 85, + 228, + 229, + 108, + 241 + ], + "accounts": [ + { + "name": "wsol_mint", + "docs": [ + "Pump Canonical Pool are quoted in wSOL" + ] + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "coin_creator" + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ] + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "transfer_creator_fees_to_pump_v2", + "discriminator": [ + 1, + 33, + 78, + 185, + 33, + 67, + 44, + 92 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "quote_mint" + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "coin_creator" + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ] + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pump_creator_vault" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "update_admin", + "discriminator": [ + 161, + 176, + 40, + 213, + 60, + 184, + 179, + 228 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "new_admin" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "update_buyback_config", + "discriminator": [ + 251, + 224, + 171, + 146, + 160, + 26, + 113, + 233 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "buyback_basis_points", + "type": { + "option": "u64" + } + } + ] + }, + { + "name": "update_fee_config", + "discriminator": [ + 104, + 184, + 103, + 242, + 88, + 151, + 107, + 20 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "admin_set_coin_creator_authority", + "type": "pubkey" + } + ] + }, + { + "name": "withdraw", + "discriminator": [ + 183, + 18, + 70, + 156, + 148, + 109, + 161, + 34 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "global_config" + }, + { + "name": "user", + "signer": true + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "lp_mint", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "user_pool_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lp_token_amount_in", + "type": "u64" + }, + { + "name": "min_base_amount_out", + "type": "u64" + }, + { + "name": "min_quote_amount_out", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "BondingCurve", + "discriminator": [ + 23, + 183, + 248, + 55, + 96, + 216, + 172, + 96 + ] + }, + { + "name": "FeeConfig", + "discriminator": [ + 143, + 52, + 146, + 187, + 219, + 123, + 76, + 155 + ] + }, + { + "name": "GlobalConfig", + "discriminator": [ + 149, + 8, + 156, + 202, + 160, + 252, + 176, + 217 + ] + }, + { + "name": "GlobalVolumeAccumulator", + "discriminator": [ + 202, + 42, + 246, + 43, + 142, + 190, + 30, + 255 + ] + }, + { + "name": "Pool", + "discriminator": [ + 241, + 154, + 109, + 4, + 17, + 177, + 109, + 188 + ] + }, + { + "name": "SharingConfig", + "discriminator": [ + 216, + 74, + 9, + 0, + 56, + 140, + 93, + 75 + ] + }, + { + "name": "UserVolumeAccumulator", + "discriminator": [ + 86, + 255, + 112, + 14, + 102, + 53, + 154, + 250 + ] + } + ], + "events": [ + { + "name": "AdminSetCoinCreatorEvent", + "discriminator": [ + 45, + 220, + 93, + 24, + 25, + 97, + 172, + 104 + ] + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "discriminator": [ + 147, + 250, + 108, + 120, + 247, + 29, + 67, + 222 + ] + }, + { + "name": "BoostBuyAndBurnEvent", + "discriminator": [ + 63, + 69, + 28, + 22, + 48, + 92, + 194, + 185 + ] + }, + { + "name": "BuyEvent", + "discriminator": [ + 103, + 244, + 82, + 31, + 44, + 245, + 119, + 119 + ] + }, + { + "name": "ClaimCashbackEvent", + "discriminator": [ + 226, + 214, + 246, + 33, + 7, + 242, + 147, + 229 + ] + }, + { + "name": "ClaimTokenIncentivesEvent", + "discriminator": [ + 79, + 172, + 246, + 49, + 205, + 91, + 206, + 232 + ] + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "discriminator": [ + 146, + 159, + 189, + 172, + 146, + 88, + 56, + 244 + ] + }, + { + "name": "CollectCoinCreatorFeeEvent", + "discriminator": [ + 232, + 245, + 194, + 238, + 234, + 218, + 58, + 89 + ] + }, + { + "name": "CreateConfigEvent", + "discriminator": [ + 107, + 52, + 89, + 129, + 55, + 226, + 81, + 22 + ] + }, + { + "name": "CreatePoolEvent", + "discriminator": [ + 177, + 49, + 12, + 210, + 160, + 118, + 167, + 116 + ] + }, + { + "name": "DepositEvent", + "discriminator": [ + 120, + 248, + 61, + 83, + 31, + 142, + 107, + 144 + ] + }, + { + "name": "DisableEvent", + "discriminator": [ + 107, + 253, + 193, + 76, + 228, + 202, + 27, + 104 + ] + }, + { + "name": "ExtendAccountEvent", + "discriminator": [ + 97, + 97, + 215, + 144, + 93, + 146, + 22, + 124 + ] + }, + { + "name": "InitBoostEvent", + "discriminator": [ + 174, + 124, + 74, + 249, + 4, + 81, + 246, + 17 + ] + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "discriminator": [ + 134, + 36, + 13, + 72, + 232, + 101, + 130, + 216 + ] + }, + { + "name": "MigratePoolCoinCreatorEvent", + "discriminator": [ + 170, + 221, + 82, + 199, + 147, + 165, + 247, + 46 + ] + }, + { + "name": "ReservedFeeRecipientsEvent", + "discriminator": [ + 43, + 188, + 250, + 18, + 221, + 75, + 187, + 95 + ] + }, + { + "name": "SellEvent", + "discriminator": [ + 62, + 47, + 55, + 10, + 165, + 3, + 220, + 42 + ] + }, + { + "name": "SetBondingCurveCoinCreatorEvent", + "discriminator": [ + 242, + 231, + 235, + 102, + 65, + 99, + 189, + 211 + ] + }, + { + "name": "SetBoostAuthorityEvent", + "discriminator": [ + 89, + 128, + 240, + 141, + 91, + 202, + 71, + 105 + ] + }, + { + "name": "SetMetaplexCoinCreatorEvent", + "discriminator": [ + 150, + 107, + 199, + 123, + 124, + 207, + 102, + 228 + ] + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "discriminator": [ + 197, + 122, + 167, + 124, + 116, + 81, + 91, + 255 + ] + }, + { + "name": "UpdateAdminEvent", + "discriminator": [ + 225, + 152, + 171, + 87, + 246, + 63, + 66, + 234 + ] + }, + { + "name": "UpdateFeeConfigEvent", + "discriminator": [ + 90, + 23, + 65, + 35, + 62, + 244, + 188, + 208 + ] + }, + { + "name": "WithdrawEvent", + "discriminator": [ + 22, + 9, + 133, + 26, + 160, + 44, + 71, + 192 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "FeeBasisPointsExceedsMaximum" + }, + { + "code": 6001, + "name": "ZeroBaseAmount" + }, + { + "code": 6002, + "name": "ZeroQuoteAmount" + }, + { + "code": 6003, + "name": "TooLittlePoolTokenLiquidity" + }, + { + "code": 6004, + "name": "ExceededSlippage" + }, + { + "code": 6005, + "name": "InvalidAdmin" + }, + { + "code": 6006, + "name": "UnsupportedBaseMint" + }, + { + "code": 6007, + "name": "UnsupportedQuoteMint" + }, + { + "code": 6008, + "name": "InvalidBaseMint" + }, + { + "code": 6009, + "name": "InvalidQuoteMint" + }, + { + "code": 6010, + "name": "InvalidLpMint" + }, + { + "code": 6011, + "name": "AllProtocolFeeRecipientsShouldBeNonZero" + }, + { + "code": 6012, + "name": "UnsortedNotUniqueProtocolFeeRecipients" + }, + { + "code": 6013, + "name": "InvalidProtocolFeeRecipient" + }, + { + "code": 6014, + "name": "InvalidPoolBaseTokenAccount" + }, + { + "code": 6015, + "name": "InvalidPoolQuoteTokenAccount" + }, + { + "code": 6016, + "name": "BuyMoreBaseAmountThanPoolReserves" + }, + { + "code": 6017, + "name": "DisabledCreatePool" + }, + { + "code": 6018, + "name": "DisabledDeposit" + }, + { + "code": 6019, + "name": "DisabledWithdraw" + }, + { + "code": 6020, + "name": "DisabledBuy" + }, + { + "code": 6021, + "name": "DisabledSell" + }, + { + "code": 6022, + "name": "SameMint" + }, + { + "code": 6023, + "name": "Overflow" + }, + { + "code": 6024, + "name": "Truncation" + }, + { + "code": 6025, + "name": "DivisionByZero" + }, + { + "code": 6026, + "name": "NewSizeLessThanCurrentSize" + }, + { + "code": 6027, + "name": "AccountTypeNotSupported" + }, + { + "code": 6028, + "name": "OnlyCanonicalPumpPoolsCanHaveCoinCreator" + }, + { + "code": 6029, + "name": "InvalidAdminSetCoinCreatorAuthority" + }, + { + "code": 6030, + "name": "StartTimeInThePast" + }, + { + "code": 6031, + "name": "EndTimeInThePast" + }, + { + "code": 6032, + "name": "EndTimeBeforeStartTime" + }, + { + "code": 6033, + "name": "TimeRangeTooLarge" + }, + { + "code": 6034, + "name": "EndTimeBeforeCurrentDay" + }, + { + "code": 6035, + "name": "SupplyUpdateForFinishedRange" + }, + { + "code": 6036, + "name": "DayIndexAfterEndIndex" + }, + { + "code": 6037, + "name": "DayInActiveRange" + }, + { + "code": 6038, + "name": "InvalidIncentiveMint" + }, + { + "code": 6039, + "name": "BuyNotEnoughQuoteTokensToCoverFees", + "msg": "buy: Not enough quote tokens to cover for fees." + }, + { + "code": 6040, + "name": "BuySlippageBelowMinBaseAmountOut", + "msg": "buy: slippage - would buy less tokens than expected min_base_amount_out" + }, + { + "code": 6041, + "name": "MayhemModeDisabled" + }, + { + "code": 6042, + "name": "OnlyPumpPoolsMayhemMode" + }, + { + "code": 6043, + "name": "MayhemModeInDesiredState" + }, + { + "code": 6044, + "name": "NotEnoughRemainingAccounts" + }, + { + "code": 6045, + "name": "InvalidSharingConfigBaseMint" + }, + { + "code": 6046, + "name": "InvalidSharingConfigCoinCreator" + }, + { + "code": 6047, + "name": "CoinCreatorMigratedToSharingConfig", + "msg": "coin creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead" + }, + { + "code": 6048, + "name": "CreatorVaultMigratedToSharingConfig", + "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" + }, + { + "code": 6049, + "name": "CashbackNotEnabled", + "msg": "Cashback is disabled" + }, + { + "code": 6050, + "name": "OnlyPumpPoolsCashback" + }, + { + "code": 6051, + "name": "CashbackNotInDesiredState" + }, + { + "code": 6052, + "name": "TokensInVaultLessThanCashbackEarned" + }, + { + "code": 6053, + "name": "BuybackFeeRecipientNotAuthorized", + "msg": "Buyback fee recipient not authorized" + }, + { + "code": 6054, + "name": "AllBuybackFeeRecipientsShouldBeNonZero" + }, + { + "code": 6055, + "name": "NotUniqueBuybackFeeRecipients" + }, + { + "code": 6056, + "name": "BuybackBasisPointsOutOfRange", + "msg": "buyback_basis_points must be <= 10_000" + }, + { + "code": 6057, + "name": "WrongBuybackFeeRecipientsCount", + "msg": "buyback fee recipients require exactly 8 remaining accounts (or none)" + }, + { + "code": 6058, + "name": "BuybackFeeRecipientMissing" + }, + { + "code": 6059, + "name": "MissingCashbackAccounts", + "msg": "Cashback trade is missing the required remaining accounts" + }, + { + "code": 6060, + "name": "InvalidCashbackAccumulator", + "msg": "Cashback user_volume_accumulator account is invalid" + }, + { + "code": 6061, + "name": "InvalidCashbackAccumulatorAta", + "msg": "Cashback user_volume_accumulator ATA is missing or invalid" + }, + { + "code": 6062, + "name": "InvalidPoolV2", + "msg": "pool_v2 remaining account is missing or invalid" + }, + { + "code": 6063, + "name": "InsufficientRealQuoteReserves", + "msg": "BOOST: sell output exceeds the real quote vault. effective = real + virtual is pricing-only; payout is capped at real_vault, so quote min(out, real_vault)" + }, + { + "code": 6064, + "name": "BoostPoolLiquidityUnsupported", + "msg": "BOOST: deposit/withdraw don't apply to boost pools" + }, + { + "code": 6065, + "name": "PoolCannotBoost", + "msg": "BOOST: pool cannot be boosted (no virtual reserves)" + }, + { + "code": 6066, + "name": "BoostDisabled", + "msg": "BOOST: boost is disabled" + }, + { + "code": 6067, + "name": "SeedLockViolation", + "msg": "BOOST: lp_supply must never drop below the circulating LP mint supply" + } + ], + "types": [ + { + "name": "AdminSetCoinCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin_set_coin_creator_authority", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "old_coin_creator", + "type": "pubkey" + }, + { + "name": "new_coin_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "token_supply_per_day", + "type": "u64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "BondingCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + } + ] + } + }, + { + "name": "BoostBuyAndBurnEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "quote_amount_in_requested", + "type": "u64" + }, + { + "name": "quote_amount_in_used", + "type": "u64" + }, + { + "name": "base_amount_burned", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "real_quote_reserves_after", + "type": "u64" + }, + { + "name": "base_reserves_after", + "type": "u64" + }, + { + "name": "boost_vault_remaining", + "type": "u64" + } + ] + } + }, + { + "name": "BuyEvent", + "docs": [ + "ix_name: \"buy\" | \"buy_exact_quote_in\"" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "base_amount_out", + "type": "u64" + }, + { + "name": "max_quote_amount_in", + "type": "u64" + }, + { + "name": "user_base_token_reserves", + "type": "u64" + }, + { + "name": "user_quote_token_reserves", + "type": "u64" + }, + { + "name": "pool_base_token_reserves", + "type": "u64" + }, + { + "name": "pool_quote_token_reserves", + "type": "u64" + }, + { + "name": "quote_amount_in", + "type": "u64" + }, + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "lp_fee", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee", + "type": "u64" + }, + { + "name": "quote_amount_in_with_lp_fee", + "type": "u64" + }, + { + "name": "user_quote_amount_in", + "type": "u64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "user_base_token_account", + "type": "pubkey" + }, + { + "name": "user_quote_token_account", + "type": "pubkey" + }, + { + "name": "protocol_fee_recipient", + "type": "pubkey" + }, + { + "name": "protocol_fee_recipient_token_account", + "type": "pubkey" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "coin_creator_fee", + "type": "u64" + }, + { + "name": "track_volume", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "min_base_amount_out", + "type": "u64" + }, + { + "name": "ix_name", + "type": "string" + }, + { + "name": "cashback_fee_basis_points", + "type": "u64" + }, + { + "name": "cashback", + "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "can_boost", + "type": "bool" + }, + { + "name": "base_supply", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimCashbackEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed", + "type": "u64" + }, + { + "name": "total_cashback_earned", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + } + ] + } + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "CollectCoinCreatorFeeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "coin_creator_fee", + "type": "u64" + }, + { + "name": "coin_creator_vault_ata", + "type": "pubkey" + }, + { + "name": "coin_creator_token_account", + "type": "pubkey" + } + ] + } + }, + { + "name": "ConfigStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Paused" + }, + { + "name": "Active" + } + ] + } + }, + { + "name": "CreateConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "admin_set_coin_creator_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "CreatePoolEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "base_mint_decimals", + "type": "u8" + }, + { + "name": "quote_mint_decimals", + "type": "u8" + }, + { + "name": "base_amount_in", + "type": "u64" + }, + { + "name": "quote_amount_in", + "type": "u64" + }, + { + "name": "pool_base_amount", + "type": "u64" + }, + { + "name": "pool_quote_amount", + "type": "u64" + }, + { + "name": "minimum_liquidity", + "type": "u64" + }, + { + "name": "initial_liquidity", + "type": "u64" + }, + { + "name": "lp_token_amount_out", + "type": "u64" + }, + { + "name": "pool_bump", + "type": "u8" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "lp_mint", + "type": "pubkey" + }, + { + "name": "user_base_token_account", + "type": "pubkey" + }, + { + "name": "user_quote_token_account", + "type": "pubkey" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + } + ] + } + }, + { + "name": "DepositEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "lp_token_amount_out", + "type": "u64" + }, + { + "name": "max_base_amount_in", + "type": "u64" + }, + { + "name": "max_quote_amount_in", + "type": "u64" + }, + { + "name": "user_base_token_reserves", + "type": "u64" + }, + { + "name": "user_quote_token_reserves", + "type": "u64" + }, + { + "name": "pool_base_token_reserves", + "type": "u64" + }, + { + "name": "pool_quote_token_reserves", + "type": "u64" + }, + { + "name": "base_amount_in", + "type": "u64" + }, + { + "name": "quote_amount_in", + "type": "u64" + }, + { + "name": "lp_mint_supply", + "type": "u64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "user_base_token_account", + "type": "pubkey" + }, + { + "name": "user_quote_token_account", + "type": "pubkey" + }, + { + "name": "user_pool_token_account", + "type": "pubkey" + } + ] + } + }, + { + "name": "DisableEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "disable_create_pool", + "type": "bool" + }, + { + "name": "disable_deposit", + "type": "bool" + }, + { + "name": "disable_withdraw", + "type": "bool" + }, + { + "name": "disable_buy", + "type": "bool" + }, + { + "name": "disable_sell", + "type": "bool" + } + ] + } + }, + { + "name": "ExtendAccountEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "account", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "current_size", + "type": "u64" + }, + { + "name": "new_size", + "type": "u64" + } + ] + } + }, + { + "name": "FeeConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + } + ] + } + }, + { + "name": "FeeTier", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market_cap_lamports_threshold", + "type": "u128" + }, + { + "name": "fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "Fees", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lp_fee_bps", + "type": "u64" + }, + { + "name": "protocol_fee_bps", + "type": "u64" + }, + { + "name": "creator_fee_bps", + "type": "u64" + } + ] + } + }, + { + "name": "GlobalConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "admin", + "docs": [ + "The admin pubkey" + ], + "type": "pubkey" + }, + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "disable_flags", + "docs": [ + "Flags to disable certain functionality", + "bit 0 - Disable create pool", + "bit 1 - Disable deposit", + "bit 2 - Disable withdraw", + "bit 3 - Disable buy", + "bit 4 - Disable sell" + ], + "type": "u8" + }, + { + "name": "protocol_fee_recipients", + "docs": [ + "Addresses of the protocol fee recipients" + ], + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "admin_set_coin_creator_authority", + "docs": [ + "The admin authority for setting coin creators" + ], + "type": "pubkey" + }, + { + "name": "whitelist_pda", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "mayhem_mode_enabled", + "type": "bool" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + }, + { + "name": "is_cashback_enabled", + "type": "bool" + }, + { + "name": "buyback_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "buyback_basis_points", + "type": "u64" + }, + { + "name": "boost_authority", + "type": "pubkey" + }, + { + "name": "boost_enabled", + "type": "bool" + } + ] + } + }, + { + "name": "GlobalVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "total_token_supply", + "type": { + "array": [ + "u64", + 30 + ] + } + }, + { + "name": "sol_volumes", + "type": { + "array": [ + "u64", + 30 + ] + } + } + ] + } + }, + { + "name": "InitBoostEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "real_quote_reserves_after", + "type": "u64" + } + ] + } + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "payer", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "MigratePoolCoinCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "old_coin_creator", + "type": "pubkey" + }, + { + "name": "new_coin_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "OptionBool", + "type": { + "kind": "struct", + "fields": [ + "bool" + ] + } + }, + { + "name": "Pool", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool_bump", + "type": "u8" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "lp_mint", + "type": "pubkey" + }, + { + "name": "pool_base_token_account", + "type": "pubkey" + }, + { + "name": "pool_quote_token_account", + "type": "pubkey" + }, + { + "name": "lp_supply", + "docs": [ + "True circulating supply without burns and lock-ups" + ], + "type": "u64" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "virtual_quote_reserves", + "docs": [ + "For non-boost pools, value is 0, so the behavior is identical to legacy pools." + ], + "type": "i128" + } + ] + } + }, + { + "name": "ReservedFeeRecipientsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + } + ] + } + }, + { + "name": "SellEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "base_amount_in", + "type": "u64" + }, + { + "name": "min_quote_amount_out", + "type": "u64" + }, + { + "name": "user_base_token_reserves", + "type": "u64" + }, + { + "name": "user_quote_token_reserves", + "type": "u64" + }, + { + "name": "pool_base_token_reserves", + "type": "u64" + }, + { + "name": "pool_quote_token_reserves", + "type": "u64" + }, + { + "name": "quote_amount_out", + "type": "u64" + }, + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "lp_fee", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee", + "type": "u64" + }, + { + "name": "quote_amount_out_without_lp_fee", + "type": "u64" + }, + { + "name": "user_quote_amount_out", + "type": "u64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "user_base_token_account", + "type": "pubkey" + }, + { + "name": "user_quote_token_account", + "type": "pubkey" + }, + { + "name": "protocol_fee_recipient", + "type": "pubkey" + }, + { + "name": "protocol_fee_recipient_token_account", + "type": "pubkey" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "coin_creator_fee", + "type": "u64" + }, + { + "name": "cashback_fee_basis_points", + "type": "u64" + }, + { + "name": "cashback", + "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "can_boost", + "type": "bool" + }, + { + "name": "base_supply", + "type": "u64" + } + ] + } + }, + { + "name": "SetBondingCurveCoinCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "coin_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetBoostAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "old_boost_authority", + "type": "pubkey" + }, + { + "name": "new_boost_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetMetaplexCoinCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "metadata", + "type": "pubkey" + }, + { + "name": "coin_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "Shareholder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "pubkey" + }, + { + "name": "share_bps", + "type": "u16" + } + ] + } + }, + { + "name": "SharingConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "status", + "type": { + "defined": { + "name": "ConfigStatus" + } + } + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "admin_revoked", + "type": "bool" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + } + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "total_claimed_tokens_before", + "type": "u64" + }, + { + "name": "total_claimed_tokens_after", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "UpdateAdminEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "new_admin", + "type": "pubkey" + } + ] + } + }, + { + "name": "UpdateFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "lp_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_basis_points", + "type": "u64" + }, + { + "name": "protocol_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "coin_creator_fee_basis_points", + "type": "u64" + }, + { + "name": "admin_set_coin_creator_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "UserVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "needs_claim", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "has_total_claimed_tokens", + "type": "bool" + }, + { + "name": "cashback_earned", + "type": "u64" + }, + { + "name": "total_cashback_claimed", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "lp_token_amount_in", + "type": "u64" + }, + { + "name": "min_base_amount_out", + "type": "u64" + }, + { + "name": "min_quote_amount_out", + "type": "u64" + }, + { + "name": "user_base_token_reserves", + "type": "u64" + }, + { + "name": "user_quote_token_reserves", + "type": "u64" + }, + { + "name": "pool_base_token_reserves", + "type": "u64" + }, + { + "name": "pool_quote_token_reserves", + "type": "u64" + }, + { + "name": "base_amount_out", + "type": "u64" + }, + { + "name": "quote_amount_out", + "type": "u64" + }, + { + "name": "lp_mint_supply", + "type": "u64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "user_base_token_account", + "type": "pubkey" + }, + { + "name": "user_quote_token_account", + "type": "pubkey" + }, + { + "name": "user_pool_token_account", + "type": "pubkey" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/crates/core/src/scenarios/protocols/pump-amm/v1/overrides.yaml b/crates/core/src/scenarios/protocols/pump-amm/v1/overrides.yaml new file mode 100644 index 000000000..796ce4692 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump-amm/v1/overrides.yaml @@ -0,0 +1,165 @@ +protocol: PumpSwap +version: v1 +account_type: Pool +idl_file_path: idl.json + +tags: + - amm + - constant-product + - defi + +constants: + # Token mints loaded from verified tokens registry + token_mint: + label: Token + description: Select the base token mint (the pump.fun coin) from verified tokens + source: verified_tokens + address_suffix: pump + +templates: + - id: pump-amm-pool-state + name: Override Pool State + description: Override any PumpSwap pool by specifying its address directly + idl_account_name: Pool + properties: + - path: lp_supply + label: LP Supply + description: Total LP token supply before user burns and lock-ups + - path: coin_creator + label: Coin Creator + description: Pubkey accruing the coin-creator fee for this pool + - path: virtual_quote_reserves + label: Virtual Quote Reserves + description: Appended quote reserves added to the quote vault balance when quoting (0 on all pools today) + llm_context: | + Pass the pool's own address; unlike pump-amm-canonical-pool this template derives + nothing. Use it for user-created or non-WSOL pools, or any pool whose address you + already have. Set fetchBeforeUse: true so the fields you don't override keep their + live values. + + PumpSwap is a constant-product AMM whose reserves live in the pool's token accounts, + not in the Pool account: price = effective_quote_reserves / base_vault_balance, where + effective_quote_reserves = pool_quote_token_account balance + Pool.virtual_quote_reserves. + Move price by setting virtual_quote_reserves here, or by editing the vault balances with + the spl-token template. + address: + type: pubkey + + - id: pump-amm-canonical-pool + name: Override Canonical Pool (Custom) + description: | + Override the canonical PumpSwap pool of a migrated pump.fun coin by specifying its mint. + Covers WSOL-quoted canonical migrations only; for any other pool pass its address to + pump-amm-pool-state. Two PDAs are derived: + - Pool authority (Pump program): ["pool-authority", base_mint] + - Pool (PumpSwap): ["pool", index 0 as u16 LE, pool_authority, base_mint, WSOL] + idl_account_name: Pool + properties: + - path: lp_supply + label: LP Supply + description: Total LP token supply before user burns and lock-ups + - path: coin_creator + label: Coin Creator + description: Pubkey accruing the coin-creator fee for this pool + - path: virtual_quote_reserves + label: Virtual Quote Reserves + description: Appended quote reserves added to the quote vault balance when quoting (0 on all pools today) + - path: base_mint + type: constant_ref + label: Base Token Mint + constant: token_mint + llm_context: | + Set fetchBeforeUse: true so the fields you don't override keep their live values. + Use false only for a later override that builds on state an earlier one prepared in + the same scenario. + + WORKS ONLY FOR MIGRATED PUMP.FUN COINS. Coins that completed before PumpSwap + launched (March 2025) migrated to Raydium and have no canonical pool. The + canonical pool (index 0) is created by the Pump program's migrate instruction: its creator seed is the Pump program's + pool-authority PDA ["pool-authority", base_mint] and its quote mint is always + wrapped SOL — this template cannot derive pools quoted in any other mint. Pools + created directly by users carry the creator's own pubkey and possibly a different + index — override those (and any non-WSOL pool) with the pump-amm-pool-state + template by passing the pool address directly. + + PRICING: PumpSwap is a constant-product AMM whose reserves live in the pool's token + accounts, NOT in the Pool account: + - price = effective_quote_reserves / base_vault_balance (coin has 6 decimals, SOL 9) + - effective_quote_reserves = pool_quote_token_account balance + Pool.virtual_quote_reserves + + TO SIMULATE PRICE CHANGES either: + 1. modify the vault token balances with the spl-token template — the vault addresses + are stored in the Pool account's pool_base_token_account and + pool_quote_token_account fields, or + 2. set virtual_quote_reserves on this Pool account — it shifts the effective quote + reserves without touching any token balance. + + EXAMPLE - "make a migrated coin ~10x more expensive to buy": read the pool's quote + vault balance Q (getTokenAccountBalance on pool_quote_token_account), then one + override at slot 0, fetchBeforeUse: true, values = + base_mint: + virtual_quote_reserves: <9 * Q> + Effective quote reserves become Q + 9*Q = 10*Q, so the same buy costs about 10x + what it did before, and a buy sized to the old price fails with ExceededSlippage + (0x1774). Use spl-token on the vaults instead when you want the balances, not just + the quote, to move. + + FEES per swap: trades read the fee program's FeeConfig market-cap fee tiers — a + required account of every buy and sell; GlobalConfig's flat lp_fee_basis_points + + protocol_fee_basis_points + coin_creator_fee_basis_points are legacy fields from + before the external fee program. + address: + type: pda + program_id: pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA + seeds: + - type: string + value: pool + - type: u16_le + value: 0 + - type: derived_pda + program_id: 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P + seeds: + - type: string + value: pool-authority + - type: property_ref + value: base_mint + - type: property_ref + value: base_mint + - type: pubkey + value: So11111111111111111111111111111111111111112 + + - id: pump-amm-global-config + name: Override Global Config + description: | + Override the PumpSwap program's single GlobalConfig account + (PDA derived from ["global_config"], resolving to ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw). + idl_account_name: GlobalConfig + properties: + - path: lp_fee_basis_points + label: LP Fee Basis Points (legacy) + description: Legacy LP fee in basis points (deposits and withdrawals are free); live trades read fees from the fee program's FeeConfig + - path: protocol_fee_basis_points + label: Protocol Fee Basis Points (legacy) + description: Legacy protocol fee in basis points, superseded by the external fee program + - path: coin_creator_fee_basis_points + label: Coin Creator Fee Basis Points (legacy) + description: Legacy coin-creator fee in basis points; accrues to each pool's coin_creator + - path: disable_flags + label: Disable Flags + description: Bitmask disabling individual instructions (0 = everything enabled) + llm_context: | + Set fetchBeforeUse: true so GlobalConfig's admin and protocol fee recipient list keep + their live values; false only to build on an earlier override's prepared state. + + lp_fee_basis_points, protocol_fee_basis_points and coin_creator_fee_basis_points + are legacy fields from before the external fee program (deposits and withdrawals + are free); trades read fees from the fee program's FeeConfig market-cap fee tiers + — a required account of every buy and sell. coin_creator_fee_basis_points accrues + to each pool's coin_creator. disable_flags is a bitmask disabling individual + instructions (0 = everything enabled). + address: + type: pda + program_id: pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA + seeds: + - type: string + value: global_config diff --git a/crates/core/src/scenarios/protocols/pump/README.md b/crates/core/src/scenarios/protocols/pump/README.md new file mode 100644 index 000000000..17ae29a46 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump/README.md @@ -0,0 +1,144 @@ +# Pump / PumpSwap state preparation + +Declarative state-preparation templates for the pump.fun ecosystem. A pump.fun coin's +life spans two on-chain programs, so the integration covers both: + +1. **Pump** (bonding curve launchpad) — new coins trade on a constant-product curve over + synthetic (virtual) reserves until the curve is bought out (`complete = true`). +2. **PumpSwap** (`pump-amm`, pump.fun's AMM) — completed curves migrate here; reserves + live in the pool's token accounts, not in the Pool account itself. + +Both programs publish Anchor IDLs, so the templates use the standard IDL override path +(no raw-offset layout needed). + +## Program identity (verified 2026-08-07) + +| | Pump | PumpSwap | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Program ID | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | +| ProgramData | `B5MvUwXdiW1NMM6QFFD3ssPKBujD4zMohncbM73Z2BQu` | `6naEzKeUuFh1Jeeu51NXQgr5qkXgXtc9WKNct4xynVJc` | +| Last deployed slot | 433095571 | 433112355 | +| Bundled IDL | byte-identical to [`idl/pump.json`](https://github.com/pump-fun/pump-public-docs/blob/main/idl/pump.json) at pump-public-docs commit `3c6721a67c0b` | byte-identical to [`idl/pump_amm.json`](https://github.com/pump-fun/pump-public-docs/blob/main/idl/pump_amm.json) at commit `2c22246b6708` | + +A later deployment slot than the one above means the program was upgraded and this +integration must be revisited (layouts, formulas, fee wiring). + +Fees: every buy/sell passes the fee program's `FeeConfig` (market-cap fee tiers) as a +required account under `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ`; the flat +basis-point fields on `Global` / `GlobalConfig` are legacy. + +## Templates + +| Template | Account | Address | Use for | +| --------------------------- | -------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `pump-bonding-curve-custom` | `BondingCurve` | PDA `["bonding-curve", mint]` | any coin's curve, selected by mint | +| `pump-global` | `Global` | PDA `["global"]` → `4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf` | fee/init parameters for curves created after the override | +| `pump-amm-pool-state` | `Pool` | caller-provided pubkey | any pool by raw address (only path for non-canonical / non-WSOL pools) | +| `pump-amm-canonical-pool` | `Pool` | PDA `["pool", u16le(0), PDA(pump, ["pool-authority", mint]), mint, WSOL]` | the canonical WSOL-quoted pool of a migrated coin, selected by mint | +| `pump-amm-global-config` | `GlobalConfig` | PDA `["global_config"]` → `ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw` | pool fee/disable flags | + +Notes: + +- The mint catalogs offer only verified tokens whose address ends in `pump` + (976 mints in the bundled catalog; `address_suffix: pump` in both `overrides.yaml` + files). Coins outside the verified catalog — including freshly launched ones — can + only be targeted through the raw REST payload, which performs no catalog validation. +- `pump-amm-canonical-pool` covers WSOL-quoted canonical migrations only; any other + pool goes through `pump-amm-pool-state` with its address. +- Always set `fetchBeforeUse: true` so non-overridden fields keep their live values. + +## Field reference + +What each overridable field means and what overriding it lets you model. Keep the +curve-lifetime invariants when you touch reserves (`virtual − real` = 279.9T tokens and +30 SOL of quote with today's mainnet `Global` defaults). + +### `BondingCurve` + +| Field | Meaning | Override it to | +| ------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| `virtual_token_reserves` | Synthetic token reserves in the price formula (raw, 6 decimals) | reprice the curve - spot = `virtual_quote / virtual_token` | +| `virtual_quote_reserves` | Synthetic quote reserves (lamports for SOL-quoted coins) | reprice the curve | +| `real_token_reserves` | Tokens the curve still holds; completion is when this reaches 0 | set how close to graduation the curve sits (a small value = one buy away) | +| `real_quote_reserves` | Quote the curve actually holds | model accumulated quote | +| `complete` | True once bought out; a completed curve rejects buy/sell and can only migrate | flip `true` to model a graduated curve, `false` to reopen trading | +| `creator` | Coin creator that accrues creator fees via the creator vault | point creator fees at a key you control | +| `token_mint` | Selects which coin's curve (a PDA seed, not a stored field) | choose the target coin | + +### `Global` (singleton, `["global"]`) + +| Field | Meaning | Override it to | +| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `fee_basis_points`, `creator_fee_basis_points` | Legacy flat fees; live trades read the fee program's `FeeConfig` | rarely useful (legacy) | +| `initial_virtual_token_reserves`, `initial_virtual_sol_reserves`, `initial_virtual_quote_reserves`, `initial_real_token_reserves`, `token_total_supply` | Seed values a **new** curve is created with | change the launch parameters of curves created after the override (existing curves keep theirs) | +| `enable_migrate` | Gates the `migrate` instruction | set `true` to let a completed curve migrate to PumpSwap | +| `pool_migration_fee` | Fee charged when a curve migrates | model migration cost | +| `withdraw_authority` | Authority the `migrate` / withdraw path checks | set to a key you control to drive a real `migrate` transaction on a fork | + +## Worked example: reset a curve to a fresh state + +Create it in the Studio editor (Pump tile → _Override Bonding Curve (Custom)_), which +fills the envelope automatically, or POST the full REST `Scenario` shape below to +`/v1/scenarios` — every envelope field is required by the endpoint, and the `account` +recipe is copied verbatim from the template. Then press Play: + +```json +{ + "id": "d2f8a1c4-7b3e-4e9a-8c5d-0f6b2a9e4d71", + "name": "fresh pump curve", + "description": "reset a live coin's curve to launch state", + "tags": ["pump"], + "overrides": [ + { + "id": "curve-reset-0", + "templateId": "pump-bonding-curve-custom", + "label": "reset bonding curve to launch state", + "enabled": true, + "scenarioRelativeSlot": 0, + "fetchBeforeUse": true, + "account": { + "pda": { + "programId": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "seeds": [ + { "string": "bonding-curve" }, + { "propertyRef": "token_mint" } + ] + } + }, + "values": { + "token_mint": "", + "virtual_token_reserves": 1073000000000000, + "virtual_quote_reserves": 30000000000, + "real_token_reserves": 793100000000000, + "real_quote_reserves": 0, + "complete": false + } + } + ] +} +``` + +Expected result: the coin's `BondingCurve` PDA holds exactly these values while every +other byte (token_total_supply, creator, the extend_account tail) stays live. Verify by +re-opening the override in the Studio field editor, or via `getAccountInfo`: u64 LE at +offsets 8 (virtual tokens), 16 (virtual quote), 24 (real tokens), 32 (real quote), bool +at 48 (complete). The surfpool log must contain no `skipping override` line. + +Variant — a semantically valid _completed_ curve (rejects buys/sells with +`BondingCurveComplete`): `complete: true` requires `real_token_reserves: 0`; keep the +curve-lifetime invariants (`virtual − real` = 279.9T tokens / 30 SOL of quote, verified +against live mainnet data). + +## Verification + +- Address identity: `cargo test -p surfpool-core --lib pump` — template PDAs pinned to + externally documented addresses (pump-public-docs), plus byte-exact apply-path tests + over real 151/300-byte mainnet snapshots. +- MCP surface: `cargo test -p surfpool-mcp --lib` — compact template listing, catalog + scoping, pre-HTTP validation errors. +- Behavioral regression: `cargo test -p surfpool-core + test_pump_token2022_graduation_lifecycle -- --ignored --nocapture` prepares the + frozen Token-2022 fixture through the production graduation scenario builder and + materializer, then executes real `buy_v2`, `migrate_v2`, and PumpSwap `sell` + instructions against live mainnet programs. It also compares baseline and + price-shocked sell simulations and requires the quote-token output to change. diff --git a/crates/core/src/scenarios/protocols/pump/v1/idl.json b/crates/core/src/scenarios/protocols/pump/v1/idl.json new file mode 100644 index 000000000..062e66f03 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump/v1/idl.json @@ -0,0 +1,6813 @@ +{ + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "metadata": { + "name": "pump", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "add_quote_mint", + "discriminator": [111, 121, 21, 56, 40, 24, 94, 209], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "quote_mint", + "type": "pubkey" + } + ] + }, + { + "name": "admin_set_creator", + "docs": [ + "Allows Global::admin_set_creator_authority to override the bonding curve creator" + ], + "discriminator": [69, 25, 171, 142, 57, 239, 13, 4], + "accounts": [ + { + "name": "admin_set_creator_authority", + "signer": true, + "relations": ["global"] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "creator", + "type": "pubkey" + } + ] + }, + { + "name": "admin_set_idl_authority", + "discriminator": [8, 217, 96, 231, 144, 104, 192, 5], + "accounts": [ + { + "name": "authority", + "signer": true, + "relations": ["global"] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "idl_account", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "program_signer", + "pda": { + "seeds": [] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "idl_authority", + "type": "pubkey" + } + ] + }, + { + "name": "admin_update_token_incentives", + "discriminator": [209, 11, 115, 87, 213, 23, 124, 204], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "global_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "pump_token_supply_per_day", + "type": "u64" + } + ] + }, + { + "name": "buy", + "docs": ["Buys tokens from a bonding curve."], + "discriminator": [102, 6, 61, 18, 1, 218, 235, 234], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "max_sol_cost", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "buy_exact_quote_in_v2", + "discriminator": [194, 171, 28, 70, 104, 77, 91, 47], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "spendable_quote_in", + "type": "u64" + }, + { + "name": "min_tokens_out", + "type": "u64" + } + ] + }, + { + "name": "buy_exact_sol_in", + "docs": [ + "Given a budget of spendable SOL, buy at least min_tokens_out tokens.", + "Fees are deducted from spendable_sol_in.", + "", + "# Quote formulas", + "Where:", + "- total_fee_bps = protocol_fee_bps + creator_fee_bps (creator_fee_bps is 0 if no creator)", + "- floor(a/b) = a / b (integer division)", + "- ceil(a/b) = (a + b - 1) / b", + "", + "SOL → tokens quote", + "To calculate tokens_out for a given spendable_sol_in:", + "1. net_sol = floor(spendable_sol_in * 10_000 / (10_000 + total_fee_bps))", + "2. fees = ceil(net_sol * protocol_fee_bps / 10_000) + ceil(net_sol * creator_fee_bps / 10_000) (creator_fee_bps is 0 if no creator)", + "3. if net_sol + fees > spendable_sol_in: net_sol = net_sol - (net_sol + fees - spendable_sol_in)", + "4. tokens_out = floor((net_sol - 1) * virtual_token_reserves / (virtual_sol_reserves + net_sol - 1))", + "", + "Reverse quote (tokens → SOL)", + "To calculate spendable_sol_in for a desired number of tokens:", + "1. net_sol = ceil(tokens * virtual_sol_reserves / (virtual_token_reserves - tokens)) + 1", + "2. spendable_sol_in = ceil(net_sol * (10_000 + total_fee_bps) / 10_000)", + "", + "Rent", + "Separately make sure the instruction's payer has enough SOL to cover rent for:", + "- creator_vault: rent.minimum_balance(0)", + "- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)" + ], + "discriminator": [56, 252, 116, 8, 158, 223, 205, 95], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "spendable_sol_in", + "type": "u64" + }, + { + "name": "min_tokens_out", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "buy_v2", + "discriminator": [184, 23, 238, 97, 103, 197, 211, 61], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "max_sol_cost", + "type": "u64" + } + ] + }, + { + "name": "claim_cashback", + "discriminator": [37, 58, 35, 126, 190, 53, 228, 197], + "accounts": [ + { + "name": "user", + "writable": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [] + }, + { + "name": "claim_cashback_v2", + "discriminator": [122, 243, 204, 65, 94, 116, 29, 55], + "accounts": [ + { + "name": "user", + "writable": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "associated_quote_user", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [] + }, + { + "name": "claim_token_incentives", + "discriminator": [16, 4, 71, 28, 204, 1, 40, 27], + "accounts": [ + { + "name": "user" + }, + { + "name": "user_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "mint", + "relations": ["global_volume_accumulator"] + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "payer", + "writable": true, + "signer": true + } + ], + "args": [] + }, + { + "name": "close_user_volume_accumulator", + "discriminator": [249, 69, 164, 218, 150, 103, 84, 138], + "accounts": [ + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "collect_creator_fee", + "docs": [ + "Collects creator_fee from creator_vault to the coin creator account" + ], + "discriminator": [20, 22, 86, 123, 198, 28, 219, 132], + "accounts": [ + { + "name": "creator", + "writable": true + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "creator" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "collect_creator_fee_v2", + "docs": [ + "Collects creator_fee from creator_vault to the coin creator account" + ], + "discriminator": [207, 17, 138, 242, 4, 34, 19, 56], + "accounts": [ + { + "name": "creator", + "writable": true + }, + { + "name": "creator_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "creator" + } + ] + } + }, + { + "name": "creator_vault_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "create", + "docs": ["Creates a new coin and bonding curve."], + "discriminator": [24, 30, 200, 40, 5, 28, 7, 119], + "accounts": [ + { + "name": "mint", + "writable": true, + "signer": true + }, + { + "name": "mint_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, 105, 110, 116, 45, 97, 117, 116, 104, 111, 114, 105, 116, + 121 + ] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, + 235, 121, 172, 28, 180, 133, 237, 95, 91, 55, 145, 58, 140, + 245, 133, 126, 255, 0, 169 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "mpl_token_metadata", + "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" + }, + { + "name": "metadata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [109, 101, 116, 97, 100, 97, 116, 97] + }, + { + "kind": "const", + "value": [ + 11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, + 4, 195, 205, 88, 184, 108, 115, 26, 160, 253, 181, 73, 182, + 209, 188, 3, 248, 41, 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "mpl_token_metadata" + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + }, + { + "name": "create_v2", + "docs": ["Creates a new spl-22 coin and bonding curve."], + "discriminator": [214, 144, 76, 236, 95, 139, 49, 180], + "accounts": [ + { + "name": "mint", + "writable": true, + "signer": true + }, + { + "name": "mint_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, 105, 110, 116, 45, 97, 117, 116, 104, 111, 114, 105, 116, + 121 + ] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "mayhem_program_id", + "writable": true, + "address": "MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e" + }, + { + "name": "global_params", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 45, 112, 97, 114, 97, 109, 115 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 5, 42, 229, 215, 167, 218, 167, 36, 166, 234, 176, 167, 41, 84, + 145, 133, 90, 212, 160, 103, 22, 96, 103, 76, 78, 3, 69, 89, + 128, 61, 101, 163 + ] + } + } + }, + { + "name": "sol_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [115, 111, 108, 45, 118, 97, 117, 108, 116] + } + ], + "program": { + "kind": "const", + "value": [ + 5, 42, 229, 215, 167, 218, 167, 36, 166, 234, 176, 167, 41, 84, + 145, 133, 90, 212, 160, 103, 22, 96, 103, 76, 78, 3, 69, 89, + 128, 61, 101, 163 + ] + } + } + }, + { + "name": "mayhem_state", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, 97, 121, 104, 101, 109, 45, 115, 116, 97, 116, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 5, 42, 229, 215, 167, 218, 167, 36, 166, 234, 176, 167, 41, 84, + 145, 133, 90, 212, 160, 103, 22, 96, 103, 76, 78, 3, 69, 89, + 128, 61, 101, 163 + ] + } + } + }, + { + "name": "mayhem_token_vault", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_enabled", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "distribute_creator_fees", + "docs": [ + "Distributes creator fees to shareholders based on their share percentages", + "The creator vault needs to have at least the minimum distributable amount to distribute fees", + "This can be checked with the get_minimum_distributable_fee instruction" + ], + "discriminator": [165, 114, 103, 0, 121, 206, 247, 81], + "accounts": [ + { + "name": "mint", + "relations": ["sharing_config"] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [], + "returns": { + "defined": { + "name": "DistributeCreatorFeesEvent" + } + } + }, + { + "name": "distribute_creator_fees_v2", + "discriminator": [255, 203, 19, 79, 244, 68, 8, 159], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "mint", + "relations": ["sharing_config"] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "creator_vault_quote_token_account", + "docs": [ + "Deserialized manually in the handler for non-legacy quote mints." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [ + { + "name": "initialize_ata", + "type": "bool" + } + ], + "returns": { + "defined": { + "name": "DistributeCreatorFeesEvent" + } + } + }, + { + "name": "extend_account", + "docs": ["Extends the size of program-owned accounts"], + "discriminator": [234, 102, 194, 203, 150, 72, 62, 229], + "accounts": [ + { + "name": "account", + "writable": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "get_minimum_distributable_fee", + "docs": [ + "Permissionless instruction to check the minimum required fees for distribution", + "Returns the minimum required balance from the creator_vault and whether distribution can proceed" + ], + "discriminator": [117, 225, 127, 202, 134, 95, 68, 35], + "accounts": [ + { + "name": "mint", + "relations": ["sharing_config"] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "creator_vault", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + } + ], + "args": [], + "returns": { + "defined": { + "name": "MinimumDistributableFeeEvent" + } + } + }, + { + "name": "init_user_volume_accumulator", + "discriminator": [94, 6, 202, 115, 255, 96, 232, 183], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "user" + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "initialize", + "docs": ["Creates the global state."], + "discriminator": [175, 175, 109, 31, 13, 152, 155, 237], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "migrate", + "docs": [ + "Migrates liquidity to pump_amm if the bonding curve is complete" + ], + "discriminator": [155, 234, 231, 146, 236, 158, 162, 30], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "withdraw_authority", + "writable": true, + "relations": ["global"] + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "user", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "pump_amm", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [112, 111, 111, 108] + }, + { + "kind": "const", + "value": [0, 0] + }, + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pool_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, 111, 111, 108, 45, 97, 117, 116, 104, 111, 114, 105, 116, + 121 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "pool_authority_mint_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_authority_wsol_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "amm_global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 99, 111, 110, 102, 105, 103 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "wsol_mint", + "address": "So11111111111111111111111111111111111111112" + }, + { + "name": "lp_mint", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, 111, 111, 108, 95, 108, 112, 95, 109, 105, 110, 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "user_pool_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_2022_program" + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_base_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "pump_amm_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "migrate_bonding_curve_creator", + "discriminator": [87, 124, 52, 191, 52, 38, 214, 232], + "accounts": [ + { + "name": "mint", + "relations": ["sharing_config"] + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "migrate_v2", + "docs": [ + "Migrates liquidity to pump_amm if the bonding curve is complete" + ], + "discriminator": [187, 203, 18, 31, 206, 237, 254, 41], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "withdraw_authority", + "writable": true, + "relations": ["global"] + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "user", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "pump_amm", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [112, 111, 111, 108] + }, + { + "kind": "const", + "value": [0, 0] + }, + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "base_mint" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pool_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, 111, 111, 108, 45, 97, 117, 116, 104, 111, 114, 105, 116, + 121 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "pool_authority_mint_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_authority_quote_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "amm_global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 99, 111, 110, 102, 105, 103 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "lp_mint", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, 111, 111, 108, 95, 108, 112, 95, 109, 105, 110, 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "user_pool_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_2022_program" + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_base_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "pump_amm_event_authority" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "remove_quote_mint", + "discriminator": [177, 65, 223, 38, 88, 209, 158, 155], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "quote_mint", + "type": "pubkey" + } + ] + }, + { + "name": "sell", + "docs": [ + "Sells tokens into a bonding curve.", + "For cashback coins, pass as remaining_accounts: [0] user_volume_accumulator,", + "[1] bonding_curve_v2. If provided and valid, creator_fee goes to user_volume_accumulator.", + "Otherwise, falls back to transferring creator_fee to creator_vault." + ], + "discriminator": [51, 230, 133, 164, 1, 127, 131, 173], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "min_sol_output", + "type": "u64" + } + ] + }, + { + "name": "sell_v2", + "discriminator": [93, 246, 130, 60, 231, 233, 64, 178], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, 53, 255, 169, 5, 90, 142, 86, 141, 168, 247, 188, 7, 86, 21, + 39, 76, 241, 201, 44, 164, 31, 64, 0, 156, 81, 106, 164, 20, + 194, 124, 112 + ] + } + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [102, 101, 101, 95, 99, 111, 110, 102, 105, 103] + }, + { + "kind": "const", + "value": [ + 1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, + 91, 170, 81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, + 182, 253, 109, 24, 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "min_sol_output", + "type": "u64" + } + ] + }, + { + "name": "set_creator", + "docs": [ + "Allows Global::set_creator_authority to set the bonding curve creator from Metaplex metadata or input argument" + ], + "discriminator": [254, 148, 255, 112, 207, 142, 170, 165], + "accounts": [ + { + "name": "set_creator_authority", + "signer": true, + "relations": ["global"] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "metadata", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [109, 101, 116, 97, 100, 97, 116, 97] + }, + { + "kind": "const", + "value": [ + 11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, + 4, 195, 205, 88, 184, 108, 115, 26, 160, 253, 181, 73, 182, + 209, 188, 3, 248, 41, 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, 4, + 195, 205, 88, 184, 108, 115, 26, 160, 253, 181, 73, 182, 209, + 188, 3, 248, 41, 70 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "creator", + "type": "pubkey" + } + ] + }, + { + "name": "set_mayhem_virtual_params", + "discriminator": [61, 169, 188, 191, 153, 149, 42, 97], + "accounts": [ + { + "name": "sol_vault_authority", + "writable": true, + "signer": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [115, 111, 108, 45, 118, 97, 117, 108, 116] + } + ], + "program": { + "kind": "const", + "value": [ + 5, 42, 229, 215, 167, 218, 167, 36, 166, 234, 176, 167, 41, 84, + 145, 133, 90, 212, 160, 103, 22, 96, 103, 76, 78, 3, 69, 89, + 128, 61, 101, 163 + ] + } + } + }, + { + "name": "mayhem_token_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "sol_vault_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "mint" + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "set_metaplex_creator", + "docs": [ + "Syncs the bonding curve creator with the Metaplex metadata creator if it exists" + ], + "discriminator": [138, 96, 174, 217, 48, 85, 197, 246], + "accounts": [ + { + "name": "mint" + }, + { + "name": "metadata", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [109, 101, 116, 97, 100, 97, 116, 97] + }, + { + "kind": "const", + "value": [ + 11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, + 4, 195, 205, 88, 184, 108, 115, 26, 160, 253, 181, 73, 182, + 209, 188, 3, 248, 41, 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, 4, + 195, 205, 88, 184, 108, 115, 26, 160, 253, 181, 73, 182, 209, + 188, 3, 248, 41, 70 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "set_params", + "docs": ["Sets the global state parameters."], + "discriminator": [27, 234, 178, 52, 147, 2, 187, 141], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + } + ] + }, + { + "name": "set_reserved_fee_recipients", + "discriminator": [111, 172, 162, 232, 114, 89, 213, 142], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "whitelist_pda", + "type": "pubkey" + } + ] + }, + { + "name": "set_virtual_quote_reserves", + "discriminator": [101, 135, 191, 104, 9, 88, 20, 96], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + } + ] + }, + { + "name": "sync_user_volume_accumulator", + "discriminator": [86, 31, 192, 87, 163, 87, 79, 238], + "accounts": [ + { + "name": "user" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, 108, 111, 98, 97, 108, 95, 118, 111, 108, 117, 109, 101, + 95, 97, 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, 115, 101, 114, 95, 118, 111, 108, 117, 109, 101, 95, 97, + 99, 99, 117, 109, 117, 108, 97, 116, 111, 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "toggle_cashback_enabled", + "discriminator": [115, 103, 224, 255, 189, 89, 86, 195], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_create_v2", + "discriminator": [28, 255, 230, 240, 172, 107, 203, 171], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_mayhem_mode", + "discriminator": [1, 9, 111, 208, 100, 31, 255, 163], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "enabled", + "type": "bool" + } + ] + }, + { + "name": "update_buyback_config", + "discriminator": [251, 224, 171, 146, 160, 26, 113, 233], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": ["global"] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "buyback_basis_points", + "type": { + "option": "u64" + } + } + ] + }, + { + "name": "update_global_authority", + "discriminator": [227, 181, 74, 196, 208, 21, 97, 213], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [103, 108, 111, 98, 97, 108] + } + ] + } + }, + { + "name": "authority", + "signer": true, + "relations": ["global"] + }, + { + "name": "new_authority" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, 95, 101, 118, 101, 110, 116, 95, 97, 117, 116, 104, 111, + 114, 105, 116, 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "BondingCurve", + "discriminator": [23, 183, 248, 55, 96, 216, 172, 96] + }, + { + "name": "FeeConfig", + "discriminator": [143, 52, 146, 187, 219, 123, 76, 155] + }, + { + "name": "Global", + "discriminator": [167, 232, 232, 177, 200, 108, 114, 127] + }, + { + "name": "GlobalVolumeAccumulator", + "discriminator": [202, 42, 246, 43, 142, 190, 30, 255] + }, + { + "name": "SharingConfig", + "discriminator": [216, 74, 9, 0, 56, 140, 93, 75] + }, + { + "name": "UserVolumeAccumulator", + "discriminator": [86, 255, 112, 14, 102, 53, 154, 250] + } + ], + "events": [ + { + "name": "AdminSetCreatorEvent", + "discriminator": [64, 69, 192, 104, 29, 30, 25, 107] + }, + { + "name": "AdminSetIdlAuthorityEvent", + "discriminator": [245, 59, 70, 34, 75, 185, 109, 92] + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "discriminator": [147, 250, 108, 120, 247, 29, 67, 222] + }, + { + "name": "ClaimCashbackEvent", + "discriminator": [226, 214, 246, 33, 7, 242, 147, 229] + }, + { + "name": "ClaimTokenIncentivesEvent", + "discriminator": [79, 172, 246, 49, 205, 91, 206, 232] + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "discriminator": [146, 159, 189, 172, 146, 88, 56, 244] + }, + { + "name": "CollectCreatorFeeEvent", + "discriminator": [122, 2, 127, 1, 14, 191, 12, 175] + }, + { + "name": "CompleteEvent", + "discriminator": [95, 114, 97, 156, 212, 46, 152, 8] + }, + { + "name": "CompletePumpAmmMigrationEvent", + "discriminator": [189, 233, 93, 185, 92, 148, 234, 148] + }, + { + "name": "CreateEvent", + "discriminator": [27, 114, 169, 77, 222, 235, 99, 118] + }, + { + "name": "DistributeCreatorFeesEvent", + "discriminator": [165, 55, 129, 112, 4, 179, 202, 40] + }, + { + "name": "ExtendAccountEvent", + "discriminator": [97, 97, 215, 144, 93, 146, 22, 124] + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "discriminator": [134, 36, 13, 72, 232, 101, 130, 216] + }, + { + "name": "MigrateBondingCurveCreatorEvent", + "discriminator": [155, 167, 104, 220, 213, 108, 243, 3] + }, + { + "name": "MinimumDistributableFeeEvent", + "discriminator": [168, 216, 132, 239, 235, 182, 49, 52] + }, + { + "name": "ReservedFeeRecipientsEvent", + "discriminator": [43, 188, 250, 18, 221, 75, 187, 95] + }, + { + "name": "SetCreatorEvent", + "discriminator": [237, 52, 123, 37, 245, 251, 72, 210] + }, + { + "name": "SetMetaplexCreatorEvent", + "discriminator": [142, 203, 6, 32, 127, 105, 191, 162] + }, + { + "name": "SetParamsEvent", + "discriminator": [223, 195, 159, 246, 62, 48, 143, 131] + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "discriminator": [197, 122, 167, 124, 116, 81, 91, 255] + }, + { + "name": "TradeEvent", + "discriminator": [189, 219, 127, 211, 78, 230, 97, 238] + }, + { + "name": "UpdateGlobalAuthorityEvent", + "discriminator": [182, 195, 137, 42, 35, 206, 207, 247] + }, + { + "name": "UpdateMayhemVirtualParamsEvent", + "discriminator": [117, 123, 228, 182, 161, 168, 220, 214] + } + ], + "errors": [ + { + "code": 6000, + "name": "NotAuthorized", + "msg": "The given account is not authorized to execute this instruction." + }, + { + "code": 6001, + "name": "AlreadyInitialized", + "msg": "The program is already initialized." + }, + { + "code": 6002, + "name": "TooMuchSolRequired", + "msg": "slippage: Too much SOL required to buy the given amount of tokens." + }, + { + "code": 6003, + "name": "TooLittleSolReceived", + "msg": "slippage: Too little SOL received to sell the given amount of tokens." + }, + { + "code": 6004, + "name": "MintDoesNotMatchBondingCurve", + "msg": "The mint does not match the bonding curve." + }, + { + "code": 6005, + "name": "BondingCurveComplete", + "msg": "The bonding curve has completed and liquidity migrated to raydium." + }, + { + "code": 6006, + "name": "BondingCurveNotComplete", + "msg": "The bonding curve has not completed." + }, + { + "code": 6007, + "name": "NotInitialized", + "msg": "The program is not initialized." + }, + { + "code": 6008, + "name": "WithdrawTooFrequent", + "msg": "Withdraw too frequent" + }, + { + "code": 6009, + "name": "NewSizeShouldBeGreaterThanCurrentSize", + "msg": "new_size should be > current_size" + }, + { + "code": 6010, + "name": "AccountTypeNotSupported", + "msg": "Account type not supported" + }, + { + "code": 6011, + "name": "InitialRealTokenReservesShouldBeLessThanTokenTotalSupply", + "msg": "initial_real_token_reserves should be less than token_total_supply" + }, + { + "code": 6012, + "name": "InitialVirtualTokenReservesShouldBeGreaterThanInitialRealTokenReserves", + "msg": "initial_virtual_token_reserves should be greater than initial_real_token_reserves" + }, + { + "code": 6013, + "name": "FeeBasisPointsGreaterThanMaximum", + "msg": "fee_basis_points greater than maximum" + }, + { + "code": 6014, + "name": "AllZerosWithdrawAuthority", + "msg": "Withdraw authority cannot be set to System Program ID" + }, + { + "code": 6015, + "name": "PoolMigrationFeeShouldBeLessThanFinalRealSolReserves", + "msg": "pool_migration_fee should be less than final_real_sol_reserves" + }, + { + "code": 6016, + "name": "PoolMigrationFeeShouldBeGreaterThanCreatorFeePlusMaxMigrateFees", + "msg": "pool_migration_fee should be greater than creator_fee + MAX_MIGRATE_FEES" + }, + { + "code": 6017, + "name": "DisabledWithdraw", + "msg": "Migrate instruction is disabled" + }, + { + "code": 6018, + "name": "DisabledMigrate", + "msg": "Migrate instruction is disabled" + }, + { + "code": 6019, + "name": "InvalidCreator", + "msg": "Invalid creator pubkey" + }, + { + "code": 6020, + "name": "BuyZeroAmount", + "msg": "Buy zero amount" + }, + { + "code": 6021, + "name": "NotEnoughTokensToBuy", + "msg": "Not enough tokens to buy" + }, + { + "code": 6022, + "name": "SellZeroAmount", + "msg": "Sell zero amount" + }, + { + "code": 6023, + "name": "NotEnoughTokensToSell", + "msg": "Not enough tokens to sell" + }, + { + "code": 6024, + "name": "Overflow", + "msg": "Overflow" + }, + { + "code": 6025, + "name": "Truncation", + "msg": "Truncation" + }, + { + "code": 6026, + "name": "DivisionByZero", + "msg": "Division by zero" + }, + { + "code": 6027, + "name": "NotEnoughRemainingAccounts", + "msg": "Not enough remaining accounts" + }, + { + "code": 6028, + "name": "AllFeeRecipientsShouldBeNonZero", + "msg": "All fee recipients should be non-zero" + }, + { + "code": 6029, + "name": "UnsortedNotUniqueFeeRecipients", + "msg": "Unsorted or not unique fee recipients" + }, + { + "code": 6030, + "name": "CreatorShouldNotBeZero", + "msg": "Creator should not be zero" + }, + { + "code": 6031, + "name": "StartTimeInThePast" + }, + { + "code": 6032, + "name": "EndTimeInThePast" + }, + { + "code": 6033, + "name": "EndTimeBeforeStartTime" + }, + { + "code": 6034, + "name": "TimeRangeTooLarge" + }, + { + "code": 6035, + "name": "EndTimeBeforeCurrentDay" + }, + { + "code": 6036, + "name": "SupplyUpdateForFinishedRange" + }, + { + "code": 6037, + "name": "DayIndexAfterEndIndex" + }, + { + "code": 6038, + "name": "DayInActiveRange" + }, + { + "code": 6039, + "name": "InvalidIncentiveMint" + }, + { + "code": 6040, + "name": "BuyNotEnoughSolToCoverRent", + "msg": "Buy: Not enough SOL to cover for rent exemption." + }, + { + "code": 6041, + "name": "BuyNotEnoughSolToCoverFees", + "msg": "Buy: Not enough SOL to cover for fees." + }, + { + "code": 6042, + "name": "BuySlippageBelowMinTokensOut", + "msg": "Slippage: Would buy less tokens than expected min_tokens_out" + }, + { + "code": 6043, + "name": "NameTooLong" + }, + { + "code": 6044, + "name": "SymbolTooLong" + }, + { + "code": 6045, + "name": "UriTooLong" + }, + { + "code": 6046, + "name": "CreateV2Disabled" + }, + { + "code": 6047, + "name": "CpitializeMayhemFailed" + }, + { + "code": 6048, + "name": "MayhemModeDisabled" + }, + { + "code": 6049, + "name": "CreatorMigratedToSharingConfig", + "msg": "creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead" + }, + { + "code": 6050, + "name": "UnableToDistributeCreatorVaultMigratedToSharingConfig", + "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" + }, + { + "code": 6051, + "name": "SharingConfigNotActive", + "msg": "Sharing config is not active" + }, + { + "code": 6052, + "name": "UnableToDistributeCreatorFeesToExecutableRecipient", + "msg": "The recipient account is executable, so it cannot receive lamports, remove it from the team first" + }, + { + "code": 6053, + "name": "BondingCurveAndSharingConfigCreatorMismatch", + "msg": "Bonding curve creator does not match sharing config" + }, + { + "code": 6054, + "name": "ShareholdersAndRemainingAccountsMismatch", + "msg": "Remaining accounts do not match shareholders, make sure to pass exactly the same pubkeys in the same order" + }, + { + "code": 6055, + "name": "InvalidShareBps", + "msg": "Share bps must be greater than 0" + }, + { + "code": 6056, + "name": "CashbackNotEnabled", + "msg": "Cashback is not enabled" + }, + { + "code": 6057, + "name": "BuybackFeeRecipientNotAuthorized", + "msg": "Buyback fee recipient not authorized" + }, + { + "code": 6058, + "name": "AllBuybackFeeRecipientsShouldBeNonZero" + }, + { + "code": 6059, + "name": "NotUniqueBuybackFeeRecipients" + }, + { + "code": 6060, + "name": "BuybackBasisPointsOutOfRange", + "msg": "buyback_basis_points must be <= 10_000" + }, + { + "code": 6061, + "name": "WrongBuybackFeeRecipientsCount", + "msg": "buyback fee recipients require exactly 8 remaining accounts (or none)" + }, + { + "code": 6062, + "name": "BuybackFeeRecipientMissing" + }, + { + "code": 6063, + "name": "UnsupportedQuoteMint", + "msg": "Unsupported quote mint" + }, + { + "code": 6064, + "name": "InvalidQuoteTokenProgram", + "msg": "Create v2: quote token program must be legacy SPL Token" + }, + { + "code": 6065, + "name": "InvalidAssociatedQuoteBondingCurve", + "msg": "Create v2: associated quote bonding curve address does not match derivation" + }, + { + "code": 6066, + "name": "QuoteMintWhitelistFull", + "msg": "Quote mint whitelist is full" + }, + { + "code": 6067, + "name": "QuoteMintAlreadyWhitelisted", + "msg": "Quote mint is already whitelisted" + }, + { + "code": 6068, + "name": "QuoteMintNotWhitelisted", + "msg": "Quote mint is not in the whitelist" + }, + { + "code": 6069, + "name": "QuoteMintNotEligibleForWhitelist", + "msg": "Quote mint cannot be added or removed via whitelist (default or native SOL mint)" + }, + { + "code": 6070, + "name": "UnableToDistributeCreatorFeesToUninitializedAccount", + "msg": "Unable to distribute creator fees to uninitialized account" + }, + { + "code": 6071, + "name": "MayhemModeQuoteMintNotAllowed", + "msg": "Mayhem mode quote mint not allowed" + } + ], + "types": [ + { + "name": "AdminSetCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "old_creator", + "type": "pubkey" + }, + { + "name": "new_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "AdminSetIdlAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "idl_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "token_supply_per_day", + "type": "u64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "BondingCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_quote_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "ClaimCashbackEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed", + "type": "u64" + }, + { + "name": "total_cashback_earned", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + } + ] + } + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "CollectCreatorFeeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "creator_fee", + "type": "u64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "CompleteEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "CompletePumpAmmMigrationEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "mint_amount", + "type": "u64" + }, + { + "name": "sol_amount", + "type": "u64" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "ConfigStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Paused" + }, + { + "name": "Active" + } + ] + } + }, + { + "name": "CreateEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "token_program", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_enabled", + "type": "bool" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + } + ] + } + }, + { + "name": "DistributeCreatorFeesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "distributed", + "type": "u64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "ExtendAccountEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "account", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "current_size", + "type": "u64" + }, + { + "name": "new_size", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "FeeConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + } + ] + } + }, + { + "name": "FeeTier", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market_cap_lamports_threshold", + "type": "u128" + }, + { + "name": "fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "Fees", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lp_fee_bps", + "type": "u64" + }, + { + "name": "protocol_fee_bps", + "type": "u64" + }, + { + "name": "creator_fee_bps", + "type": "u64" + } + ] + } + }, + { + "name": "Global", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "docs": ["Unused"], + "type": "bool" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "docs": ["Unused"], + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "fee_recipients", + "type": { + "array": ["pubkey", 7] + } + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + }, + { + "name": "create_v2_enabled", + "type": "bool" + }, + { + "name": "whitelist_pda", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "mayhem_mode_enabled", + "type": "bool" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": ["pubkey", 7] + } + }, + { + "name": "is_cashback_enabled", + "type": "bool" + }, + { + "name": "buyback_fee_recipients", + "type": { + "array": ["pubkey", 8] + } + }, + { + "name": "buyback_basis_points", + "type": "u64" + }, + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + }, + { + "name": "whitelisted_quote_mints", + "type": { + "array": ["pubkey", 1] + } + } + ] + } + }, + { + "name": "GlobalVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "total_token_supply", + "type": { + "array": ["u64", 30] + } + }, + { + "name": "sol_volumes", + "type": { + "array": ["u64", 30] + } + } + ] + } + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "payer", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "MigrateBondingCurveCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "old_creator", + "type": "pubkey" + }, + { + "name": "new_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "MinimumDistributableFeeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "minimum_required", + "type": "u64" + }, + { + "name": "distributable_fees", + "type": "u64" + }, + { + "name": "can_distribute", + "type": "bool" + } + ] + } + }, + { + "name": "OptionBool", + "type": { + "kind": "struct", + "fields": ["bool"] + } + }, + { + "name": "ReservedFeeRecipientsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": ["pubkey", 7] + } + } + ] + } + }, + { + "name": "SetCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetMetaplexCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "metadata", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetParamsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "final_real_sol_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "fee_recipients", + "type": { + "array": ["pubkey", 8] + } + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "Shareholder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "pubkey" + }, + { + "name": "share_bps", + "type": "u16" + } + ] + } + }, + { + "name": "SharingConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "status", + "type": { + "defined": { + "name": "ConfigStatus" + } + } + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "admin_revoked", + "type": "bool" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + } + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "total_claimed_tokens_before", + "type": "u64" + }, + { + "name": "total_claimed_tokens_after", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "TradeEvent", + "docs": ["ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\""], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "sol_amount", + "type": "u64" + }, + { + "name": "token_amount", + "type": "u64" + }, + { + "name": "is_buy", + "type": "bool" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "fee", + "type": "u64" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "creator_fee", + "type": "u64" + }, + { + "name": "track_volume", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "ix_name", + "type": "string" + }, + { + "name": "mayhem_mode", + "type": "bool" + }, + { + "name": "cashback_fee_basis_points", + "type": "u64" + }, + { + "name": "cashback", + "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "quote_amount", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "real_quote_reserves", + "type": "u64" + } + ] + } + }, + { + "name": "UpdateGlobalAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "new_authority", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "UpdateMayhemVirtualParamsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "new_virtual_token_reserves", + "type": "u64" + }, + { + "name": "new_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + } + ] + } + }, + { + "name": "UserVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "needs_claim", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "has_total_claimed_tokens", + "type": "bool" + }, + { + "name": "cashback_earned", + "type": "u64" + }, + { + "name": "total_cashback_claimed", + "type": "u64" + }, + { + "name": "stable_cashback_earned", + "type": "u64" + }, + { + "name": "total_stable_cashback_claimed", + "type": "u64" + } + ] + } + } + ] +} diff --git a/crates/core/src/scenarios/protocols/pump/v1/overrides.yaml b/crates/core/src/scenarios/protocols/pump/v1/overrides.yaml new file mode 100644 index 000000000..af07697f7 --- /dev/null +++ b/crates/core/src/scenarios/protocols/pump/v1/overrides.yaml @@ -0,0 +1,153 @@ +protocol: Pump +version: v1 +account_type: BondingCurve +idl_file_path: idl.json + +tags: + - amm + - bonding-curve + - launchpad + - defi + +constants: + # Token mints loaded from verified tokens registry + token_mint: + label: Token + description: Select a pump.fun coin mint address from verified tokens + source: verified_tokens + address_suffix: pump + +templates: + - id: pump-bonding-curve-custom + name: Override Bonding Curve (Custom) + description: | + Override the bonding curve of any pump.fun coin by specifying its mint. + The bonding curve address is a PDA derived from ["bonding-curve", mint]. + idl_account_name: BondingCurve + properties: + - path: virtual_token_reserves + label: Virtual Token Reserves + description: Synthetic token reserves driving the price formula (raw units, 6 decimals) + - path: virtual_quote_reserves + label: Virtual Quote Reserves + description: Synthetic quote reserves driving the price formula (lamports for SOL-quoted coins) + - path: real_token_reserves + label: Real Token Reserves + description: Tokens actually held by the curve; the curve completes when this reaches 0 + - path: real_quote_reserves + label: Real Quote Reserves + description: Quote actually held by the curve (lamports for SOL-quoted coins) + - path: complete + label: Complete + description: True once the curve is bought out and ready to migrate to PumpSwap + - path: creator + label: Creator + description: Coin creator that accrues creator fees via the creator vault + - path: token_mint + type: constant_ref + label: Token Mint + constant: token_mint + llm_context: | + Set fetchBeforeUse: true so the fields you don't override keep their live values. + Use false only for a later override that builds on state an earlier one prepared in + the same scenario. + + ONLY pump.fun coins have a bonding curve account. Their mint addresses usually end + with "pump". Selecting any other mint derives an address that holds no account, so + the override has nothing to apply to. + + HOW THE CURVE PRICES (constant product over synthetic reserves, Uniswap V2 style): + - spot price in quote-per-token raw units = virtual_quote_reserves / virtual_token_reserves + - pump.fun coins have 6 decimals; the classic quote is SOL in lamports (9 decimals) + + INVARIANTS THE PROGRAM MAINTAINS (keep them consistent when overriding): + - a buy increases virtual_quote_reserves and real_quote_reserves by the same amount + and decreases virtual_token_reserves and real_token_reserves by the same amount; + a sell does the exact reverse + - therefore virtual_token_reserves - real_token_reserves never changes over the life + of a curve (279,900,000,000,000 with today's mainnet Global defaults), and + virtual_quote_reserves - real_quote_reserves stays at the initial virtual quote + (30,000,000,000 lamports today) + - complete flips to true when real_token_reserves reaches 0; a completed curve + rejects buy and sell and can only be migrated to a PumpSwap pool + + NEW CURVES START FROM Global's initial values: 1,073,000,000,000,000 virtual tokens, + 30,000,000,000 virtual quote lamports, 793,100,000,000,000 real tokens, 0 real quote, + and a 1,000,000,000,000,000 total supply. Override a curve to exactly these (with + complete: false) to reopen an already-migrated coin for trading on its curve. + + total_fee_bps = protocol fee + creator fee (0 when the curve has no creator). + Trades read both from the fee program's FeeConfig market-cap fee tiers — a + required account of every buy and sell. Global.fee_basis_points and + Global.creator_fee_basis_points are legacy fields from before the external fee + program. The creator share accrues to the curve's creator vault. + + EXAMPLE - "prove a completed curve rejects trading": one override, values = + token_mint: + real_token_reserves: 0 + complete: true + A buy against it now fails with BondingCurveComplete (0x1775). + address: + type: pda + program_id: 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P + seeds: + - type: string + value: bonding-curve + - type: property_ref + value: token_mint + + - id: pump-global + name: Override Global Configuration + description: | + Override the Pump program's single Global configuration account + (PDA derived from ["global"], resolving to 4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf). + idl_account_name: Global + properties: + - path: fee_basis_points + label: Fee Basis Points (legacy) + description: Legacy protocol fee in basis points, from before the external fee program; live trades read fees from the fee program's FeeConfig instead + - path: creator_fee_basis_points + label: Creator Fee Basis Points (legacy) + description: Legacy creator fee in basis points, likewise superseded by the external fee program + - path: initial_virtual_token_reserves + label: Initial Virtual Token Reserves + description: Virtual token reserves a newly created curve starts with; parameterizes new curves only, existing curves keep their creation values + - path: initial_virtual_sol_reserves + label: Initial Virtual SOL Reserves + description: Virtual SOL reserves seeding classic SOL-quoted curves at creation + - path: initial_virtual_quote_reserves + label: Initial Virtual Quote Reserves + description: Virtual quote reserves seeding newer non-SOL quote-mint curves at creation + - path: initial_real_token_reserves + label: Initial Real Token Reserves + description: Real token reserves a new curve starts with, the tokens available to buy before it completes + - path: token_total_supply + label: Token Total Supply + description: Total token supply minted for a new curve + - path: enable_migrate + label: Enable Migrate + description: Gates the migrate instruction that moves a completed curve's liquidity to PumpSwap + - path: pool_migration_fee + label: Pool Migration Fee + description: Fee taken when a completed curve migrates to its PumpSwap pool + - path: withdraw_authority + label: Withdraw Authority + description: Authority the migrate and withdraw paths check; override it to a key you control to drive a real migrate transaction on a fork + llm_context: | + Set fetchBeforeUse: true so Global's other fields (authorities, fee recipient lists) + keep their live values; false only to build on an earlier override's prepared state. + + The initial_* values only parameterize bonding curves created AFTER the override; + existing curves keep the values they were created with. initial_virtual_sol_reserves + seeds classic SOL-quoted curves, initial_virtual_quote_reserves the newer non-SOL + quote-mint curves. fee_basis_points and creator_fee_basis_points are legacy + fields from before the external fee program; trades read fees from the fee + program's FeeConfig — a required account of every buy and sell. enable_migrate + gates the migrate instruction that moves a completed curve's liquidity to + PumpSwap. + address: + type: pda + program_id: 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P + seeds: + - type: string + value: global diff --git a/crates/core/src/scenarios/protocols/spl-token/overrides.yaml b/crates/core/src/scenarios/protocols/spl-token/overrides.yaml index d3c7a8fea..53e1a8e16 100644 --- a/crates/core/src/scenarios/protocols/spl-token/overrides.yaml +++ b/crates/core/src/scenarios/protocols/spl-token/overrides.yaml @@ -8,6 +8,13 @@ tags: - vault - balance +constants: + pump_token_mint: + label: Pump Token + description: Select a pump.fun token or enter a validated Token-2022 mint + source: verified_tokens + address_suffix: pump + templates: - id: spl-token-account-balance name: Override Token Account Balance @@ -40,3 +47,37 @@ templates: properties: ["supply"] address: type: pubkey + + - id: pump-token-2022-curve-balance + name: Override Pump Token-2022 Curve Balance + description: Override the Token-2022 base-token vault belonging to a pump.fun bonding curve. + idl_account_name: TokenAccount + write_mode: token_2022_account_amount + properties: + - path: amount + label: Curve Vault Amount + description: Base tokens held by the curve, including the reserve retained for migration + - path: token_mint + type: constant_ref + label: Pump Token Mint + constant: pump_token_mint + llm_context: | + Use this together with pump-bonding-curve-custom when preparing a Token-2022 + pump.fun coin near graduation. The amount must equal the new real_token_reserves + plus the coin's existing migration reserve. Never set it to real_token_reserves alone. + Set fetchBeforeUse: true so Token-2022 extensions remain intact. + address: + type: pda + program_id: ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL + seeds: + - type: derived_pda + program_id: 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P + seeds: + - type: string + value: bonding-curve + - type: property_ref + value: token_mint + - type: pubkey + value: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb + - type: property_ref + value: token_mint diff --git a/crates/core/src/scenarios/pump_graduation.rs b/crates/core/src/scenarios/pump_graduation.rs new file mode 100644 index 000000000..74c929eb2 --- /dev/null +++ b/crates/core/src/scenarios/pump_graduation.rs @@ -0,0 +1,401 @@ +use std::collections::HashMap; + +use solana_account::Account; +use solana_pubkey::Pubkey; +use surfpool_types::{OverrideInstance, Scenario}; + +use super::TemplateRegistry; +use crate::{ + error::{SurfpoolError, SurfpoolResult}, + types::TokenAccount, +}; + +const PUMP_PROGRAM_ID: Pubkey = + Pubkey::from_str_const("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); +const PUMP_AMM_PROGRAM_ID: Pubkey = + Pubkey::from_str_const("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"); +const TOKEN_2022_PROGRAM_ID: Pubkey = + Pubkey::from_str_const("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); +const ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = + Pubkey::from_str_const("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); +const WSOL_MINT: Pubkey = Pubkey::from_str_const("So11111111111111111111111111111111111111112"); +const GLOBAL_ACCOUNT: Pubkey = + Pubkey::from_str_const("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"); + +const VIRTUAL_TOKEN_RESERVES_OFFSET: usize = 8; +const VIRTUAL_QUOTE_RESERVES_OFFSET: usize = 16; +const REAL_TOKEN_RESERVES_OFFSET: usize = 24; +const REAL_QUOTE_RESERVES_OFFSET: usize = 32; +const COMPLETE_OFFSET: usize = 48; +const POOL_MIGRATION_FEE_OFFSET: usize = 146; +const PREPARATION_SLOT: u64 = 1; +const MIGRATION_FEE_BUFFER: u64 = 3; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PumpGraduationAddresses { + pub bonding_curve: Pubkey, + pub curve_vault: Pubkey, + pub canonical_pool: Pubkey, + pub global: Pubkey, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct PumpGraduationPreparation { + pub scenario: Scenario, + pub token_mint: Pubkey, + pub addresses: PumpGraduationAddresses, + pub completing_buy_amount: u64, + pub migration_reserve: u64, +} + +pub fn pump_graduation_addresses(token_mint: &Pubkey) -> PumpGraduationAddresses { + let bonding_curve = + Pubkey::find_program_address(&[b"bonding-curve", token_mint.as_ref()], &PUMP_PROGRAM_ID).0; + let curve_vault = Pubkey::find_program_address( + &[ + bonding_curve.as_ref(), + TOKEN_2022_PROGRAM_ID.as_ref(), + token_mint.as_ref(), + ], + &ASSOCIATED_TOKEN_PROGRAM_ID, + ) + .0; + let pool_authority = + Pubkey::find_program_address(&[b"pool-authority", token_mint.as_ref()], &PUMP_PROGRAM_ID).0; + let canonical_pool = Pubkey::find_program_address( + &[ + b"pool", + &0u16.to_le_bytes(), + pool_authority.as_ref(), + token_mint.as_ref(), + WSOL_MINT.as_ref(), + ], + &PUMP_AMM_PROGRAM_ID, + ) + .0; + + PumpGraduationAddresses { + bonding_curve, + curve_vault, + canonical_pool, + global: GLOBAL_ACCOUNT, + } +} + +pub fn build_pump_graduation_scenario( + token_mint: Pubkey, + mint_account: &Account, + curve_account: &Account, + curve_vault_account: &Account, + canonical_pool_account: Option<&Account>, + global_account: &Account, +) -> SurfpoolResult { + validate_accounts( + token_mint, + mint_account, + curve_account, + curve_vault_account, + canonical_pool_account, + global_account, + )?; + + let virtual_token_reserves = read_u64( + &curve_account.data, + VIRTUAL_TOKEN_RESERVES_OFFSET, + "virtual_token_reserves", + )?; + let virtual_quote_reserves = read_u64( + &curve_account.data, + VIRTUAL_QUOTE_RESERVES_OFFSET, + "virtual_quote_reserves", + )?; + let real_token_reserves = read_u64( + &curve_account.data, + REAL_TOKEN_RESERVES_OFFSET, + "real_token_reserves", + )?; + let real_quote_reserves = read_u64( + &curve_account.data, + REAL_QUOTE_RESERVES_OFFSET, + "real_quote_reserves", + )?; + let token_offset = virtual_token_reserves + .checked_sub(real_token_reserves) + .ok_or_else(|| invalid_curve("virtual token reserves are below real token reserves"))?; + let quote_offset = virtual_quote_reserves + .checked_sub(real_quote_reserves) + .ok_or_else(|| invalid_curve("virtual quote reserves are below real quote reserves"))?; + + let token_account = + TokenAccount::unpack_for_program(&curve_vault_account.data, &curve_vault_account.owner)?; + let migration_reserve = token_account + .amount() + .checked_sub(real_token_reserves) + .ok_or_else(|| invalid_curve("curve vault balance is below real token reserves"))?; + if migration_reserve == 0 { + return Err(invalid_curve("curve vault has no migration reserve")); + } + + let pool_migration_fee = read_u64( + &global_account.data, + POOL_MIGRATION_FEE_OFFSET, + "pool_migration_fee", + )?; + let target_quote = pool_migration_fee + .checked_mul(MIGRATION_FEE_BUFFER) + .ok_or_else(|| invalid_curve("pool migration fee overflows"))?; + let prepared_real_quote_reserves = 1u64; + let prepared_virtual_quote_reserves = quote_offset + .checked_add(prepared_real_quote_reserves) + .ok_or_else(|| invalid_curve("virtual quote reserves overflow"))?; + let completing_buy_amount = div_ceil( + u128::from(target_quote) * u128::from(token_offset), + u128::from(prepared_virtual_quote_reserves), + )?; + let completing_buy_amount = u64::try_from(completing_buy_amount) + .map_err(|_| invalid_curve("completing buy amount does not fit in u64"))?; + if completing_buy_amount == 0 || completing_buy_amount > real_token_reserves { + return Err(invalid_curve( + "curve does not have enough real token reserves for a migration-safe finishing buy", + )); + } + + let prepared_virtual_token_reserves = token_offset + .checked_add(completing_buy_amount) + .ok_or_else(|| invalid_curve("virtual token reserves overflow"))?; + let prepared_vault_amount = migration_reserve + .checked_add(completing_buy_amount) + .ok_or_else(|| invalid_curve("curve vault amount overflow"))?; + let registry = TemplateRegistry::new(); + let curve_template = registry + .get("pump-bonding-curve-custom") + .ok_or_else(|| SurfpoolError::internal("pump bonding curve template is unavailable"))?; + let vault_template = registry + .get("pump-token-2022-curve-balance") + .ok_or_else(|| SurfpoolError::internal("pump Token-2022 vault template is unavailable"))?; + let global_template = registry + .get("pump-global") + .ok_or_else(|| SurfpoolError::internal("pump Global template is unavailable"))?; + let mint = token_mint.to_string(); + + let curve_values = HashMap::from([ + ("token_mint".to_string(), serde_json::json!(mint)), + ( + "virtual_token_reserves".to_string(), + serde_json::json!(prepared_virtual_token_reserves), + ), + ( + "virtual_quote_reserves".to_string(), + serde_json::json!(prepared_virtual_quote_reserves), + ), + ( + "real_token_reserves".to_string(), + serde_json::json!(completing_buy_amount), + ), + ( + "real_quote_reserves".to_string(), + serde_json::json!(prepared_real_quote_reserves), + ), + ("complete".to_string(), serde_json::json!(false)), + ]); + let vault_values = HashMap::from([ + ("token_mint".to_string(), serde_json::json!(mint)), + ( + "amount".to_string(), + serde_json::json!(prepared_vault_amount), + ), + ]); + let global_values = HashMap::from([("enable_migrate".to_string(), serde_json::json!(true))]); + let mut curve_override = OverrideInstance::new( + curve_template.id.clone(), + PREPARATION_SLOT, + curve_template.address.clone(), + ) + .with_values(curve_values) + .with_label("Near-complete bonding curve".to_string()); + curve_override.fetch_before_use = true; + let mut vault_override = OverrideInstance::new( + vault_template.id.clone(), + PREPARATION_SLOT, + vault_template.address.clone(), + ) + .with_values(vault_values) + .with_label("Migration-safe curve vault".to_string()); + vault_override.fetch_before_use = true; + let mut global_override = OverrideInstance::new( + global_template.id.clone(), + PREPARATION_SLOT, + global_template.address.clone(), + ) + .with_values(global_values) + .with_label("Migration enabled".to_string()); + global_override.fetch_before_use = true; + + let mut scenario = Scenario::new( + "Pump Graduation".to_string(), + "Prepare a Token-2022 pump.fun curve for one finishing buy and migration to PumpSwap." + .to_string(), + ); + scenario.tags = vec!["pump".to_string(), "graduation".to_string()]; + scenario.add_override(curve_override); + scenario.add_override(vault_override); + scenario.add_override(global_override); + + Ok(PumpGraduationPreparation { + scenario, + token_mint, + addresses: pump_graduation_addresses(&token_mint), + completing_buy_amount, + migration_reserve, + }) +} + +fn validate_accounts( + token_mint: Pubkey, + mint_account: &Account, + curve_account: &Account, + curve_vault_account: &Account, + canonical_pool_account: Option<&Account>, + global_account: &Account, +) -> SurfpoolResult<()> { + if mint_account.owner != TOKEN_2022_PROGRAM_ID { + return Err(invalid_curve("mint is not owned by Token-2022")); + } + if curve_account.owner != PUMP_PROGRAM_ID { + return Err(invalid_curve("bonding curve is not owned by pump")); + } + if curve_account.data.get(COMPLETE_OFFSET) != Some(&0) { + return Err(invalid_curve("bonding curve is already complete")); + } + if canonical_pool_account.is_some() { + return Err(invalid_curve("canonical PumpSwap pool already exists")); + } + if curve_vault_account.owner != TOKEN_2022_PROGRAM_ID { + return Err(invalid_curve("curve vault is not owned by Token-2022")); + } + let token_account = + TokenAccount::unpack_for_program(&curve_vault_account.data, &curve_vault_account.owner)?; + if token_account.mint() != token_mint { + return Err(invalid_curve("curve vault contains a different mint")); + } + if global_account.owner != PUMP_PROGRAM_ID { + return Err(invalid_curve("pump Global account has the wrong owner")); + } + Ok(()) +} + +fn read_u64(data: &[u8], offset: usize, field: &str) -> SurfpoolResult { + let bytes = data + .get(offset..offset + 8) + .ok_or_else(|| invalid_curve(format!("missing {field}")))?; + Ok(u64::from_le_bytes(bytes.try_into().unwrap())) +} + +fn div_ceil(numerator: u128, denominator: u128) -> SurfpoolResult { + if denominator == 0 { + return Err(invalid_curve("division by zero")); + } + Ok(numerator.div_ceil(denominator)) +} + +fn invalid_curve(message: impl Into) -> SurfpoolError { + SurfpoolError::internal(message.into()) +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use surfpool_types::AccountSnapshot; + + use super::*; + + fn fixture_account( + snapshot: &BTreeMap>, + address: &Pubkey, + ) -> Account { + snapshot[&address.to_string()] + .as_ref() + .unwrap() + .to_account() + .unwrap() + } + + #[test] + fn builds_the_verified_hrt_z_graduation_scenario() { + let snapshot: BTreeMap> = serde_json::from_str( + include_str!("../tests/assets/pump_token2022_graduation.snapshot.json"), + ) + .unwrap(); + let mint = Pubkey::from_str_const("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"); + let addresses = pump_graduation_addresses(&mint); + let preparation = build_pump_graduation_scenario( + mint, + &fixture_account(&snapshot, &mint), + &fixture_account(&snapshot, &addresses.bonding_curve), + &fixture_account(&snapshot, &addresses.curve_vault), + None, + &fixture_account(&snapshot, &addresses.global), + ) + .unwrap(); + + assert_eq!( + preparation.addresses.bonding_curve.to_string(), + "GBpTHrtF8dGwxC7thRD7T6VfGtbVYEabKkQ7k6g3u7QF" + ); + assert_eq!( + preparation.addresses.curve_vault.to_string(), + "9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh" + ); + assert_eq!( + preparation.addresses.canonical_pool.to_string(), + "FFgT2bSo5xrGs5uHyRY7xztL8hntvwuswGM8iYLrdBgx" + ); + assert_eq!(preparation.completing_buy_amount, 216_645_197_009); + assert_eq!(preparation.migration_reserve, 206_900_000_000_000); + assert_eq!(preparation.token_mint, mint); + assert_eq!( + preparation + .scenario + .overrides + .iter() + .map(|item| item.template_id.as_str()) + .collect::>(), + [ + "pump-bonding-curve-custom", + "pump-token-2022-curve-balance", + "pump-global", + ] + ); + assert!( + preparation + .scenario + .overrides + .iter() + .all(|item| item.scenario_relative_slot == PREPARATION_SLOT) + ); + } + + #[test] + fn rejects_a_mint_with_an_existing_pool() { + let snapshot: BTreeMap> = serde_json::from_str( + include_str!("../tests/assets/pump_token2022_graduation.snapshot.json"), + ) + .unwrap(); + let mint = Pubkey::from_str_const("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"); + let addresses = pump_graduation_addresses(&mint); + let pool = Account::default(); + + assert!( + build_pump_graduation_scenario( + mint, + &fixture_account(&snapshot, &mint), + &fixture_account(&snapshot, &addresses.bonding_curve), + &fixture_account(&snapshot, &addresses.curve_vault), + Some(&pool), + &fixture_account(&snapshot, &addresses.global), + ) + .is_err() + ); + } +} diff --git a/crates/core/src/scenarios/pump_swap_price_shock.rs b/crates/core/src/scenarios/pump_swap_price_shock.rs new file mode 100644 index 000000000..804c763c4 --- /dev/null +++ b/crates/core/src/scenarios/pump_swap_price_shock.rs @@ -0,0 +1,159 @@ +use std::collections::HashMap; + +use solana_account::Account; +use solana_pubkey::Pubkey; +use surfpool_types::{OverrideInstance, Scenario}; + +use super::{TemplateRegistry, pump_graduation::pump_graduation_addresses}; +use crate::error::{SurfpoolError, SurfpoolResult}; + +const PUMP_AMM_PROGRAM_ID: Pubkey = + Pubkey::from_str_const("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"); +const WSOL_MINT: Pubkey = Pubkey::from_str_const("So11111111111111111111111111111111111111112"); +const POOL_DISCRIMINATOR: [u8; 8] = [241, 154, 109, 4, 17, 177, 109, 188]; +const BASE_MINT_OFFSET: usize = 43; +const QUOTE_MINT_OFFSET: usize = 75; +const PREPARATION_SLOT: u64 = 1; + +#[derive(Clone, Debug, PartialEq)] +pub struct PumpSwapPriceShockPreparation { + pub scenario: Scenario, + pub token_mint: Pubkey, + pub canonical_pool: Pubkey, + pub virtual_quote_reserves: u64, +} + +pub fn build_pump_swap_price_shock_scenario( + token_mint: Pubkey, + canonical_pool_account: &Account, + virtual_quote_reserves: u64, +) -> SurfpoolResult { + validate_canonical_pool(token_mint, canonical_pool_account, virtual_quote_reserves)?; + + let template_registry = TemplateRegistry::new(); + let template = template_registry + .get("pump-amm-canonical-pool") + .ok_or_else(|| { + SurfpoolError::internal("PumpSwap canonical pool template is unavailable") + })?; + let values = HashMap::from([ + ( + "base_mint".to_string(), + serde_json::json!(token_mint.to_string()), + ), + ( + "virtual_quote_reserves".to_string(), + serde_json::json!(virtual_quote_reserves), + ), + ]); + let mut pool_override = OverrideInstance::new( + template.id.clone(), + PREPARATION_SLOT, + template.address.clone(), + ) + .with_values(values) + .with_label("PumpSwap virtual quote reserve shock".to_string()); + pool_override.fetch_before_use = true; + + let mut scenario = Scenario::new( + "PumpSwap Price Shock".to_string(), + "Shift a canonical PumpSwap pool price through its virtual quote reserves.".to_string(), + ); + scenario.tags = vec!["pumpswap".to_string(), "price-shock".to_string()]; + scenario.add_override(pool_override); + + Ok(PumpSwapPriceShockPreparation { + scenario, + token_mint, + canonical_pool: pump_graduation_addresses(&token_mint).canonical_pool, + virtual_quote_reserves, + }) +} + +fn validate_canonical_pool( + token_mint: Pubkey, + account: &Account, + virtual_quote_reserves: u64, +) -> SurfpoolResult<()> { + if virtual_quote_reserves == 0 { + return Err(invalid_pool( + "virtual quote reserves must be greater than zero", + )); + } + if account.owner != PUMP_AMM_PROGRAM_ID { + return Err(invalid_pool("canonical pool is not owned by PumpSwap")); + } + if account.data.get(..8) != Some(POOL_DISCRIMINATOR.as_slice()) { + return Err(invalid_pool("canonical pool has the wrong discriminator")); + } + if read_pubkey(&account.data, BASE_MINT_OFFSET, "base_mint")? != token_mint { + return Err(invalid_pool( + "canonical pool contains a different base mint", + )); + } + if read_pubkey(&account.data, QUOTE_MINT_OFFSET, "quote_mint")? != WSOL_MINT { + return Err(invalid_pool("canonical pool is not quoted in WSOL")); + } + Ok(()) +} + +fn read_pubkey(data: &[u8], offset: usize, field: &str) -> SurfpoolResult { + let bytes = data + .get(offset..offset + 32) + .ok_or_else(|| invalid_pool(format!("canonical pool is missing {field}")))?; + Pubkey::try_from(bytes) + .map_err(|_| invalid_pool(format!("canonical pool has an invalid {field}"))) +} + +fn invalid_pool(message: impl Into) -> SurfpoolError { + SurfpoolError::internal(message.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn pool_account(base_mint: Pubkey) -> Account { + let mut data = vec![0; 261]; + data[..8].copy_from_slice(&POOL_DISCRIMINATOR); + data[BASE_MINT_OFFSET..BASE_MINT_OFFSET + 32].copy_from_slice(base_mint.as_ref()); + data[QUOTE_MINT_OFFSET..QUOTE_MINT_OFFSET + 32].copy_from_slice(WSOL_MINT.as_ref()); + Account { + data, + owner: PUMP_AMM_PROGRAM_ID, + ..Account::default() + } + } + + #[test] + fn builds_a_canonical_pool_price_shock() { + let mint = Pubkey::from_str_const("7LSsEoJGhLeZzGvDofTdNg7M3JttxQqGWNLo6vWMpump"); + let preparation = + build_pump_swap_price_shock_scenario(mint, &pool_account(mint), 15_000_000_000_000) + .unwrap(); + let pool_override = &preparation.scenario.overrides[0]; + + assert_eq!(pool_override.template_id, "pump-amm-canonical-pool"); + assert_eq!(pool_override.scenario_relative_slot, PREPARATION_SLOT); + assert!(pool_override.fetch_before_use); + assert_eq!( + pool_override.values["virtual_quote_reserves"], + serde_json::json!(15_000_000_000_000u64) + ); + assert_eq!( + pool_override.account.resolve(Some(&pool_override.values)), + Some(preparation.canonical_pool) + ); + } + + #[test] + fn rejects_a_pool_for_a_different_mint() { + let requested_mint = Pubkey::new_unique(); + let stored_mint = Pubkey::new_unique(); + + assert!( + build_pump_swap_price_shock_scenario(requested_mint, &pool_account(stored_mint), 1,) + .is_err() + ); + } +} diff --git a/crates/core/src/scenarios/registry.rs b/crates/core/src/scenarios/registry.rs index 9d69b0eee..979b7ab11 100644 --- a/crates/core/src/scenarios/registry.rs +++ b/crates/core/src/scenarios/registry.rs @@ -32,6 +32,13 @@ pub const WHIRLPOOL_OVERRIDES_CONTENT: &str = include_str!("./protocols/whirlpoo pub const SPL_TOKEN_IDL_CONTENT: &str = include_str!("./protocols/spl-token/idl.json"); pub const SPL_TOKEN_OVERRIDES_CONTENT: &str = include_str!("./protocols/spl-token/overrides.yaml"); +pub const PUMP_V1_IDL_CONTENT: &str = include_str!("./protocols/pump/v1/idl.json"); +pub const PUMP_V1_OVERRIDES_CONTENT: &str = include_str!("./protocols/pump/v1/overrides.yaml"); + +pub const PUMP_AMM_V1_IDL_CONTENT: &str = include_str!("./protocols/pump-amm/v1/idl.json"); +pub const PUMP_AMM_V1_OVERRIDES_CONTENT: &str = + include_str!("./protocols/pump-amm/v1/overrides.yaml"); + /// Registry for managing override templates loaded from YAML files #[derive(Clone, Debug, Default)] pub struct TemplateRegistry { @@ -51,6 +58,7 @@ impl TemplateRegistry { default.load_drift_overrides(); default.load_whirlpool_overrides(); default.load_spl_token_overrides(); + default.load_pump_overrides(); default } @@ -111,6 +119,15 @@ impl TemplateRegistry { ); } + pub fn load_pump_overrides(&mut self) { + self.load_protocol_overrides(PUMP_V1_IDL_CONTENT, PUMP_V1_OVERRIDES_CONTENT, "pump"); + self.load_protocol_overrides( + PUMP_AMM_V1_IDL_CONTENT, + PUMP_AMM_V1_OVERRIDES_CONTENT, + "pump-amm", + ); + } + fn load_protocol_overrides( &mut self, idl_content: &str, @@ -325,15 +342,95 @@ mod tests { ); } + /// Both singleton addresses are documented in pump-public-docs: the Pump Global + /// account at 4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf and the PumpSwap + /// GlobalConfig at ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw. + #[test] + fn pump_singletons_derive_their_documented_addresses() { + let registry = TemplateRegistry::new(); + + let global = registry.get("pump-global").expect("template"); + assert_eq!( + global.address.resolve(None).expect("resolves"), + Pubkey::from_str("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf").expect("address"), + ); + + let config = registry.get("pump-amm-global-config").expect("template"); + assert_eq!( + config.address.resolve(None).expect("resolves"), + Pubkey::from_str("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw").expect("address"), + ); + } + + /// The expected addresses are not ours. pump-public-docs (PUMP_SWAP_README.md) + /// documents the canonical pool GseMAnNDvntR5uFePZ51yZBXzNSn7GdFPkfHwfr6d77J of the + /// migrated coin 7LSsEoJG…pump, with the Pump pool-authority PDA 9XDYTfQK… as its + /// creator, so deriving both pins the whole canonical chain: index 0 as u16 LE, the + /// nested pool-authority PDA, the base mint, and wrapped SOL. The bonding curve + /// address was checked against mainnet on 2026-08-06: owner 6EF8rrec…, discriminator + /// 23 183 248 55 96 216 172 96 (BondingCurve), complete = true. + #[test] + fn pump_templates_derive_the_documented_migrated_coin_accounts() { + let registry = TemplateRegistry::new(); + let mint = "7LSsEoJGhLeZzGvDofTdNg7M3JttxQqGWNLo6vWMpump"; + + let curve = registry.get("pump-bonding-curve-custom").expect("template"); + let values = HashMap::from([( + "token_mint".to_string(), + serde_json::Value::String(mint.to_string()), + )]); + assert_eq!( + curve.address.resolve(Some(&values)).expect("resolves"), + Pubkey::from_str("3MUkKMbuornHohtAtzrToSzqkj1gEEhQqYVz8sZnmQg1").expect("address"), + ); + + let pool = registry.get("pump-amm-canonical-pool").expect("template"); + let values = HashMap::from([( + "base_mint".to_string(), + serde_json::Value::String(mint.to_string()), + )]); + + let AccountAddress::Pda { seeds, .. } = &pool.address else { + panic!("the pool address is a PDA"); + }; + let pool_authority = seeds + .iter() + .find(|seed| matches!(seed, PdaSeed::DerivedPda { .. })) + .expect("the pool PDA derives the pool authority PDA") + .to_bytes(Some(&values)) + .expect("the pool authority resolves"); + assert_eq!( + Pubkey::try_from(pool_authority.as_slice()).expect("32 bytes"), + Pubkey::from_str("9XDYTfQKwW8sHPqnFdUreMmtmffmkHVPGTNV2e3LKxNW").expect("address"), + ); + + assert_eq!( + pool.address.resolve(Some(&values)).expect("resolves"), + Pubkey::from_str("GseMAnNDvntR5uFePZ51yZBXzNSn7GdFPkfHwfr6d77J").expect("address"), + ); + } + + #[test] + fn pump_pool_address_needs_every_seed_to_resolve() { + let registry = TemplateRegistry::new(); + let template = registry.get("pump-amm-canonical-pool").expect("template"); + + assert_eq!( + template.address.resolve(Some(&HashMap::new())), + None, + "a missing base mint must not derive a shorter address" + ); + } + #[test] fn test_registry_loads_all_protocols() { let registry = TemplateRegistry::new(); - // Should have Pyth (1 template) + Jupiter (1) + Raydium CLMM (1) + Raydium AMM v4 (4) + Drift(4) + Meteora (2) + Kamino(3) + Whirlpool(6) + SPL Token (2) = 24 total + // Should have Pyth (1 template) + Jupiter (1) + Raydium CLMM (1) + Raydium AMM v4 (4) + Drift(4) + Meteora (2) + Kamino(3) + Whirlpool(6) + SPL Token (3) + Pump (2) + PumpSwap (3) = 30 total assert_eq!( registry.count(), - 24, - "Registry should load 24 templates total" + 30, + "Registry should load 30 templates total" ); assert!(registry.contains("pyth-price-feed-v2")); @@ -368,6 +465,13 @@ mod tests { assert!(registry.contains("spl-token-account-balance")); assert!(registry.contains("spl-token-mint-supply")); + + assert!(registry.contains("pump-bonding-curve-custom")); + assert!(registry.contains("pump-global")); + + assert!(registry.contains("pump-amm-pool-state")); + assert!(registry.contains("pump-amm-canonical-pool")); + assert!(registry.contains("pump-amm-global-config")); } #[test] @@ -418,6 +522,16 @@ mod tests { 6, "Should have 6 Whirlpool templates" ); + + let pump_templates = registry.by_protocol("Pump"); + assert_eq!(pump_templates.len(), 2, "Should have 2 Pump templates"); + + let pump_swap_templates = registry.by_protocol("PumpSwap"); + assert_eq!( + pump_swap_templates.len(), + 3, + "Should have 3 PumpSwap templates" + ); } #[test] @@ -503,25 +617,26 @@ mod tests { token_mint_constant.options.len() ); - // Check that common tokens are present with correct addresses + // Check that common tokens are present, keyed by their mint address let sol_option = token_mint_constant .options .iter() - .find(|o| o.id == "sol") + .find(|o| o.value == "So11111111111111111111111111111111111111112") .expect("SOL token should be present"); assert_eq!( - sol_option.value, "So11111111111111111111111111111111111111112", - "SOL address should match" + sol_option.id, sol_option.value, + "option ids are mint addresses so colliding symbols keep every mint" ); let usdc_option = token_mint_constant .options .iter() - .find(|o| o.id == "usdc") + .find(|o| o.value == "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") .expect("USDC token should be present"); assert_eq!( - usdc_option.value, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "USDC address should match" + usdc_option.metadata.get("symbol").and_then(|v| v.as_str()), + Some("USDC"), + "USDC symbol should be in metadata" ); // Check metadata is populated diff --git a/crates/core/src/surfnet/locker.rs b/crates/core/src/surfnet/locker.rs index 1606b40bb..71c506cf5 100644 --- a/crates/core/src/surfnet/locker.rs +++ b/crates/core/src/surfnet/locker.rs @@ -4426,7 +4426,7 @@ mod tests { use std::collections::HashMap; use solana_account::Account; - use solana_account_decoder::UiAccountEncoding; + use solana_account_decoder::{UiAccountEncoding, encode_ui_account}; use solana_epoch_schedule::EpochSchedule; use solana_transaction_status::TransactionStatusMeta; @@ -4454,6 +4454,320 @@ mod tests { ] } + /// Real mainnet snapshots taken 2026-08-07. The pump.fun bonding curve + /// GFNxCSS2gzkjvb9gbpQjj3j2K4Ytzm2x9gbuuNTwj1sv (mint 8zRA…pump) is 151 bytes: the + /// Borsh layout ends at byte 115 and the rest is extend_account headroom that a + /// write must not truncate. The canonical PumpSwap pool + /// GseMAnNDvntR5uFePZ51yZBXzNSn7GdFPkfHwfr6d77J (mint 7LSs…pump) is 300 bytes with + /// the layout ending at byte 261. + const PUMP_BONDING_CURVE_SNAPSHOT_B64: &str = "F7f4N2DYrGCi+mz8Tc8DAJdmNf0GAAAAomJasLzQAgCXuhEBAAAAAACAxqR+jQMAACPt5ZthxElG/KKqmHL/o5iqYZ+X4S6Q3ScES4T3rUMKAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; + const PUMP_AMM_POOL_SNAPSHOT_B64: &str = "8ZptBBGxbbz+AAB+mxn3SV4fDSiGJjikd71G0TGcQ4EhIEqv6W1neQXhm14hZ2YvvpBe0kKca5L4CM1XV7nCv//37K3qhtHBlqLvBpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAFTuV8eunFWJDvuXQizncUgufHibf2dZ0qbu/sD21BzZkZIr8sOIx378NbJc46QYgj+BAa5pKnW3I2nB4fGJtIBLSRZQRYEintSF0UaZPk1Mq6QyxwOB8qClHQuTjoeAynhDN6S0AMAAEBSJXvOKKUQdTokJiE6YgocGypswlhuYW526HQhLhuuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + const PUMP_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"; + const PUMP_AMM_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"; + + /// Builds a one-override scenario from a registered pump template, with + /// fetch_before_use enabled to prove it is a harmless no-op without a remote client. + fn pump_override_scenario( + template_id: &str, + values: &HashMap, + ) -> (surfpool_types::Scenario, Pubkey) { + use crate::scenarios::registry::TemplateRegistry; + + let registry = TemplateRegistry::new(); + let template = registry.get(template_id).expect("template exists"); + let address = template.address.clone(); + let pda = address.resolve(Some(values)).expect("address resolves"); + + let mut scenario = surfpool_types::Scenario::new( + "pump validation".to_string(), + "exercises the full register + materialize path".to_string(), + ); + let mut instance = + surfpool_types::OverrideInstance::new(template_id.to_string(), 0, address) + .with_values(values.clone()); + instance.fetch_before_use = true; + scenario.add_override(instance); + (scenario, pda) + } + + #[tokio::test(flavor = "multi_thread")] + async fn materialize_patches_only_the_pump_token_2022_vault_amount() { + let snapshot: std::collections::BTreeMap> = + serde_json::from_str(include_str!( + "../tests/assets/pump_token2022_graduation.snapshot.json" + )) + .unwrap(); + let expected_vault = Pubkey::from_str_const("9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh"); + let base = snapshot[&expected_vault.to_string()] + .as_ref() + .unwrap() + .to_account() + .unwrap(); + let values = HashMap::from([ + ( + "token_mint".to_string(), + serde_json::json!("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"), + ), + ("amount".to_string(), serde_json::json!(216_645_197_009u64)), + ]); + let (scenario, derived_vault) = + pump_override_scenario("pump-token-2022-curve-balance", &values); + assert_eq!(derived_vault, expected_vault); + + let (svm, _events_rx, _geyser_rx) = SurfnetSvm::default(); + let locker = SurfnetSvmLocker::new(svm); + locker.with_svm_writer(|svm_writer| { + svm_writer + .set_account(&derived_vault, base.clone()) + .unwrap(); + }); + locker.register_scenario(scenario, Some(100)).unwrap(); + locker + .materialize_overrides_for_slot(&None, 100) + .await + .unwrap(); + + let after = locker + .with_svm_reader(|svm_reader| svm_reader.get_account(&derived_vault)) + .unwrap() + .unwrap(); + assert_eq!(after.data.len(), 170); + assert_eq!(&after.data[64..72], &216_645_197_009u64.to_le_bytes()); + assert_eq!(&after.data[..64], &base.data[..64]); + assert_eq!(&after.data[72..], &base.data[72..]); + } + + #[tokio::test(flavor = "multi_thread")] + async fn fetch_before_use_materializes_a_remote_token_2022_vault() { + let snapshot: std::collections::BTreeMap> = + serde_json::from_str(include_str!( + "../tests/assets/pump_token2022_graduation.snapshot.json" + )) + .unwrap(); + let mint = Pubkey::from_str_const("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"); + let vault = Pubkey::from_str_const("9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh"); + let mint_account = snapshot[&mint.to_string()] + .as_ref() + .unwrap() + .to_account() + .unwrap(); + let vault_account = snapshot[&vault.to_string()] + .as_ref() + .unwrap() + .to_account() + .unwrap(); + let accounts = Arc::new(HashMap::from([ + (mint.to_string(), (mint, mint_account)), + (vault.to_string(), (vault, vault_account.clone())), + ])); + let mut io = jsonrpc_core::IoHandler::new(); + io.add_sync_method("getAccountInfo", move |params: jsonrpc_core::Params| { + let params: Vec = params.parse()?; + let address = params.first().and_then(serde_json::Value::as_str).unwrap(); + let value = accounts.get(address).map(|(pubkey, account)| { + encode_ui_account(pubkey, account, UiAccountEncoding::Base64, None, None) + }); + Ok(serde_json::json!({ + "context": { "slot": 1 }, + "value": value, + })) + }); + let server = tokio::task::spawn_blocking(move || { + jsonrpc_http_server::ServerBuilder::new(io) + .start_http(&"127.0.0.1:0".parse().unwrap()) + .unwrap() + }) + .await + .unwrap(); + let remote_client = SurfnetRemoteClient::new(format!("http://{}", server.address())); + let remote_result = remote_client + .get_account(&vault, CommitmentConfig::confirmed()) + .await + .unwrap(); + assert!(matches!( + remote_result, + GetAccountResult::FoundTokenAccount(..) + )); + + let values = HashMap::from([ + ( + "token_mint".to_string(), + serde_json::json!(mint.to_string()), + ), + ("amount".to_string(), serde_json::json!(216_645_197_009u64)), + ]); + let (scenario, derived_vault) = + pump_override_scenario("pump-token-2022-curve-balance", &values); + let (svm, _events_rx, _geyser_rx) = SurfnetSvm::default(); + let locker = SurfnetSvmLocker::new(svm); + locker.register_scenario(scenario, Some(100)).unwrap(); + locker + .materialize_overrides_for_slot( + &Some((remote_client, CommitmentConfig::confirmed())), + 100, + ) + .await + .unwrap(); + + let after = locker + .with_svm_reader(|svm_reader| svm_reader.get_account(&derived_vault)) + .unwrap() + .unwrap(); + assert_eq!(after.data.len(), 170); + assert_eq!(&after.data[64..72], &216_645_197_009u64.to_le_bytes()); + assert_eq!(&after.data[..64], &vault_account.data[..64]); + assert_eq!(&after.data[72..], &vault_account.data[72..]); + tokio::task::spawn_blocking(move || server.close()) + .await + .unwrap(); + } + + #[tokio::test(flavor = "multi_thread")] + async fn materialize_applies_a_pump_bonding_curve_override_and_leaves_every_other_byte_alone() { + use base64::{Engine, prelude::BASE64_STANDARD}; + + let (svm, _events_rx, _geyser_rx) = SurfnetSvm::default(); + let locker = SurfnetSvmLocker::new(svm); + + let base = BASE64_STANDARD + .decode(PUMP_BONDING_CURVE_SNAPSHOT_B64) + .unwrap(); + assert_eq!(base.len(), 151, "the snapshot carries the extended tail"); + + // A semantically valid just-completed curve: complete=true requires + // real_token_reserves = 0, and the curve-lifetime invariants hold + // (virtual - real stays at 279.9T tokens / 30 SOL of quote). + let values = HashMap::from([ + ( + "token_mint".to_string(), + serde_json::json!("8zRAv4u7uQfgsjowWrdTTT3XyWzFGwYi2sgJ2CMapump"), + ), + ( + "virtual_token_reserves".to_string(), + serde_json::json!(279_900_000_000_000u64), + ), + ( + "virtual_quote_reserves".to_string(), + serde_json::json!(115_000_000_000u64), + ), + ("real_token_reserves".to_string(), serde_json::json!(0u64)), + ( + "real_quote_reserves".to_string(), + serde_json::json!(85_000_000_000u64), + ), + ("complete".to_string(), serde_json::json!(true)), + ]); + let (scenario, pda) = pump_override_scenario("pump-bonding-curve-custom", &values); + assert_eq!( + pda.to_string(), + "GFNxCSS2gzkjvb9gbpQjj3j2K4Ytzm2x9gbuuNTwj1sv", + "the template derives the documented curve for this mint" + ); + + locker.with_svm_writer(|svm_writer| { + svm_writer + .set_account( + &pda, + Account { + lamports: 43_767_051_506, + data: base.clone(), + owner: Pubkey::from_str_const(PUMP_PROGRAM_ID), + executable: false, + rent_epoch: u64::MAX, + }, + ) + .unwrap(); + }); + + locker.register_scenario(scenario, Some(100)).unwrap(); + locker + .materialize_overrides_for_slot(&None, 100) + .await + .unwrap(); + + let after = locker + .with_svm_reader(|svm_reader| svm_reader.get_account(&pda)) + .unwrap() + .expect("the account is still there"); + + let mut expected = base.clone(); + expected[8..16].copy_from_slice(&279_900_000_000_000u64.to_le_bytes()); + expected[16..24].copy_from_slice(&115_000_000_000u64.to_le_bytes()); + expected[24..32].copy_from_slice(&0u64.to_le_bytes()); + expected[32..40].copy_from_slice(&85_000_000_000u64.to_le_bytes()); + expected[48] = 1; + assert_eq!( + after.data, expected, + "only the overridden fields may change; token_total_supply, creator and the \ + 36-byte extend_account tail must be byte-identical" + ); + } + + #[tokio::test(flavor = "multi_thread")] + async fn materialize_applies_a_pump_amm_pool_override_including_the_i128_field() { + use base64::{Engine, prelude::BASE64_STANDARD}; + + let (svm, _events_rx, _geyser_rx) = SurfnetSvm::default(); + let locker = SurfnetSvmLocker::new(svm); + + let base = BASE64_STANDARD.decode(PUMP_AMM_POOL_SNAPSHOT_B64).unwrap(); + assert_eq!(base.len(), 300, "the snapshot carries the extended tail"); + + let values = HashMap::from([ + ( + "base_mint".to_string(), + serde_json::json!("7LSsEoJGhLeZzGvDofTdNg7M3JttxQqGWNLo6vWMpump"), + ), + ("lp_supply".to_string(), serde_json::json!(9_876_543_210u64)), + ( + "virtual_quote_reserves".to_string(), + serde_json::json!(5_000_000_000i64), + ), + ]); + let (scenario, pda) = pump_override_scenario("pump-amm-canonical-pool", &values); + assert_eq!( + pda.to_string(), + "GseMAnNDvntR5uFePZ51yZBXzNSn7GdFPkfHwfr6d77J", + "the template derives the documented canonical pool for this mint" + ); + + locker.with_svm_writer(|svm_writer| { + svm_writer + .set_account( + &pda, + Account { + lamports: 3_620_640, + data: base.clone(), + owner: Pubkey::from_str_const(PUMP_AMM_PROGRAM_ID), + executable: false, + rent_epoch: u64::MAX, + }, + ) + .unwrap(); + }); + + locker.register_scenario(scenario, Some(100)).unwrap(); + locker + .materialize_overrides_for_slot(&None, 100) + .await + .unwrap(); + + let after = locker + .with_svm_reader(|svm_reader| svm_reader.get_account(&pda)) + .unwrap() + .expect("the account is still there"); + + let mut expected = base.clone(); + expected[203..211].copy_from_slice(&9_876_543_210u64.to_le_bytes()); + expected[245..261].copy_from_slice(&5_000_000_000i128.to_le_bytes()); + assert_eq!( + after.data, expected, + "only lp_supply and virtual_quote_reserves may change; base_mint (a PDA seed \ + reference) and the 39-byte extend_account tail must be byte-identical" + ); + } + #[test] fn test_get_forged_account_data_with_pyth_fixture() { use borsh::{BorshDeserialize, BorshSerialize}; @@ -5427,6 +5741,71 @@ mod tests { assert!(!account.executable); } + #[tokio::test(flavor = "multi_thread")] + async fn test_pump_token2022_graduation_snapshot_loads_with_absent_accounts_offline() { + let snapshot: BTreeMap> = serde_json::from_str( + include_str!("../tests/assets/pump_token2022_graduation.snapshot.json"), + ) + .expect("graduation snapshot should deserialize (camelCase fields)"); + + let (svm, _events_rx, _geyser_rx) = SurfnetSvm::default(); + let locker = SurfnetSvmLocker::new(svm); + locker + .load_snapshot(&snapshot, None, CommitmentConfig::confirmed()) + .await + .unwrap(); + + let mint = Pubkey::from_str_const("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"); + let curve = Pubkey::from_str_const("GBpTHrtF8dGwxC7thRD7T6VfGtbVYEabKkQ7k6g3u7QF"); + let vault = Pubkey::from_str_const("9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh"); + let pump_global = Pubkey::from_str_const("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"); + let amm_global_config = + Pubkey::from_str_const("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw"); + + for (label, pk) in [ + ("mint", mint), + ("curve", curve), + ("vault", vault), + ("pump_global", pump_global), + ("amm_global_config", amm_global_config), + ] { + let account = locker + .with_svm_reader(|svm| svm.get_account(&pk)) + .unwrap() + .unwrap_or_else(|| panic!("{label} should load from the snapshot")); + assert!( + !account.data.is_empty(), + "{label} should carry its real data" + ); + } + + // pinned-empty: read as absent AND offline, so the fork never fetches them from mainnet + let pool = Pubkey::from_str_const("FFgT2bSo5xrGs5uHyRY7xztL8hntvwuswGM8iYLrdBgx"); + let quote_vault = Pubkey::from_str_const("CyugdSkzUoF1srFgCJGuMaAGjCUQ8ys4ca8cqLkWPXFJ"); + let sharing_config = Pubkey::from_str_const("3NFHbr82N29vRbNHewWuuBHcNzdNuSU6zUBJBaRBPqj8"); + let assoc_creator_vault = + Pubkey::from_str_const("C7jfrHkdirzU8F5r1Z1BKacwmwEMgKmLn9Ct3nKpLzmA"); + + for (label, pk) in [ + ("pool", pool), + ("quote_vault", quote_vault), + ("pump_sharing_config", sharing_config), + ("pump_assoc_creator_vault", assoc_creator_vault), + ] { + assert!( + locker + .with_svm_reader(|svm| svm.get_account(&pk)) + .unwrap() + .is_none(), + "the pinned-empty {label} should read as absent" + ); + assert!( + locker.is_account_offline(&pk), + "the pinned-empty {label} must be recorded offline so it is never fetched from mainnet" + ); + } + } + #[tokio::test(flavor = "multi_thread")] async fn test_load_snapshot_multiple_accounts() { use base64::{Engine, engine::general_purpose}; diff --git a/crates/core/src/surfnet/mod.rs b/crates/core/src/surfnet/mod.rs index 55fc53204..245bcb95d 100644 --- a/crates/core/src/surfnet/mod.rs +++ b/crates/core/src/surfnet/mod.rs @@ -238,6 +238,15 @@ pub enum GetAccountResult { } impl GetAccountResult { + pub fn account(&self) -> Option<&Account> { + match self { + Self::None(_) => None, + Self::FoundAccount(_, account, _) + | Self::FoundProgramAccount((_, account), _) + | Self::FoundTokenAccount((_, account), _) => Some(account), + } + } + pub fn expected_data(&self) -> &Vec { match &self { Self::None(_) => unreachable!(), diff --git a/crates/core/src/surfnet/svm.rs b/crates/core/src/surfnet/svm.rs index b48d8a685..850f8441a 100644 --- a/crates/core/src/surfnet/svm.rs +++ b/crates/core/src/surfnet/svm.rs @@ -56,7 +56,7 @@ use spl_token_2022_interface::extension::{ use surfpool_types::{ AccountChange, AccountProfileState, AccountSnapshot, DEFAULT_PROFILING_MAP_CAPACITY, DEFAULT_SLOT_TIME_MS, ExportSnapshotConfig, ExportSnapshotScope, FifoMap, Idl, - OverrideInstance, ProfileResult, RpcProfileDepth, RpcProfileResultConfig, + OverrideInstance, OverrideWriteMode, ProfileResult, RpcProfileDepth, RpcProfileResultConfig, RunbookExecutionStatusReport, SimnetEvent, SimnetEventsTx, StartupError, SurfnetStartupStatus, SurfnetStartupTask, SvmFeatureConfig, TransactionConfirmationStatus, TransactionStatusEvent, UiAccountChange, UiAccountProfileState, UiProfileResult, VersionedIdl, @@ -117,6 +117,51 @@ lazy_static::lazy_static! { }; } +fn forge_token_2022_amount( + account_pubkey: &Pubkey, + account: &Account, + values: &HashMap, + account_values: &HashMap, +) -> SurfpoolResult> { + if account.owner != spl_token_2022_interface::id() { + return Err(SurfpoolError::invalid_account_data( + account_pubkey, + "Expected a Token-2022 account", + Some("owner is not the Token-2022 program"), + )); + } + if account_values.len() != 1 || !account_values.contains_key("amount") { + return Err(SurfpoolError::internal( + "Token-2022 amount overrides accept only the amount field", + )); + } + + let expected_mint = values + .get("token_mint") + .and_then(serde_json::Value::as_str) + .and_then(|mint| Pubkey::from_str(mint).ok()) + .ok_or_else(|| SurfpoolError::internal("token_mint must be a valid pubkey"))?; + let amount = account_values["amount"] + .as_u64() + .or_else(|| { + account_values["amount"] + .as_str() + .and_then(|amount| amount.parse().ok()) + }) + .ok_or_else(|| SurfpoolError::internal("amount must be an unsigned 64-bit integer"))?; + + let mut token_account = TokenAccount::unpack_for_program(&account.data, &account.owner)?; + if token_account.mint() != expected_mint { + return Err(SurfpoolError::invalid_account_data( + account_pubkey, + "Token account mint does not match token_mint", + Some("derived vault contains a different mint"), + )); + } + token_account.set_amount(amount); + token_account.patch_amount_preserving_extensions(&account.data) +} + /// Helper function to apply an override to a decoded account value using dot notation pub fn apply_override_to_decoded_account( decoded_value: &mut Value, @@ -2587,6 +2632,8 @@ impl SurfnetSvm { target_slot ); + let template_registry = TemplateRegistry::new(); + for override_instance in overrides { if !override_instance.enabled { debug!("Skipping disabled override: {}", override_instance.id); @@ -2636,28 +2683,29 @@ impl SurfnetSvm { .get_account(&account_pubkey, CommitmentConfig::confirmed()) .await { - Ok(GetAccountResult::FoundAccount(_pubkey, remote_account, _)) => { - debug!( - "Fetched account {} from remote: {} lamports, {} bytes", - account_pubkey, - remote_account.lamports(), - remote_account.data().len() - ); - - // Set the fresh account data in the SVM - if let Err(e) = self.inner.set_account(account_pubkey, remote_account) { - warn!( - "Failed to set account {} from remote: {}", - account_pubkey, e + Ok(result) => match result.account().cloned() { + Some(remote_account) => { + debug!( + "Fetched account {} from remote: {} lamports, {} bytes", + account_pubkey, + remote_account.lamports(), + remote_account.data().len() ); + + // Set the fresh account data in the SVM + if let Err(e) = + self.inner.set_account(account_pubkey, remote_account) + { + warn!( + "Failed to set account {} from remote: {}", + account_pubkey, e + ); + } } - } - Ok(GetAccountResult::None(_)) => { - debug!("Account {} not found on remote", account_pubkey); - } - Ok(_) => { - debug!("Account {} fetched (other variant)", account_pubkey); - } + None => { + debug!("Account {} not found on remote", account_pubkey); + } + }, Err(e) => { warn!( "Failed to fetch account {} from remote: {}", @@ -2709,6 +2757,29 @@ impl SurfnetSvm { continue; }; + let write_mode = template_registry + .get(&override_instance.template_id) + .map(|template| template.write_mode) + .unwrap_or_default(); + + if write_mode == OverrideWriteMode::Token2022AccountAmount { + let new_account_data = forge_token_2022_amount( + &account_pubkey, + &account, + &override_instance.values, + &account_values, + )?; + let modified_account = Account { + lamports: account.lamports(), + data: new_account_data, + owner: *account.owner(), + executable: account.executable(), + rent_epoch: account.rent_epoch(), + }; + self.inner.set_account(account_pubkey, modified_account)?; + continue; + } + // Get the account owner (program ID) let owner_program_id = account.owner(); @@ -4130,6 +4201,45 @@ mod tests { assert!(!startup.has_changed().unwrap()); } + fn pump_token_2022_vault_fixture() -> (Pubkey, Account) { + let snapshot: BTreeMap> = serde_json::from_str( + include_str!("../tests/assets/pump_token2022_graduation.snapshot.json"), + ) + .unwrap(); + let vault = Pubkey::from_str_const("9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh"); + let account = snapshot[&vault.to_string()] + .as_ref() + .unwrap() + .to_account() + .unwrap(); + (vault, account) + } + + #[test] + fn token_2022_amount_override_rejects_the_wrong_owner() { + let (vault, mut account) = pump_token_2022_vault_fixture(); + account.owner = spl_token_interface::id(); + let values = HashMap::from([( + "token_mint".to_string(), + serde_json::json!("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"), + )]); + let account_values = HashMap::from([("amount".to_string(), serde_json::json!(1u64))]); + + assert!(forge_token_2022_amount(&vault, &account, &values, &account_values).is_err()); + } + + #[test] + fn token_2022_amount_override_rejects_a_different_mint() { + let (vault, account) = pump_token_2022_vault_fixture(); + let values = HashMap::from([( + "token_mint".to_string(), + serde_json::json!(Pubkey::new_unique().to_string()), + )]); + let account_values = HashMap::from([("amount".to_string(), serde_json::json!(1u64))]); + + assert!(forge_token_2022_amount(&vault, &account, &values, &account_values).is_err()); + } + fn build_transfer_transaction( payer: &Keypair, recipient: &Pubkey, diff --git a/crates/core/src/tests/assets/pump_token2022_graduation.snapshot.json b/crates/core/src/tests/assets/pump_token2022_graduation.snapshot.json new file mode 100644 index 000000000..7ddf4725c --- /dev/null +++ b/crates/core/src/tests/assets/pump_token2022_graduation.snapshot.json @@ -0,0 +1,170 @@ +{ + "HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump": { + "lamports": 3681840, + "owner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNSf0aBwAGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP/cmq86SqUJgZ604EVWKJspLtV52pDYF1dWu4PIqKPEwCjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/9yarzpKpQmBnrTgRVYomyku1XnakNgXV1a7g8ioo8IAAAAQXN0ZXJvaWQIAAAAQXN0ZXJvaWRDAAAAaHR0cHM6Ly9pcGZzLmlvL2lwZnMvUW1laHZyUHo3aVVKN3RTZk5lajRmdW4zeXVZZmR6Unl1UUVEMkFZUGd0UWU3YQAAAAA=", + "parsedData": null + }, + "GBpTHrtF8dGwxC7thRD7T6VfGtbVYEabKkQ7k6g3u7QF": { + "lamports": 1691281, + "owner": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "F7f4N2DYrGB7C2+bCM4DANtSWIkNAAAAe3NcT3fPAgABAAAAAAAAAACAxqR+jQMAAMlOj+PnSojUX3T46Z6szlfdiXPMTI64ebzBSYzGJK0YAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "parsedData": null + }, + "9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh": { + "lamports": 2074080, + "owner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "8/9yarzpKpQmBnrTgRVYomyku1XnakNgXV1a7g8ioo/hpMSAwBpGz2NzrEmOA5nyAvtJlU4J8HEytaNEjfGkfHt7XfijiwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgcAAAA=", + "parsedData": null + }, + "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf": { + "lamports": 4089612038, + "owner": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "p+joschscn8B07uMqzQc4FKEV/LDgX0yeEQZY9zVX+1YuiTJmd2sAqpKwvjQ3Vy8l+MonBl8tQYqVPPZVrnOblEV+WVnqlyz5gAQ2EfjzwMAAKwj/AYAAAAAeMX7UdECAACAxqR+jQMAXwAAAAAAAAAf6nQ58860xO9Lucx77kChpiYXG2hBX+3tQLeolW+E5wHB4eQAAAAAAAUAAAAAAAAAYIzMHfzpYbQ7d5wZFQWm4tO/RdWk20YYrXbILWF1RTVjg3MADqIssmTTSv9koEte+r+7dN3NBImXsZgVR9fREIOEdCkuZ1qUtDbssKmYiUIyioPdxiM4ApYSZ8XNYRfLjRgaDISfqTem80re0wge+VcAqssMm7PZCaS5FHUnpOutEeak/ClEpPqCUb74FUJuG/soxrZkZndgfGrZ9WamRteqj7Bg2CkbTE1HXa/3Yslr3A2s6zbAEurRLtOpSEFh4ATIfOuY+lzkf4A4Bv0seUXSlSSVmuwA3tl4FPOPeEYf6nQ58860xO9Lucx77kChpiYXG2hBX+3tQLeolW+E5wchXZlAeTaU4RYGbORZuBj9+bugx7QbeD+joSDKQZUyAaKLX9JqtHmmqcxsv2sLI+thiFo3HgEgrKkTvu89E4p46JMUH7GOnxV02BDheOGeMGBOMXWqLkoy38hgByfRBwkBNYRTYlYJT5EoGRJ++k5Ea0MzcheT0Th2+arb89x9C19udQGCIPlCZ3ADI3tNa0U3WbSlxpC1nDXZuxh6CQy9KjOYep67E2eZq1mSWxPl3Iswgd8AXbQnwUePpG/4w0egdOlUPz43otBGInrdy06cd0xEJYxD7fJKqKrh8AIUZlvaTDjNbbdDj1m0CLuew7TKnorR8fJGU8SZtXlsINv5sy3dnuo/ObNyEVxxhHwYRc+lNsaFB04DDkTQId4++eNcTLeA8I7i/uhL7ERqV3gl2mjUOfqKXaOwxc/1D2P0VGsBQ55lEMA9ZfrZMeidBL4Ltw1Rlx9RxBX7NEwH20GfISICI1UWqRcTTGdYjEk4IK4VXulmZVd6wbcY2kfdzyoFDuan4iBou4hkCqV/kJMIxh/vcRoBY/WnVcBwvIYNH2NnIHzs2lvMbLHq8PFtaEBFZrGNVtJIGssxcDJlbpBVHHhElkH4SVjcc6dqhdh1b1XALNrKiboZMnkMNoqxV+ktc8VLlrXJMZQeRupL4uDjESd0T8a3TPtFXv6vi9VxeSztRPwfePlKM9CQnF5rX7AhVwrY262N6P2z0g7RzZnrjk6HcBV+6+tnimVduZs39rEybHZX25DPuKh6vvjHtvLIaYgTAAAAAAAAALnS/wAAAADG+nrzvtutOj1l82qryXQxsbvkwtL24OR8pgIDRS9dYQ==", + "parsedData": null + }, + "8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt": { + "lamports": 37290293, + "owner": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "jzSSu9t7TJv907uMqzQc4FKEV/LDgX0yeEQZY9zVX+1YuiTJmd2sAqoAAAAAAAAAAF8AAAAAAAAAHgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXwAAAAAAAAAeAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "parsedData": null + }, + "3NFHbr82N29vRbNHewWuuBHcNzdNuSU6zUBJBaRBPqj8": { + "lamports": 0, + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 0, + "data": "", + "parsedData": null + }, + "Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y": { + "lamports": 17016138, + "owner": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "yir2K46+Hv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "parsedData": null + }, + "6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA": { + "lamports": 13741536777, + "owner": "MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "lPrRirnqw+P/B3+3e2oAAAAAMvPF4BoEAADdkr3OAAAAAIG3e2oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "parsedData": null + }, + "ghSBUgyxyvyurm1vJBkU4rUyLJoUipCZhFeiBogKCSy": { + "lamports": 684496646, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAFMt4DwjuL+6EvsRGpXeCXaaNQ5+opdo7DFz/UPY/RUaxZ5rSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "parsedData": null + }, + "GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL": { + "lamports": 2952536341, + "owner": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "maZHkLO9ifs7X0HMsWD08BTTVENuLWdcsy2NAUMT8U/o+iLg/e8/uNlKiY2vKgAANjdcxHURAACW9azNJQAAAFkifGoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "parsedData": null + }, + "AktftA98kSWAxn6kVSoqBXBELUArjKu2H9WmKB48ULFY": { + "lamports": 1550236538, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAHmp+IgaLuIZAqlf5CTCMYf73EaAWP1p1XAcLyGDR9jZ4qdR1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "parsedData": null + }, + "7rnCZqrwmd4L1ZcX7VaKvYZ9n2BjgxmoysrmGhweW97C": { + "lamports": 9199618, + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "", + "parsedData": null + }, + "C7jfrHkdirzU8F5r1Z1BKacwmwEMgKmLn9Ct3nKpLzmA": { + "lamports": 0, + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 0, + "data": "", + "parsedData": null + }, + "ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw": { + "lamports": 9215825, + "owner": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "lQicyqD8sNnTu4yrNBzgUoRX8sOBfTJ4RBlj3NVf7Vi6JMmZ3awCqhQAAAAAAAAABQAAAAAAAAAASsL40N1cvJfjKJwZfLUGKlTz2Va5zm5RFfllZ6pcs+ZgjMwd/OlhtDt3nBkVBabi079F1aTbRhitdsgtYXVFNWODcwAOoiyyZNNK/2SgS176v7t03c0EiZexmBVH19EQg4R0KS5nWpS0NuywqZiJQjKKg93GIzgClhJnxc1hF8uNGBoMhJ+pN6bzSt7TCB75VwCqywybs9kJpLkUdSek69eqj7Bg2CkbTE1HXa/3Yslr3A2s6zbAEurRLtOpSEFh4ATIfOuY+lzkf4A4Bv0seUXSlSSVmuwA3tl4FPOPeEb/g4OBi6j6KMPNO21ek/n6uPCXm8NyFazFskaHe6jDyQUAAAAAAAAAByFdmUB5NpThFgZs5Fm4GP35u6DHtBt4P6OhIMpBlTKii1/SarR5pqnMbL9rCyPrYYhaNx4BIKypE77vPROKeOiTFB+xjp8VdNgQ4XjhnjBgTjF1qi5KMt/IYAcn0QcJATWEU2JWCU+RKBkSfvpORGtDM3IXk9E4dvmq2/PcfQtfbnUBgiD5QmdwAyN7TWtFN1m0pcaQtZw12bsYegkMvSozmHqeuxNnmatZklsT5dyLMIHfAF20J8FHj6Rv+MNHoHTpVD8+N6LQRiJ63ctOnHdMRCWMQ+3ySqiq4fACFGZb2kw4zW23Q49ZtAi7nsO0yp6K0fHyRlPEmbV5bCDb+bMt3Z7qPzmzchFccYR8GEXPpTbGhQdOAw5E0CHePvnjXEy3gPCO4v7oS+xEald4Jdpo1Dn6il2jsMXP9Q9j9FRrAUOeZRDAPWX62THonQS+C7cNUZcfUcQV+zRMB9tBnyEiAiNVFqkXE0xnWIxJOCCuFV7pZmVXesG3GNpH3c8qBQ7mp+IgaLuIZAqlf5CTCMYf73EaAWP1p1XAcLyGDR9jZyB87NpbzGyx6vDxbWhARWaxjVbSSBrLMXAyZW6QVRx4RJZB+ElY3HOnaoXYdW9VwCzayom6GTJ5DDaKsVfpLXPFS5a1yTGUHkbqS+Lg4xEndE/Gt0z7RV7+r4vVcXks7UT8H3j5SjPQkJxea1+wIVcK2Nutjej9s9IO0c2Z645Oh3AVfuvrZ4plXbmbN/axMmx2V9uQz7ioer74x7byyGmIEwAAAAAAAPSEYofB0vyr1vGBX2l/VABW/RZVaGEbANPK6XSCwSxlAQ==", + "parsedData": null + }, + "5PHirr8joyTMp9JMm6nW7hNDVyEYdkzDqazxPD7RaTjx": { + "lamports": 33103977, + "owner": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "jzSSu9t7TJv/07uMqzQc4FKEV/LDgX0yeEQZY9zVX+1YuiTJmd2sAqoZAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAXQAAAAAAAAAeAAAAAAAAAABo88lhAAAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAXwAAAAAAAAAA7NNCVgEAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAFoAAAAAAAAAABhtwzwCAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAABVAAAAAAAAAABg+u8gAwAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAUAAAAAAAAAAAqIccBQQAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAEsAAAAAAAAAAJicZe4IAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAABGAAAAAAAAAADIduxnDQAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAQQAAAAAAAAAAFEUf3xEAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAADwAAAAAAAAAAGATUlYWAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAA3AAAAAAAAAACs4YTNGgAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAMgAAAAAAAAAA+K+3RB8AAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAC0AAAAAAAAAACiKPr4jAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAoAAAAAAAAAAB0WHE1KAAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAIwAAAAAAAAAAwCakrCwAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAB4AAAAAAAAAAAz11iMxAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAcAAAAAAAAAABYwwmbNQAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAGQAAAAAAAAAAiJ2QFDoAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAABcAAAAAAAAAANRrw4s+AAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAUAAAAAAAAAADq1DEDQwAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAEgAAAAAAAAAAbAgpekcAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAA8AAAAAAAAAALjWW/FLAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAANAAAAAAAAAAAgmTpmUAAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAACgAAAAAAAAAANH8V4lQAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAAgAAAAAAAAAAIBNSFlZAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAFAAAAAAAAABkAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABdAAAAAAAAAB4AAAAAAAAAAI6svA0AAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAABfAAAAAAAAAAC4ZNlFAAAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAWgAAAAAAAAAAiFJqdAAAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAFUAAAAAAAAAAFhA+6IAAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAABQAAAAAAAAAAAoLozRAAAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAASwAAAAAAAAAAIEqp0QEAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAEYAAAAAAAAAADDvfboCAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAABBAAAAAAAAAABAlFKjAwAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAPAAAAAAAAAAAUDknjAQAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAADcAAAAAAAAAAGDe+3QFAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAyAAAAAAAAAABwg9BdBgAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAALQAAAAAAAAAAgCilRgcAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAACgAAAAAAAAAAJDNeS8IAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAjAAAAAAAAAACgck4YCQAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAHgAAAAAAAAAAsBcjAQoAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAABwAAAAAAAAAAMC89+kKAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAZAAAAAAAAAADQYczSCwAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAAFwAAAAAAAAAA4AahuwwAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAABQAAAAAAAAAAPCrdaQNAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAASAAAAAAAAAAAAUUqNDgAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAADwAAAAAAAAAAEPYedg8AAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAA0AAAAAAAAAACCb814QAAAAAAAAAAAAABQAAAAAAAAABQAAAAAAAAAKAAAAAAAAAAAwQMhHEQAAAAAAAAAAAAAUAAAAAAAAAAUAAAAAAAAACAAAAAAAAAAAQOWcMBIAAAAAAAAAAAAAFAAAAAAAAAAFAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "parsedData": null + }, + "GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS": { + "lamports": 34781143371, + "owner": "MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "lPrRirnqw+P/AH+3e2oAAAAAIOlUoaUhAAB74IIzAgAAAIG3e2oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "parsedData": null + }, + "C93K8DX4YsABYJtHX9awzgZW3LWzBqBVezEbbLJH4yet": { + "lamports": 2074605366, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAHokxQfsY6fFXTYEOF44Z4wYE4xdaouSjLfyGAHJ9EHCUTZiHsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "parsedData": null + }, + "EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL": { + "lamports": 3461067437, + "owner": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "maZHkLO9ifs7X0HMsWD08BTTVENuLWdcsy2NAUMT8U/o+iLg/e8/uLuSqAaGKgAAsCzFzbsYAACow2jHKgAAAFkifGoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "parsedData": null + }, + "CA7v8gHfbquYXyDnDx6QxWW8hmL1H7X6Y2RYDrGLnuck": { + "lamports": 1867105863, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "data": "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAHFS5a1yTGUHkbqS+Lg4xEndE/Gt0z7RV7+r4vVcXks7VeoKm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "parsedData": null + }, + "FFgT2bSo5xrGs5uHyRY7xztL8hntvwuswGM8iYLrdBgx": { + "lamports": 0, + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 0, + "data": "", + "parsedData": null + }, + "CyugdSkzUoF1srFgCJGuMaAGjCUQ8ys4ca8cqLkWPXFJ": { + "lamports": 0, + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 0, + "data": "", + "parsedData": null + } +} diff --git a/crates/core/src/tests/integration.rs b/crates/core/src/tests/integration.rs index 03247694c..717730ec6 100644 --- a/crates/core/src/tests/integration.rs +++ b/crates/core/src/tests/integration.rs @@ -21,7 +21,10 @@ use solana_address_lookup_table_interface::{ }; use solana_client::{ nonblocking::rpc_client::RpcClient, - rpc_config::{RpcSendTransactionConfig, RpcSimulateTransactionConfig}, + rpc_config::{ + RpcSendTransactionConfig, RpcSimulateTransactionAccountsConfig, + RpcSimulateTransactionConfig, + }, rpc_response::RpcLogsResponse, }; use solana_clock::{Clock, Slot}; @@ -69,6 +72,9 @@ use surfpool_types::{ use test_case::test_case; use tokio::{sync::RwLock, task}; use uuid::Uuid; + +mod pump_token2022_graduation; + pub const LAMPORTS_PER_SOL: u64 = 1_000_000_000; use crate::{ diff --git a/crates/core/src/tests/integration/pump_token2022_graduation.rs b/crates/core/src/tests/integration/pump_token2022_graduation.rs new file mode 100644 index 000000000..c7e2e880d --- /dev/null +++ b/crates/core/src/tests/integration/pump_token2022_graduation.rs @@ -0,0 +1,582 @@ +use super::*; +use crate::scenarios::{ + pump_graduation::build_pump_graduation_scenario, + pump_swap_price_shock::build_pump_swap_price_shock_scenario, +}; + +const PUMP: Pubkey = Pubkey::from_str_const("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); +const PAMM: Pubkey = Pubkey::from_str_const("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"); +const FEE_PROGRAM: Pubkey = Pubkey::from_str_const("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"); +const TOKEN_2022: Pubkey = Pubkey::from_str_const("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); +const TOKENKEG: Pubkey = Pubkey::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +const WSOL: Pubkey = Pubkey::from_str_const("So11111111111111111111111111111111111111112"); +const ATA_PROGRAM: Pubkey = Pubkey::from_str_const("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); +const SYSTEM_PROGRAM: Pubkey = Pubkey::from_str_const("11111111111111111111111111111111"); +const RENT_SYSVAR: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111"); + +const MINT: Pubkey = Pubkey::from_str_const("HRTzNRJNnY78xe8e4a9DuMotw6qA97GwSQLzpVw9pump"); +const CURVE: Pubkey = Pubkey::from_str_const("GBpTHrtF8dGwxC7thRD7T6VfGtbVYEabKkQ7k6g3u7QF"); +const BASE_VAULT: Pubkey = Pubkey::from_str_const("9sXf9hAtryY1mncMxKGZnLMJzQbnTsUoSu8GJTX3FpFh"); +const QUOTE_VAULT: Pubkey = Pubkey::from_str_const("CyugdSkzUoF1srFgCJGuMaAGjCUQ8ys4ca8cqLkWPXFJ"); +const PUMP_GLOBAL: Pubkey = Pubkey::from_str_const("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"); +const PUMP_FEE_RECIPIENT: Pubkey = + Pubkey::from_str_const("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"); +const PUMP_FEE_RECIPIENT_ATA: Pubkey = + Pubkey::from_str_const("ghSBUgyxyvyurm1vJBkU4rUyLJoUipCZhFeiBogKCSy"); +const PUMP_BUYBACK_RECIPIENT: Pubkey = + Pubkey::from_str_const("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"); +const PUMP_BUYBACK_RECIPIENT_ATA: Pubkey = + Pubkey::from_str_const("AktftA98kSWAxn6kVSoqBXBELUArjKu2H9WmKB48ULFY"); +const PUMP_CREATOR_VAULT: Pubkey = + Pubkey::from_str_const("7rnCZqrwmd4L1ZcX7VaKvYZ9n2BjgxmoysrmGhweW97C"); +const PUMP_CREATOR_VAULT_ATA: Pubkey = + Pubkey::from_str_const("C7jfrHkdirzU8F5r1Z1BKacwmwEMgKmLn9Ct3nKpLzmA"); +const PUMP_SHARING_CONFIG: Pubkey = + Pubkey::from_str_const("3NFHbr82N29vRbNHewWuuBHcNzdNuSU6zUBJBaRBPqj8"); +const PUMP_GLOBAL_VOLUME_ACCUMULATOR: Pubkey = + Pubkey::from_str_const("Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y"); +const PUMP_FEE_CONFIG: Pubkey = + Pubkey::from_str_const("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"); +const PUMP_WITHDRAW_AUTHORITY: Pubkey = + Pubkey::from_str_const("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg"); +const AMM_GLOBAL_CONFIG: Pubkey = + Pubkey::from_str_const("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw"); +const BREAKING_FEE_RECIPIENT: Pubkey = + Pubkey::from_str_const("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"); + +const BUY_V2_DISCRIMINATOR: [u8; 8] = [184, 23, 238, 97, 103, 197, 211, 61]; +const MIGRATE_V2_DISCRIMINATOR: [u8; 8] = [187, 203, 18, 31, 206, 237, 254, 41]; +const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173]; + +const MAX_SOL_COST: u64 = 1_000_000_000; + +const CURVE_REAL_TOKEN_RESERVES_OFFSET: usize = 24; +const CURVE_COMPLETE_OFFSET: usize = 48; +const TOKEN_AMOUNT_OFFSET: usize = 64; +const AMM_RESERVED_FEE_RECIPIENT_OFFSET: usize = 385; +const AMM_MAYHEM_MODE_OFFSET: usize = 417; +const POOL_COIN_CREATOR_OFFSET: usize = 211; +const POOL_CASHBACK_FLAG_OFFSET: usize = 244; + +struct GraduationFixture { + user: Pubkey, + user_base: Pubkey, + user_quote: Pubkey, + user_volume_accumulator: Pubkey, + user_volume_accumulator_quote: Pubkey, + pool_authority: Pubkey, + pool: Pubkey, + lp_mint: Pubkey, + pool_authority_base: Pubkey, + pool_authority_quote: Pubkey, + pool_authority_lp: Pubkey, + pool_base: Pubkey, + pool_quote: Pubkey, + pump_event_authority: Pubkey, + pamm_event_authority: Pubkey, + pool_v2: Pubkey, + sell_fee_config: Pubkey, +} + +impl GraduationFixture { + fn new(user: Pubkey) -> Self { + let user_base = associated_token_address(&user, &MINT, &TOKEN_2022); + let user_quote = associated_token_address(&user, &WSOL, &TOKENKEG); + let user_volume_accumulator = + Pubkey::find_program_address(&[b"user_volume_accumulator", user.as_ref()], &PUMP).0; + let user_volume_accumulator_quote = + associated_token_address(&user_volume_accumulator, &WSOL, &TOKENKEG); + let pool_authority = + Pubkey::find_program_address(&[b"pool-authority", MINT.as_ref()], &PUMP).0; + let pool = Pubkey::find_program_address( + &[ + b"pool", + &0u16.to_le_bytes(), + pool_authority.as_ref(), + MINT.as_ref(), + WSOL.as_ref(), + ], + &PAMM, + ) + .0; + let lp_mint = Pubkey::find_program_address(&[b"pool_lp_mint", pool.as_ref()], &PAMM).0; + + Self { + user, + user_base, + user_quote, + user_volume_accumulator, + user_volume_accumulator_quote, + pool_authority, + pool, + lp_mint, + pool_authority_base: associated_token_address(&pool_authority, &MINT, &TOKEN_2022), + pool_authority_quote: associated_token_address(&pool_authority, &WSOL, &TOKENKEG), + pool_authority_lp: associated_token_address(&pool_authority, &lp_mint, &TOKEN_2022), + pool_base: associated_token_address(&pool, &MINT, &TOKEN_2022), + pool_quote: associated_token_address(&pool, &WSOL, &TOKENKEG), + pump_event_authority: Pubkey::find_program_address(&[b"__event_authority"], &PUMP).0, + pamm_event_authority: Pubkey::find_program_address(&[b"__event_authority"], &PAMM).0, + pool_v2: Pubkey::find_program_address(&[b"pool-v2", MINT.as_ref()], &PAMM).0, + sell_fee_config: Pubkey::find_program_address( + &[b"fee_config", PAMM.as_ref()], + &FEE_PROGRAM, + ) + .0, + } + } +} + +fn associated_token_address(owner: &Pubkey, mint: &Pubkey, token_program: &Pubkey) -> Pubkey { + Pubkey::find_program_address( + &[owner.as_ref(), token_program.as_ref(), mint.as_ref()], + &ATA_PROGRAM, + ) + .0 +} + +fn account_meta(pubkey: Pubkey, signer: bool, writable: bool) -> AccountMeta { + if writable { + AccountMeta::new(pubkey, signer) + } else { + AccountMeta::new_readonly(pubkey, signer) + } +} + +fn start_snapshot_surfnet() -> (RpcClient, SurfnetSvmLocker, RunloopGuard) { + let snapshot: std::collections::BTreeMap> = + serde_json::from_str(include_str!( + "../assets/pump_token2022_graduation.snapshot.json" + )) + .expect("graduation snapshot should deserialize"); + let bind_host = "127.0.0.1"; + let bind_port = get_free_port().unwrap(); + let ws_port = get_free_port().unwrap(); + let config = SurfpoolConfig { + simnets: vec![SimnetConfig { + snapshot, + ..SimnetConfig::default() + }], + rpc: RpcConfig { + bind_host: bind_host.to_string(), + bind_port, + ws_port, + ..RpcConfig::default() + }, + ..SurfpoolConfig::default() + }; + let (surfnet_svm, simnet_events_rx, geyser_events_rx) = TestType::no_db().initialize_svm(); + let (simnet_commands_tx, simnet_commands_rx) = unbounded(); + let locker = SurfnetSvmLocker::new(surfnet_svm); + let runloop = spawn_runloop( + locker.clone(), + config, + (simnet_commands_tx, simnet_commands_rx), + geyser_events_rx, + ) + .expect("the runloop should start"); + wait_for_ready_and_connected(&simnet_events_rx) + .expect("surfnet should be ready and connected to the datasource"); + let rpc = RpcClient::new_with_commitment( + format!("http://{bind_host}:{bind_port}"), + CommitmentConfig::confirmed(), + ); + + (rpc, locker, runloop) +} + +async fn cheatcode(rpc: &RpcClient, method: &'static str, params: serde_json::Value) { + let _: serde_json::Value = rpc + .send( + solana_client::rpc_request::RpcRequest::Custom { method }, + params, + ) + .await + .unwrap_or_else(|error| panic!("{method} cheatcode failed: {error:?}")); +} + +fn read_u64(data: &[u8], offset: usize) -> u64 { + u64::from_le_bytes( + data.get(offset..offset + 8) + .unwrap_or_else(|| panic!("missing u64 at account-data offset {offset}")) + .try_into() + .unwrap(), + ) +} + +async fn token_amount(rpc: &RpcClient, address: &Pubkey) -> u64 { + let account = rpc + .get_account(address) + .await + .unwrap_or_else(|error| panic!("token account {address} should exist: {error:?}")); + read_u64(&account.data, TOKEN_AMOUNT_OFFSET) +} + +async fn send_transaction(rpc: &RpcClient, payer: &Keypair, instructions: Vec) { + let transaction = signed_transaction(rpc, payer, instructions).await; + rpc.send_and_confirm_transaction(&transaction) + .await + .unwrap_or_else(|error| panic!("transaction failed: {error:?}")); +} + +async fn signed_transaction( + rpc: &RpcClient, + payer: &Keypair, + instructions: Vec, +) -> Transaction { + let blockhash = rpc + .get_latest_blockhash() + .await + .expect("recent blockhash should be available"); + Transaction::new_signed_with_payer(&instructions, Some(&payer.pubkey()), &[payer], blockhash) +} + +async fn simulate_token_amount_after_transaction( + rpc: &RpcClient, + payer: &Keypair, + instruction: Instruction, + token_account: Pubkey, +) -> u64 { + let transaction = signed_transaction(rpc, payer, vec![instruction]).await; + let simulation = rpc + .simulate_transaction_with_config( + &transaction, + RpcSimulateTransactionConfig { + sig_verify: true, + commitment: Some(CommitmentConfig::confirmed()), + accounts: Some(RpcSimulateTransactionAccountsConfig { + encoding: Some(UiAccountEncoding::Base64), + addresses: vec![token_account.to_string()], + }), + ..RpcSimulateTransactionConfig::default() + }, + ) + .await + .unwrap(); + assert_eq!(simulation.value.err, None, "swap simulation should succeed"); + let account_data = simulation + .value + .accounts + .unwrap() + .into_iter() + .next() + .flatten() + .and_then(|account| account.data.decode()) + .expect("simulation should return the requested token account"); + read_u64(&account_data, TOKEN_AMOUNT_OFFSET) +} + +async fn fund_user(rpc: &RpcClient, user: Pubkey) { + cheatcode( + rpc, + "surfnet_setAccount", + serde_json::json!([user.to_string(), { "lamports": 2_000_000_000u64 }]), + ) + .await; + cheatcode( + rpc, + "surfnet_setTokenAccount", + serde_json::json!([user.to_string(), WSOL.to_string(), { "amount": 1_500_000_000u64 }, null]), + ) + .await; + // buy_v2 requires the user's Token-2022 base ATA to exist. + cheatcode( + rpc, + "surfnet_setTokenAccount", + serde_json::json!([user.to_string(), MINT.to_string(), { "amount": 0u64 }, TOKEN_2022.to_string()]), + ) + .await; +} + +fn build_buy_v2(fixture: &GraduationFixture, completing_buy_amount: u64) -> Instruction { + let accounts = vec![ + account_meta(PUMP_GLOBAL, false, false), // 0 global + account_meta(MINT, false, false), // 1 base_mint + account_meta(WSOL, false, false), // 2 quote_mint + account_meta(TOKEN_2022, false, false), // 3 base_token_program + account_meta(TOKENKEG, false, false), // 4 quote_token_program + account_meta(ATA_PROGRAM, false, false), // 5 associated_token_program + account_meta(PUMP_FEE_RECIPIENT, false, true), // 6 fee_recipient + account_meta(PUMP_FEE_RECIPIENT_ATA, false, true), // 7 associated_quote_fee_recipient + account_meta(PUMP_BUYBACK_RECIPIENT, false, true), // 8 buyback_fee_recipient + account_meta(PUMP_BUYBACK_RECIPIENT_ATA, false, true), // 9 associated_quote_buyback_fee_recipient + account_meta(CURVE, false, true), // 10 bonding_curve + account_meta(BASE_VAULT, false, true), // 11 associated_base_bonding_curve + account_meta(QUOTE_VAULT, false, true), // 12 associated_quote_bonding_curve + account_meta(fixture.user, true, true), // 13 user + account_meta(fixture.user_base, false, true), // 14 associated_base_user + account_meta(fixture.user_quote, false, true), // 15 associated_quote_user + account_meta(PUMP_CREATOR_VAULT, false, true), // 16 creator_vault + account_meta(PUMP_CREATOR_VAULT_ATA, false, true), // 17 associated_creator_vault + account_meta(PUMP_SHARING_CONFIG, false, false), // 18 sharing_config + account_meta(PUMP_GLOBAL_VOLUME_ACCUMULATOR, false, false), // 19 global_volume_accumulator + account_meta(fixture.user_volume_accumulator, false, true), // 20 user_volume_accumulator + account_meta(fixture.user_volume_accumulator_quote, false, true), // 21 associated_user_volume_accumulator + account_meta(PUMP_FEE_CONFIG, false, false), // 22 fee_config + account_meta(FEE_PROGRAM, false, false), // 23 fee_program + account_meta(SYSTEM_PROGRAM, false, false), // 24 system_program + account_meta(fixture.pump_event_authority, false, false), // 25 event_authority + account_meta(PUMP, false, false), // 26 program + ]; + let mut data = BUY_V2_DISCRIMINATOR.to_vec(); + data.extend_from_slice(&completing_buy_amount.to_le_bytes()); + data.extend_from_slice(&MAX_SOL_COST.to_le_bytes()); + + Instruction { + program_id: PUMP, + accounts, + data, + } +} + +fn build_migrate_v2(fixture: &GraduationFixture) -> Vec { + let accounts = vec![ + account_meta(PUMP_GLOBAL, false, false), // 0 global + account_meta(PUMP_WITHDRAW_AUTHORITY, false, true), // 1 withdraw_authority + account_meta(MINT, false, false), // 2 base_mint + account_meta(WSOL, false, false), // 3 quote_mint + account_meta(CURVE, false, true), // 4 bonding_curve + account_meta(BASE_VAULT, false, true), // 5 associated_base_bonding_curve + account_meta(QUOTE_VAULT, false, true), // 6 associated_quote_bonding_curve + account_meta(fixture.user, true, false), // 7 user + account_meta(SYSTEM_PROGRAM, false, false), // 8 system_program + account_meta(PAMM, false, false), // 9 pump_amm_program + account_meta(fixture.pool, false, true), // 10 pool + account_meta(fixture.pool_authority, false, true), // 11 pool_authority + account_meta(fixture.pool_authority_base, false, true), // 12 pool_authority_mint_account + account_meta(fixture.pool_authority_quote, false, true), // 13 pool_authority_quote_account + account_meta(AMM_GLOBAL_CONFIG, false, false), // 14 amm_global_config + account_meta(fixture.lp_mint, false, true), // 15 pool_lp_mint + account_meta(fixture.pool_authority_lp, false, true), // 16 user_pool_token_account + account_meta(fixture.pool_base, false, true), // 17 pool_base_token_account + account_meta(fixture.pool_quote, false, true), // 18 pool_quote_token_account + account_meta(TOKEN_2022, false, false), // 19 base_token_program + account_meta(TOKENKEG, false, false), // 20 quote_token_program + account_meta(TOKEN_2022, false, false), // 21 token_2022_program + account_meta(ATA_PROGRAM, false, false), // 22 associated_token_program + account_meta(fixture.pamm_event_authority, false, false), // 23 pump_amm_event_authority + account_meta(RENT_SYSVAR, false, false), // 24 rent + account_meta(fixture.pump_event_authority, false, false), // 25 event_authority + account_meta(PUMP, false, false), // 26 program + ]; + + vec![ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + Instruction { + program_id: PUMP, + accounts, + data: MIGRATE_V2_DISCRIMINATOR.to_vec(), + }, + ] +} + +async fn build_sell( + rpc: &RpcClient, + fixture: &GraduationFixture, + base_amount_in: u64, +) -> Instruction { + let global_config = rpc + .get_account(&AMM_GLOBAL_CONFIG) + .await + .expect("frozen AMM global config should load"); + assert_eq!( + global_config.data[AMM_MAYHEM_MODE_OFFSET], 1, + "fixture expects mayhem mode" + ); + let reserved_fee_recipient = Pubkey::try_from( + &global_config.data + [AMM_RESERVED_FEE_RECIPIENT_OFFSET..AMM_RESERVED_FEE_RECIPIENT_OFFSET + 32], + ) + .unwrap(); + let pool_data = rpc + .get_account(&fixture.pool) + .await + .expect("migrated pool should exist") + .data; + assert_eq!( + pool_data[POOL_CASHBACK_FLAG_OFFSET], 0, + "24-account sell is only valid for a non-cashback pool" + ); + let coin_creator = + Pubkey::try_from(&pool_data[POOL_COIN_CREATOR_OFFSET..POOL_COIN_CREATOR_OFFSET + 32]) + .unwrap(); + let coin_creator_vault_authority = + Pubkey::find_program_address(&[b"creator_vault", coin_creator.as_ref()], &PAMM).0; + let accounts = vec![ + account_meta(fixture.pool, false, true), // 0 pool + account_meta(fixture.user, true, true), // 1 user + account_meta(AMM_GLOBAL_CONFIG, false, false), // 2 global_config + account_meta(MINT, false, false), // 3 base_mint + account_meta(WSOL, false, false), // 4 quote_mint + account_meta(fixture.user_base, false, true), // 5 user_base_token_account + account_meta(fixture.user_quote, false, true), // 6 user_quote_token_account + account_meta(fixture.pool_base, false, true), // 7 pool_base_token_account + account_meta(fixture.pool_quote, false, true), // 8 pool_quote_token_account + account_meta(reserved_fee_recipient, false, false), // 9 protocol_fee_recipient + account_meta( + associated_token_address(&reserved_fee_recipient, &WSOL, &TOKENKEG), + false, + true, + ), // 10 protocol_fee_recipient_token_account + account_meta(TOKEN_2022, false, false), // 11 base_token_program + account_meta(TOKENKEG, false, false), // 12 quote_token_program + account_meta(SYSTEM_PROGRAM, false, false), // 13 system_program + account_meta(ATA_PROGRAM, false, false), // 14 associated_token_program + account_meta(fixture.pamm_event_authority, false, false), // 15 event_authority + account_meta(PAMM, false, false), // 16 program + account_meta( + associated_token_address(&coin_creator_vault_authority, &WSOL, &TOKENKEG), + false, + true, + ), // 17 coin_creator_vault_ata + account_meta(coin_creator_vault_authority, false, false), // 18 coin_creator_vault_authority + account_meta(fixture.sell_fee_config, false, false), // 19 fee_config + account_meta(FEE_PROGRAM, false, false), // 20 fee_program + account_meta(fixture.pool_v2, false, false), // 21 pool_v2 + account_meta(BREAKING_FEE_RECIPIENT, false, false), // 22 fee_recipient + account_meta( + associated_token_address(&BREAKING_FEE_RECIPIENT, &WSOL, &TOKENKEG), + false, + true, + ), // 23 fee_recipient_token_account + ]; + let mut data = SELL_DISCRIMINATOR.to_vec(); + data.extend_from_slice(&base_amount_in.to_le_bytes()); + // Zero slippage protection is acceptable only in this regression test. + data.extend_from_slice(&0u64.to_le_bytes()); + + Instruction { + program_id: PAMM, + accounts, + data, + } +} + +/// The snapshot freezes account state while the live programs expose behavioral upgrades. +#[tokio::test(flavor = "multi_thread")] +#[ignore = "requires network: forks mainnet for the live pump and pAMM programs"] +async fn test_pump_token2022_graduation_lifecycle() { + let (rpc, locker, _runloop) = start_snapshot_surfnet(); + let user = Keypair::new(); + let fixture = GraduationFixture::new(user.pubkey()); + let preparation = build_pump_graduation_scenario( + MINT, + &rpc.get_account(&MINT).await.unwrap(), + &rpc.get_account(&CURVE).await.unwrap(), + &rpc.get_account(&BASE_VAULT).await.unwrap(), + None, + &rpc.get_account(&PUMP_GLOBAL).await.unwrap(), + ) + .unwrap(); + let completing_buy_amount = preparation.completing_buy_amount; + let migration_reserve = preparation.migration_reserve; + locker + .register_scenario(preparation.scenario, Some(0)) + .unwrap(); + locker + .materialize_overrides_for_slot(&None, 1) + .await + .unwrap(); + fund_user(&rpc, fixture.user).await; + + send_transaction( + &rpc, + &user, + vec![build_buy_v2(&fixture, completing_buy_amount)], + ) + .await; + + let curve_after = rpc.get_account(&CURVE).await.unwrap(); + assert_eq!( + read_u64(&curve_after.data, CURVE_REAL_TOKEN_RESERVES_OFFSET), + 0, + "buy should exhaust the curve's real token reserves" + ); + assert_eq!( + curve_after.data[CURVE_COMPLETE_OFFSET], 1, + "buy should complete the curve" + ); + assert_eq!( + token_amount(&rpc, &fixture.user_base).await, + completing_buy_amount, + "user should receive the purchased base tokens" + ); + assert_eq!( + token_amount(&rpc, &BASE_VAULT).await, + migration_reserve, + "buy should leave the migration reserve in the curve vault" + ); + + send_transaction(&rpc, &user, build_migrate_v2(&fixture)).await; + + assert_eq!( + rpc.get_account(&fixture.pool).await.unwrap().owner, + PAMM, + "migrate should create a pAMM-owned pool" + ); + assert_eq!( + rpc.get_account(&fixture.lp_mint).await.unwrap().owner, + TOKEN_2022, + "migrate should create a Token-2022 LP mint" + ); + assert_eq!( + token_amount(&rpc, &fixture.pool_base).await, + migration_reserve, + "migrate should seed the pool with the reserved base liquidity" + ); + assert!( + token_amount(&rpc, &fixture.pool_quote).await > 0, + "migrate should seed the pool with quote liquidity" + ); + + let pre_user_base = token_amount(&rpc, &fixture.user_base).await; + let pre_user_quote = token_amount(&rpc, &fixture.user_quote).await; + let pre_pool_base = token_amount(&rpc, &fixture.pool_base).await; + let pre_pool_quote = token_amount(&rpc, &fixture.pool_quote).await; + let base_amount_in = pre_user_base / 2; + let sell = build_sell(&rpc, &fixture, base_amount_in).await; + let baseline_user_quote = + simulate_token_amount_after_transaction(&rpc, &user, sell.clone(), fixture.user_quote) + .await; + let price_shock = build_pump_swap_price_shock_scenario( + MINT, + &rpc.get_account(&fixture.pool).await.unwrap(), + pre_pool_quote.checked_mul(9).unwrap(), + ) + .unwrap(); + locker + .register_scenario(price_shock.scenario, Some(0)) + .unwrap(); + locker + .materialize_overrides_for_slot(&None, 1) + .await + .unwrap(); + let shocked_user_quote = + simulate_token_amount_after_transaction(&rpc, &user, sell.clone(), fixture.user_quote) + .await; + assert_ne!( + shocked_user_quote - pre_user_quote, + baseline_user_quote - pre_user_quote, + "the price-shock scenario should change the real swap output" + ); + send_transaction(&rpc, &user, vec![sell]).await; + + assert_eq!( + token_amount(&rpc, &fixture.user_base).await, + pre_user_base - base_amount_in, + "sell should debit the user's base tokens" + ); + assert!( + token_amount(&rpc, &fixture.user_quote).await > pre_user_quote, + "sell should credit the user's quote tokens" + ); + assert_eq!( + token_amount(&rpc, &fixture.pool_base).await, + pre_pool_base + base_amount_in, + "sell should credit the pool's base vault" + ); + assert!( + token_amount(&rpc, &fixture.pool_quote).await < pre_pool_quote, + "sell should debit the pool's quote vault" + ); +} diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index 8634f304d..b6e23b737 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -1070,6 +1070,25 @@ impl TokenAccount { } } + pub fn unpack_for_program(bytes: &[u8], token_program_id: &Pubkey) -> SurfpoolResult { + if *token_program_id == spl_token_2022_interface::id() { + if let Ok(account) = + StateWithExtensions::::unpack(bytes) + { + return Ok(Self::SplToken2022(account.base)); + } + return spl_token_2022_interface::state::Account::unpack(bytes) + .map(Self::SplToken2022) + .map_err(|_| SurfpoolError::unpack_token_account()); + } + if *token_program_id == spl_token_interface::id() { + return spl_token_interface::state::Account::unpack(bytes) + .map(Self::SplToken) + .map_err(|_| SurfpoolError::unpack_token_account()); + } + Err(SurfpoolError::unsupported_token_program(*token_program_id)) + } + pub fn new( token_program_id: &Pubkey, owner: Pubkey, @@ -1110,6 +1129,30 @@ impl TokenAccount { } } + pub fn pack_into_preserving_extensions(&self, original: &[u8]) -> SurfpoolResult> { + let base_len = spl_token_interface::state::Account::LEN; + if original.len() < base_len { + return Err(SurfpoolError::unpack_token_account()); + } + + let mut data = original.to_vec(); + match self { + Self::SplToken2022(account) => account.pack_into_slice(&mut data[..base_len]), + Self::SplToken(account) => account.pack_into_slice(&mut data[..base_len]), + } + Ok(data) + } + + pub fn patch_amount_preserving_extensions(&self, original: &[u8]) -> SurfpoolResult> { + if original.len() < spl_token_interface::state::Account::LEN { + return Err(SurfpoolError::unpack_token_account()); + } + + let mut data = original.to_vec(); + data[64..72].copy_from_slice(&self.amount().to_le_bytes()); + Ok(data) + } + pub fn owner(&self) -> Pubkey { match self { Self::SplToken2022(account) => account.owner, @@ -1201,6 +1244,92 @@ impl TokenAccount { } } +#[cfg(test)] +mod token_account_packing_tests { + use super::*; + + #[test] + fn packing_preserves_token_2022_extension_bytes() { + let mut token_account = TokenAccount::new( + &spl_token_2022_interface::id(), + Pubkey::new_unique(), + Pubkey::new_unique(), + None, + ); + token_account.set_amount(42); + let mut original = vec![0u8; 170]; + original[165..].copy_from_slice(&[1, 2, 3, 4, 5]); + + let packed = token_account + .pack_into_preserving_extensions(&original) + .unwrap(); + + assert_eq!(packed.len(), 170); + assert_eq!(&packed[64..72], &42u64.to_le_bytes()); + assert_eq!(&packed[165..], &[1, 2, 3, 4, 5]); + } + + #[test] + fn packing_keeps_classic_token_accounts_at_165_bytes() { + let mut token_account = TokenAccount::new( + &spl_token_interface::id(), + Pubkey::new_unique(), + Pubkey::new_unique(), + None, + ); + token_account.set_amount(42); + + let packed = token_account + .pack_into_preserving_extensions(&[0u8; 165]) + .unwrap(); + + assert_eq!(packed.len(), 165); + assert_eq!(&packed[64..72], &42u64.to_le_bytes()); + } + + #[test] + fn amount_patch_changes_only_the_amount_bytes() { + for token_program in [spl_token_interface::id(), spl_token_2022_interface::id()] { + let mut token_account = TokenAccount::new( + &token_program, + Pubkey::new_unique(), + Pubkey::new_unique(), + None, + ); + let mut original = token_account.pack_into_vec(); + if token_program == spl_token_2022_interface::id() { + original.extend_from_slice(&[1, 2, 3, 4, 5]); + } + token_account.set_amount(42); + + let patched = token_account + .patch_amount_preserving_extensions(&original) + .unwrap(); + + assert_eq!(patched.len(), original.len()); + assert_eq!(&patched[64..72], &42u64.to_le_bytes()); + assert_eq!(&patched[..64], &original[..64]); + assert_eq!(&patched[72..], &original[72..]); + } + } + + #[test] + fn unpack_for_program_uses_the_account_owner() { + let classic = TokenAccount::new( + &spl_token_interface::id(), + Pubkey::new_unique(), + Pubkey::new_unique(), + None, + ) + .pack_into_vec(); + + assert!(matches!( + TokenAccount::unpack_for_program(&classic, &spl_token_interface::id()).unwrap(), + TokenAccount::SplToken(_) + )); + } +} + /// Returns `true` if the given account bytes are a Token-2022 mint that carries /// the transfer-fee config extension. Used to decide whether a fabricated /// confidential account also needs the companion confidential-transfer diff --git a/crates/mcp/src/surfpool/mod.rs b/crates/mcp/src/surfpool/mod.rs index b7568cc95..983b49726 100644 --- a/crates/mcp/src/surfpool/mod.rs +++ b/crates/mcp/src/surfpool/mod.rs @@ -104,6 +104,26 @@ pub struct SearchConstantOptionsParams { pub query: String, } +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreatePumpGraduationScenarioParams { + #[schemars( + description = "Live Token-2022 Pump mint. If validation fails, report the error and do not retry without tokenMint." + )] + pub token_mint: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreatePumpSwapPriceShockScenarioParams { + #[schemars( + description = "Mint of a migrated pump.fun coin with a canonical WSOL PumpSwap pool." + )] + pub token_mint: String, + #[schemars(description = "Positive virtual quote reserve amount, passed as a decimal string.")] + pub virtual_quote_reserves: String, +} + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct StartSurfnetWithTokenAccountsParams { #[schemars( @@ -724,10 +744,12 @@ impl Surfpool { // Check if the value exists in values map if let Some(value) = override_instance.values.get(&prop.path) { if let Some(value_str) = value.as_str() { - // Validate the value is one of the valid options - let is_valid = constant_def.options.iter().any(|opt| { - opt.value.to_lowercase() == value_str.to_lowercase() - }); + // Base58 is case-sensitive: a case-folded match would + // accept a value that derives a different PDA. + let is_valid = constant_def + .options + .iter() + .any(|opt| opt.value == value_str); if !is_valid { // Show only first 10 options to avoid overwhelming error messages let sample_options: Vec = constant_def @@ -755,6 +777,15 @@ impl Surfpool { sample_options.join("\n ") )); } + } else { + validation_errors.push(format!( + "Override '{}' (template '{}'): Value for '{}' (constant: '{}') must be a base-58 mint address string, got: {}", + override_instance.id, + override_instance.template_id, + prop.path, + constant_name, + value + )); } } else { // Value is missing - required for PDA derivation @@ -883,6 +914,128 @@ impl Surfpool { Ok(CallToolResult::success(vec![Content::text(json_str)])) } + #[tool( + description = "Creates an editable Pump Graduation state-preparation scenario for the required tokenMint. If validation fails, report that error and do not retry. The backend validates the mint, incomplete bonding curve, curve vault, and absent canonical PumpSwap pool." + )] + async fn create_pump_graduation_scenario( + &self, + Parameters(params): Parameters, + ) -> Result { + let endpoint = format!( + "http://127.0.0.1:{}/v1/scenarios/pump-graduation", + CHANGE_TO_DEFAULT_STUDIO_PORT_ONCE_SUPERVISOR_MERGED + ); + let response = match reqwest::Client::new() + .post(&endpoint) + .json(&serde_json::json!({ "tokenMint": params.token_mint })) + .send() + .await + { + Ok(response) => response, + Err(error) => { + let result = RegisterScenarioResponse::error(format!( + "Failed to reach the Pump graduation endpoint: {error}" + )); + return Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])); + } + }; + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + if !status.is_success() { + let result = RegisterScenarioResponse::error(if body.is_empty() { + format!("Pump graduation validation failed with HTTP {status}") + } else { + body + }); + return Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])); + } + + let response: serde_json::Value = serde_json::from_str(&body).map_err(|error| { + McpError::internal_error(format!("Invalid Pump graduation response: {error}"), None) + })?; + let scenario_id = response + .get("id") + .and_then(serde_json::Value::as_str) + .ok_or_else(|| McpError::internal_error("Pump graduation response has no id", None))?; + let url = format!( + "http://127.0.0.1:{}/scenarios?id={}&tab=editor", + CHANGE_TO_DEFAULT_STUDIO_PORT_ONCE_SUPERVISOR_MERGED, scenario_id + ); + let result = RegisterScenarioResponse::success(url); + Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])) + } + + #[tool( + description = "Creates an editable PumpSwap price-shock scenario for a required migrated tokenMint and positive virtualQuoteReserves decimal string. The backend validates that the mint has a canonical WSOL PumpSwap pool. Prepare state only; do not build or execute swaps." + )] + async fn create_pump_swap_price_shock_scenario( + &self, + Parameters(params): Parameters, + ) -> Result { + let endpoint = format!( + "http://127.0.0.1:{}/v1/scenarios/pump-swap-price-shock", + CHANGE_TO_DEFAULT_STUDIO_PORT_ONCE_SUPERVISOR_MERGED + ); + let response = match reqwest::Client::new() + .post(&endpoint) + .json(&serde_json::json!({ + "tokenMint": params.token_mint, + "virtualQuoteReserves": params.virtual_quote_reserves, + })) + .send() + .await + { + Ok(response) => response, + Err(error) => { + let result = RegisterScenarioResponse::error(format!( + "Failed to reach the PumpSwap price shock endpoint: {error}" + )); + return Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])); + } + }; + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + if !status.is_success() { + let result = RegisterScenarioResponse::error(if body.is_empty() { + format!("PumpSwap price shock validation failed with HTTP {status}") + } else { + body + }); + return Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])); + } + + let response: serde_json::Value = serde_json::from_str(&body).map_err(|error| { + McpError::internal_error( + format!("Invalid PumpSwap price shock response: {error}"), + None, + ) + })?; + let scenario_id = response + .get("id") + .and_then(serde_json::Value::as_str) + .ok_or_else(|| { + McpError::internal_error("PumpSwap price shock response has no id", None) + })?; + let url = format!( + "http://127.0.0.1:{}/scenarios?id={}&tab=editor", + CHANGE_TO_DEFAULT_STUDIO_PORT_ONCE_SUPERVISOR_MERGED, scenario_id + ); + let result = RegisterScenarioResponse::success(url); + Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string(&result).unwrap_or_default(), + )])) + } + #[tool( description = "Fetches ALL available override templates. MUST be called before create_scenario to get valid templateId values and property names. Constants are summarized as {label, description, optionsCount} - resolve an actual option value with search_constant_options." )] @@ -1153,6 +1306,29 @@ mod tests { assert_eq!(parsed.template_id, "pyth-price-feed-v2"); } + #[test] + fn pump_graduation_mint_is_required() { + assert!( + serde_json::from_value::(serde_json::json!({})) + .is_err() + ); + } + + #[test] + fn pump_swap_price_shock_inputs_are_required() { + assert!( + serde_json::from_value::(serde_json::json!({})) + .is_err() + ); + let parsed: CreatePumpSwapPriceShockScenarioParams = + serde_json::from_value(serde_json::json!({ + "tokenMint": "mint", + "virtualQuoteReserves": "15000000000000", + })) + .unwrap(); + assert_eq!(parsed.virtual_quote_reserves, "15000000000000"); + } + fn json_of(result: &CallToolResult) -> serde_json::Value { let text = &result.content[0].as_text().expect("text content").text; serde_json::from_str(text).expect("valid JSON payload") @@ -1273,4 +1449,223 @@ mod tests { .unwrap(); assert_eq!(unknown_constant.is_error, Some(true)); } + + #[tokio::test] + async fn get_override_templates_lists_the_pump_templates_compactly() { + let surfpool = Surfpool::new(); + let result = surfpool.get_override_templates().await.unwrap(); + assert_ne!(result.is_error, Some(true)); + + let templates = json_of(&result); + let templates = templates.as_array().unwrap(); + for id in [ + "pump-bonding-curve-custom", + "pump-global", + "pump-amm-pool-state", + "pump-amm-canonical-pool", + "pump-amm-global-config", + ] { + let template = templates + .iter() + .find(|t| t["id"] == id) + .unwrap_or_else(|| panic!("template {id} missing from the model's view")); + assert!( + template.get("idl").is_none(), + "{id} must not inline the ~160KB IDL into the LLM context" + ); + } + + let curve = templates + .iter() + .find(|t| t["id"] == "pump-bonding-curve-custom") + .unwrap(); + let token_mint = &curve["constants"]["token_mint"]; + assert!( + token_mint["optionsCount"].as_u64().unwrap() > 0, + "the verified-tokens catalog must be visible as a summary" + ); + assert!(token_mint.get("options").is_none()); + } + + #[tokio::test] + async fn search_resolves_pump_coin_mints_for_both_programs() { + let surfpool = Surfpool::new(); + + let result = surfpool + .search_constant_options(search("pump-bonding-curve-custom", None, "pump")) + .await + .unwrap(); + assert_ne!(result.is_error, Some(true)); + let payload = json_of(&result); + let results = payload["results"].as_array().unwrap(); + assert!(!results.is_empty(), "a pump coin must be findable"); + assert!( + results + .iter() + .all(|r| !r["value"].as_str().unwrap().is_empty()), + "every result must carry the mint create_scenario expects" + ); + assert!( + results + .iter() + .any(|r| r["value"].as_str().unwrap().ends_with("pump")), + "pump.fun mints are recognizable by their suffix" + ); + + let result = surfpool + .search_constant_options(search("pump-amm-canonical-pool", Some("token_mint"), "")) + .await + .unwrap(); + assert_ne!(result.is_error, Some(true)); + let payload = json_of(&result); + assert!( + payload["totalMatches"].as_u64().unwrap() > 0, + "the canonical pool template must expose base mints to search" + ); + } + + #[tokio::test] + async fn pump_token_catalogs_offer_only_pump_mints() { + let registry = TemplateRegistry::new(); + for template_id in ["pump-bonding-curve-custom", "pump-amm-canonical-pool"] { + let template = registry.get(template_id).expect("template"); + let constant = template.constants.get("token_mint").expect("constant"); + assert_eq!( + constant.options.len(), + 976, + "{template_id} must offer every pump-suffixed catalog mint (976 in the CSV)" + ); + assert!( + constant.options.iter().all(|o| o.value.ends_with("pump")), + "{template_id} must offer only pump.fun mints" + ); + } + + let surfpool = Surfpool::new(); + for mint in [ + "So11111111111111111111111111111111111111112", + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + ] { + let result = surfpool + .search_constant_options(search("pump-bonding-curve-custom", None, mint)) + .await + .unwrap(); + let payload = json_of(&result); + assert_eq!( + payload["totalMatches"].as_u64().unwrap(), + 0, + "{mint} must not be offered for a bonding curve" + ); + } + } + + #[tokio::test] + async fn create_scenario_rejects_bad_pump_overrides_before_any_http_call() { + let surfpool = Surfpool::new(); + let registry = TemplateRegistry::new(); + let curve_address = registry + .get("pump-bonding-curve-custom") + .expect("template") + .address + .clone(); + + let mut unknown = surfpool_types::Scenario::new( + "bad template".to_string(), + "unknown templateId must be rejected".to_string(), + ); + unknown.add_override(surfpool_types::OverrideInstance::new( + "pump-bonding-curve".to_string(), + 0, + curve_address.clone(), + )); + let result = surfpool.create_scenario(Parameters(unknown)).await.unwrap(); + let text = &result.content[0].as_text().expect("text").text; + assert!( + text.contains("Invalid templateId"), + "a misremembered id must be named, got: {text}" + ); + + let mut incomplete = surfpool_types::Scenario::new( + "missing mint".to_string(), + "a PDA constant without a value must be rejected".to_string(), + ); + incomplete.add_override( + surfpool_types::OverrideInstance::new( + "pump-bonding-curve-custom".to_string(), + 0, + curve_address, + ) + .with_values(HashMap::from([( + "complete".to_string(), + serde_json::json!(true), + )])), + ); + let result = surfpool + .create_scenario(Parameters(incomplete)) + .await + .unwrap(); + let text = &result.content[0].as_text().expect("text").text; + assert!( + text.contains("Missing required value") && text.contains("token_mint"), + "the missing PDA seed value must be named, got: {text}" + ); + + let mut wrong_type = surfpool_types::Scenario::new( + "numeric mint".to_string(), + "a non-string mint must be rejected, not silently skipped".to_string(), + ); + wrong_type.add_override( + surfpool_types::OverrideInstance::new( + "pump-bonding-curve-custom".to_string(), + 0, + registry + .get("pump-bonding-curve-custom") + .expect("template") + .address + .clone(), + ) + .with_values(HashMap::from([( + "token_mint".to_string(), + serde_json::json!(12345), + )])), + ); + let result = surfpool + .create_scenario(Parameters(wrong_type)) + .await + .unwrap(); + let text = &result.content[0].as_text().expect("text").text; + assert!( + text.contains("must be a base-58 mint address string") && text.contains("token_mint"), + "the wrong-typed mint must be named, got: {text}" + ); + + let mut tampered = surfpool_types::Scenario::new( + "tampered mint".to_string(), + "base58 is case-sensitive; a case-folded match targets another PDA".to_string(), + ); + tampered.add_override( + surfpool_types::OverrideInstance::new( + "pump-bonding-curve-custom".to_string(), + 0, + registry + .get("pump-bonding-curve-custom") + .expect("template") + .address + .clone(), + ) + .with_values(HashMap::from([( + "token_mint".to_string(), + serde_json::json!("9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgPUMP"), + )])), + ); + let result = surfpool + .create_scenario(Parameters(tampered)) + .await + .unwrap(); + let text = &result.content[0].as_text().expect("text").text; + assert!( + text.contains("Invalid value"), + "a case-flipped mint must be rejected, got: {text}" + ); + } } diff --git a/crates/types/src/scenarios.rs b/crates/types/src/scenarios.rs index fb3859572..ca48ea716 100644 --- a/crates/types/src/scenarios.rs +++ b/crates/types/src/scenarios.rs @@ -390,6 +390,8 @@ pub struct OverrideTemplate { /// Account type name from the IDL (e.g., "PriceAccount") /// This specifies which account struct in the IDL to use pub account_type: String, + #[serde(default)] + pub write_mode: OverrideWriteMode, /// List of editable properties with full metadata pub properties: Vec, /// Protocol-specific constants (e.g., AMM configs, well-known tokens) @@ -422,6 +424,7 @@ impl OverrideTemplate { idl, address, account_type, + write_mode: OverrideWriteMode::default(), properties, constants: HashMap::new(), tags: Vec::new(), @@ -461,6 +464,15 @@ impl OverrideTemplate { } } +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OverrideWriteMode { + #[default] + Idl, + #[serde(rename = "token_2022_account_amount")] + Token2022AccountAmount, +} + /// A concrete instance of an override template with specific values #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, schemars::JsonSchema)] #[serde(rename_all = "camelCase")] @@ -626,6 +638,8 @@ pub struct YamlOverrideTemplateFile { pub version: String, pub account_type: String, #[serde(default)] + pub write_mode: OverrideWriteMode, + #[serde(default)] pub properties: Vec, #[serde(default)] pub constants: HashMap, @@ -649,6 +663,7 @@ impl YamlOverrideTemplateFile { idl, address: self.address.into(), account_type: self.account_type, + write_mode: self.write_mode, properties: self.properties.into_iter().map(Into::into).collect(), constants: self .constants @@ -706,6 +721,9 @@ pub enum YamlConstantSource { /// Optional limit on number of tokens to include #[serde(default)] limit: Option, + /// Optional required suffix on the token's mint address (e.g. "pump") + #[serde(default)] + address_suffix: Option, }, } @@ -731,13 +749,14 @@ impl YamlConstantDefinition { source, filter_tags, limit, + address_suffix, } => { if source == "verified_tokens" { - use crate::verified_tokens::VERIFIED_TOKENS_BY_SYMBOL; + use crate::verified_tokens::VERIFIED_TOKENS; - let mut tokens: Vec<_> = VERIFIED_TOKENS_BY_SYMBOL + let mut tokens: Vec<_> = VERIFIED_TOKENS .iter() - .filter(|(_, _token)| { + .filter(|_token| { // If no filter tags specified, include all tokens if filter_tags.is_empty() { return true; @@ -748,8 +767,14 @@ impl YamlConstantDefinition { // TODO: Parse tags from CSV into TokenInfo struct true }) - .map(|(symbol, token)| ConstantOption { - id: symbol.to_lowercase(), + .filter(|token| { + address_suffix + .as_deref() + .map_or(true, |suffix| token.address.ends_with(suffix)) + }) + .map(|token| ConstantOption { + // The mint address: unique even when symbols collide + id: token.address.clone(), label: format!("{} ({})", token.symbol, token.name), description: Some(token.name.clone()), value: token.address.clone(), @@ -774,8 +799,8 @@ impl YamlConstantDefinition { }) .collect(); - // Sort by symbol for consistent ordering - tokens.sort_by(|a, b| a.id.cmp(&b.id)); + // Deterministic order keeps search_constant_options paging stable + tokens.sort_by(|a, b| a.label.cmp(&b.label).then_with(|| a.id.cmp(&b.id))); // Apply limit if specified if let Some(limit) = limit { @@ -922,6 +947,8 @@ pub struct YamlOverrideTemplateEntry { /// Account type name from the IDL (overrides collection-level account_type) #[serde(default)] pub idl_account_name: Option, + #[serde(default)] + pub write_mode: OverrideWriteMode, /// Properties with full metadata #[serde(default)] pub properties: Vec, @@ -955,6 +982,7 @@ impl YamlOverrideTemplateCollection { account_type: entry .idl_account_name .unwrap_or_else(|| default_account_type.clone()), + write_mode: entry.write_mode, properties: entry.properties.into_iter().map(Into::into).collect(), constants: constants.clone(), tags: self.tags.clone(), @@ -974,6 +1002,8 @@ pub struct YamlOverrideTemplate { pub protocol: String, pub version: String, pub account_type: String, + #[serde(default)] + pub write_mode: OverrideWriteMode, pub idl: Idl, pub address: YamlAccountAddress, #[serde(default)] @@ -998,6 +1028,7 @@ impl YamlOverrideTemplate { idl: self.idl, address: self.address.into(), account_type: self.account_type, + write_mode: self.write_mode, properties: self.properties.into_iter().map(Into::into).collect(), constants: self .constants diff --git a/crates/types/src/verified_tokens.rs b/crates/types/src/verified_tokens.rs index 6ac43e556..b3958e55d 100644 --- a/crates/types/src/verified_tokens.rs +++ b/crates/types/src/verified_tokens.rs @@ -45,9 +45,11 @@ fn parse_csv_line(line: &str) -> Vec { fields } -pub static VERIFIED_TOKENS_BY_SYMBOL: Lazy> = Lazy::new(|| { +/// Every catalog row in CSV order. Option lists must use this: the by-symbol +/// map collapses tokens sharing a symbol and silently drops their mints. +pub static VERIFIED_TOKENS: Lazy> = Lazy::new(|| { let csv = include_str!("verified_tokens.csv"); - let mut map = HashMap::new(); + let mut tokens = Vec::new(); for (i, line) in csv.lines().enumerate() { if i == 0 { @@ -69,18 +71,23 @@ pub static VERIFIED_TOKENS_BY_SYMBOL: Lazy> = Lazy::n let icon = fields[3].clone(); let decimals: u8 = fields[4].parse().unwrap_or(0); - let token = TokenInfo { + tokens.push(TokenInfo { address, name, - symbol: symbol.clone(), + symbol, decimals, logo_uri: if icon.is_empty() { None } else { Some(icon) }, - }; - - map.insert(symbol.to_uppercase(), token); + }); } - map + tokens +}); + +pub static VERIFIED_TOKENS_BY_SYMBOL: Lazy> = Lazy::new(|| { + VERIFIED_TOKENS + .iter() + .map(|token| (token.symbol.to_uppercase(), token.clone())) + .collect() }); #[cfg(test)]