use std::collections::HashMap;
use std::fs;
use anchor_lang::AccountSerialize;
use anchor_spl::token::spl_token;
use solana_client::rpc_client::RpcClient;
use solana_program::program_pack::Pack;
use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::rent::Rent;
use solana_sdk::system_program;
use pump_rust_client::accounts::pump_amm::decode_pool;
use pump_rust_client::accounts::{decode_bonding_curve, decode_global, decode_sharing_config};
use pump_rust_client::constants;
use pump_rust_client::pda;
use pump_rust_client::state::Global;
#[path = "../../../tests/common/fixtures.rs"]
mod fixtures;
use fixtures::{FixtureMint, FIXTURE_MINTS};
const OUT_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/artifacts/accounts_to_load.zst"
);
mod raydium_sol_usdc {
use super::Pubkey;
use solana_program::pubkey;
pub const POOL: Pubkey = pubkey!("58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2");
pub const AMM_AUTHORITY: Pubkey = pubkey!("5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1");
pub const OPEN_ORDERS: Pubkey = pubkey!("HRk9CMrpq7Jn9sh7mzxE8CChHG8dneX9p475QKz4Fsfc");
pub const TARGET_ORDERS: Pubkey = pubkey!("CZza3Ej4Mc58MnxWA385itCC9jCo3L1D7zc3LKy1bZMR");
pub const WSOL_VAULT: Pubkey = pubkey!("DQyrAcCrDXQ7NeoqGgDCZwBvWDcYmFCjSb9JtteuvPpz");
pub const USDC_VAULT: Pubkey = pubkey!("HLmqeL62xR1QoZ1HKKbXRrdN1p3phKpxRMb2VVopvBBz");
#[allow(dead_code)]
pub const MARKET_PROGRAM_ID: Pubkey = pubkey!("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX");
pub const MARKET: Pubkey = pubkey!("8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6");
pub const MARKET_AUTHORITY: Pubkey = pubkey!("CTz5UMLQm2SRWHzQnU62Pi4yJqbNGjgRBHqqp6oDHfF7");
pub const MARKET_BASE_VAULT: Pubkey = pubkey!("CKxTHwM9fPMRRvZmFnFoqKNd9pQR21c5Aq9bh5h9oghX");
pub const MARKET_QUOTE_VAULT: Pubkey = pubkey!("6A5NHCj1yF6urc9wZNe6Bcjj4LVszQNj5DwAWG97yzMu");
pub const MARKET_BIDS: Pubkey = pubkey!("5jWUncPNBMZJ3sTHKmMLszypVkoRK6bfEQMQUHweeQnh");
pub const MARKET_ASKS: Pubkey = pubkey!("EaXdHx7x3mdGA38j5RSmKYSXMzAFzzUXCLNBEDXDn1d5");
}
#[derive(Clone, Copy, Debug)]
enum Network {
Devnet,
Mainnet,
}
impl Network {
fn parse(s: &str) -> Result<Self, String> {
match s.trim().to_ascii_lowercase().as_str() {
"mainnet" | "mainnet-beta" => Ok(Network::Mainnet),
"devnet" => Ok(Network::Devnet),
other => Err(format!(
"network must be devnet or mainnet (or mainnet-beta), got `{other}`"
)),
}
}
fn from_env() -> Self {
let s = std::env::var("PUMP_NETWORK").unwrap_or_else(|_| "devnet".into());
Self::parse(&s).unwrap_or_else(|e| panic!("{e}"))
}
fn default_rpc(self) -> &'static str {
match self {
Network::Devnet => "https://api.devnet.solana.com",
Network::Mainnet => "https://api.mainnet-beta.solana.com",
}
}
fn alt(self) -> Pubkey {
match self {
Network::Devnet => constants::DEVNET_ALT,
Network::Mainnet => constants::MAINNET_ALT,
}
}
fn alt_label(self) -> &'static str {
match self {
Network::Devnet => "alt:devnet",
Network::Mainnet => "alt:mainnet",
}
}
}
struct Cli {
network: Option<Network>,
rpc_url: Option<String>,
}
fn print_usage() {
println!(
"\
clone_devnet_accounts — snapshot on-chain accounts for the local validator
Usage:
clone_devnet_accounts [OPTIONS] [RPC_URL]
Options:
-n, --network <NETWORK> devnet | mainnet (sets which ALT / fixtures apply).
Overrides PUMP_NETWORK.
-r, --rpc-url <URL> RPC HTTP endpoint. Overrides PUMP_CLONE_RPC.
-h, --help Print this help.
If RPC_URL is given as the first non-option argument, it is treated like --rpc-url.
Do not pass both --rpc-url and a positional RPC_URL.
RPC resolution: CLI (-r or positional) → PUMP_CLONE_RPC → public cluster RPC.
A .env file in the current directory is loaded when present.
Examples:
clone_devnet_accounts --network devnet
clone_devnet_accounts -r https://api.devnet.solana.com
clone_devnet_accounts https://api.mainnet-beta.solana.com --network mainnet
"
);
}
fn parse_cli() -> Result<Cli, String> {
let mut network = None;
let mut rpc_flag = None;
let mut positional = None;
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"-h" | "--help" => {
print_usage();
std::process::exit(0);
}
"-n" | "--network" => {
let v = args
.next()
.ok_or_else(|| "--network requires a value (devnet or mainnet)".to_string())?;
network = Some(Network::parse(&v)?);
}
"-r" | "--rpc-url" => {
let v = args
.next()
.ok_or_else(|| "--rpc-url requires a URL".to_string())?;
rpc_flag = Some(v);
}
s if s.starts_with('-') => {
return Err(format!("unknown option `{s}` (try --help)"));
}
s => {
if positional.is_some() {
return Err(format!("unexpected extra argument `{s}`"));
}
positional = Some(s.to_string());
}
}
}
if rpc_flag.is_some() && positional.is_some() {
return Err("use either --rpc-url or one positional RPC URL, not both".into());
}
Ok(Cli {
network,
rpc_url: rpc_flag.or(positional),
})
}
fn fixed_pdas(network: Network) -> Vec<(&'static str, Pubkey, bool, bool)> {
vec![
("pump:global", pda::pump::global().0, true, false),
(
"pump:event_authority",
pda::pump::event_authority().0,
false,
false,
),
(
"pump:mint_authority",
pda::pump::mint_authority().0,
false,
false,
),
(
"pump:global_volume_accumulator",
pda::pump::global_volume_accumulator().0,
true,
false,
),
("pump:fee_config", pda::pump::fee_config().0, true, false),
(
"pump_amm:global_config",
pda::pump_amm::global_config().0,
false,
false,
),
(
"pump_amm:event_authority",
pda::pump_amm::event_authority().0,
false,
false,
),
(
"pump_amm:global_volume_accumulator",
pda::pump_amm::global_volume_accumulator().0,
false,
false,
),
(
"pump_amm:fee_config",
pda::pump_amm::fee_config().0,
true,
false,
),
(
"pump_agent_payments:global_config",
pda::pump_agent_payments::global_config().0,
false,
false,
),
(
"mayhem:global_params",
pda::mayhem::global_params().0,
true,
false,
),
("mayhem:sol_vault", pda::mayhem::sol_vault().0, true, false),
(network.alt_label(), network.alt(), true, false),
(
"raydium_amm_v4:sol_usdc:pool",
raydium_sol_usdc::POOL,
true,
true,
),
(
"raydium_amm_v4:sol_usdc:amm_authority",
raydium_sol_usdc::AMM_AUTHORITY,
false,
true,
),
(
"raydium_amm_v4:sol_usdc:open_orders",
raydium_sol_usdc::OPEN_ORDERS,
true,
true,
),
(
"raydium_amm_v4:sol_usdc:target_orders",
raydium_sol_usdc::TARGET_ORDERS,
true,
true,
),
(
"raydium_amm_v4:sol_usdc:wsol_vault",
raydium_sol_usdc::WSOL_VAULT,
true,
true,
),
(
"raydium_amm_v4:sol_usdc:usdc_vault",
raydium_sol_usdc::USDC_VAULT,
true,
true,
),
(
"openbook:sol_usdc:market",
raydium_sol_usdc::MARKET,
true,
true,
),
(
"openbook:sol_usdc:market_authority",
raydium_sol_usdc::MARKET_AUTHORITY,
false,
true,
),
(
"openbook:sol_usdc:market_base_vault",
raydium_sol_usdc::MARKET_BASE_VAULT,
true,
true,
),
(
"openbook:sol_usdc:market_quote_vault",
raydium_sol_usdc::MARKET_QUOTE_VAULT,
true,
true,
),
(
"openbook:sol_usdc:market_bids",
raydium_sol_usdc::MARKET_BIDS,
true,
true,
),
(
"openbook:sol_usdc:market_asks",
raydium_sol_usdc::MARKET_ASKS,
true,
true,
),
(
"openbook:srm_mint",
solana_program::pubkey!("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"),
false,
true,
),
(
"openbook:msrm_mint",
solana_program::pubkey!("MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L"),
false,
true,
),
]
}
fn print_account(label: &str, key: &Pubkey, acct: &Account) {
println!(
" {:<40} {} lamports={} owner={} data={}B",
label,
key,
acct.lamports,
acct.owner,
acct.data.len()
);
}
fn clone_one(
rpc: &RpcClient,
out: &mut HashMap<Pubkey, Account>,
label: &str,
key: Pubkey,
required: bool,
) -> Result<Option<Account>, Box<dyn std::error::Error>> {
if let Some(existing) = out.get(&key) {
print_account(label, &key, existing);
return Ok(Some(existing.clone()));
}
let acct = rpc
.get_account_with_commitment(&key, rpc.commitment())?
.value;
match acct {
Some(acct) => {
print_account(label, &key, &acct);
out.insert(key, acct.clone());
Ok(Some(acct))
}
None if required => {
panic!("required fixture account `{label}` missing on cluster at {key}")
}
None => {
println!(" {:<40} {} (not on cluster — skipped)", label, key);
Ok(None)
}
}
}
fn clone_fixture_mint(
rpc: &RpcClient,
out: &mut HashMap<Pubkey, Account>,
fixture: &FixtureMint,
) -> Result<(), Box<dyn std::error::Error>> {
let mint = fixture.mint;
println!("📥 Fixture {} ({}):", fixture.label, mint);
clone_one(rpc, out, " mint", mint, true)?;
let bonding_curve_key = pda::pump::bonding_curve(&mint).0;
let bc_account = clone_one(rpc, out, " bonding_curve", bonding_curve_key, true)?
.expect("bonding_curve required entry returned None");
let mut bc = decode_bonding_curve(&bc_account.data)?;
let original_bc_quote_mint = bc.quote_mint;
if fixture.patch_quote_mint_to_test && bc.quote_mint != fixtures::USDC_QUOTE_MINT {
let original_quote_mint = bc.quote_mint;
bc.quote_mint = fixtures::USDC_QUOTE_MINT;
let mut new_data = Vec::new();
bc.try_serialize(&mut new_data)?;
let entry = out
.get_mut(&bonding_curve_key)
.expect("bonding_curve must be present in `out` before patching");
if new_data.len() < entry.data.len() {
new_data.resize(entry.data.len(), 0);
}
entry.data = new_data;
println!(
"🛠 Patched bonding_curve.quote_mint -> {} for {}",
fixtures::USDC_QUOTE_MINT,
fixture.label
);
let original_quote_ata_key = pda::associated_token(
&bonding_curve_key,
&constants::SPL_TOKEN_PROGRAM_ID,
&original_quote_mint,
)
.0;
let new_quote_ata_key = pda::associated_token(
&bonding_curve_key,
&constants::SPL_TOKEN_PROGRAM_ID,
&fixtures::USDC_QUOTE_MINT,
)
.0;
let source = clone_one(
rpc,
out,
" bonding_curve_quote_ata (source)",
original_quote_ata_key,
false,
)?;
match source {
Some(mut acct) => {
let mut token = spl_token::state::Account::unpack(&acct.data)?;
token.mint = fixtures::USDC_QUOTE_MINT;
let mut new_data = vec![0u8; spl_token::state::Account::LEN];
spl_token::state::Account::pack(token, &mut new_data)?;
acct.data = new_data;
out.insert(new_quote_ata_key, acct);
out.remove(&original_quote_ata_key);
println!(
"🛠 Re-keyed bonding curve quote ATA {} -> {} (Token.mint -> {})",
original_quote_ata_key,
new_quote_ata_key,
fixtures::USDC_QUOTE_MINT
);
}
None => {
println!(
"ℹ️ No quote ATA on cluster at {} — skipping re-key (mint-to from USDC_QUOTE_MINT_AUTHORITY in tests to seed reserves)",
original_quote_ata_key
);
}
}
}
let creator_vault = pda::pump::creator_vault(&bc.creator).0;
clone_one(rpc, out, " creator_vault", creator_vault, false)?;
{
let on_chain_quote_mint = if original_bc_quote_mint == Pubkey::default() {
constants::NATIVE_MINT
} else {
original_bc_quote_mint
};
let patched_quote_mint = if bc.quote_mint == Pubkey::default() {
constants::NATIVE_MINT
} else {
bc.quote_mint
};
let original_cv_quote_ata = pda::associated_token(
&creator_vault,
&constants::SPL_TOKEN_PROGRAM_ID,
&on_chain_quote_mint,
)
.0;
let new_cv_quote_ata = pda::associated_token(
&creator_vault,
&constants::SPL_TOKEN_PROGRAM_ID,
&patched_quote_mint,
)
.0;
let source = clone_one(
rpc,
out,
" creator_vault_quote_ata (source)",
original_cv_quote_ata,
false,
)?;
if patched_quote_mint != on_chain_quote_mint {
if let Some(mut acct) = source {
let mut token = spl_token::state::Account::unpack(&acct.data)?;
token.mint = patched_quote_mint;
let mut new_data = vec![0u8; spl_token::state::Account::LEN];
spl_token::state::Account::pack(token, &mut new_data)?;
acct.data = new_data;
out.insert(new_cv_quote_ata, acct);
out.remove(&original_cv_quote_ata);
println!(
"🛠 Re-keyed creator_vault_quote_ata {} -> {} (Token.mint -> {})",
original_cv_quote_ata, new_cv_quote_ata, patched_quote_mint
);
} else {
println!(
"ℹ️ creator_vault_quote_ata not on cluster at {} — skipped re-key",
original_cv_quote_ata
);
}
}
}
let sharing_config_key = pda::pump::sharing_config(&mint).0;
if let Some(sharing_acct) = clone_one(rpc, out, " sharing_config", sharing_config_key, false)?
{
let quote_mint = if bc.quote_mint == Pubkey::default() {
constants::NATIVE_MINT
} else {
bc.quote_mint
};
let quote_token_program = out
.get("e_mint)
.map(|a| a.owner)
.unwrap_or(constants::SPL_TOKEN_PROGRAM_ID);
match decode_sharing_config(&sharing_acct.data) {
Ok(sc) => {
println!(" {} shareholder(s)", sc.shareholders.len());
for (i, sh) in sc.shareholders.iter().enumerate() {
clone_one(
rpc,
out,
&format!(" shareholder[{i}]"),
sh.address,
false,
)?;
clone_one(
rpc,
out,
&format!(" shareholder[{i}]_quote_ata"),
pda::associated_token(&sh.address, "e_token_program, "e_mint).0,
false,
)?;
}
}
Err(e) => println!(" sharing_config decode failed: {e}"),
}
}
clone_one(
rpc,
out,
" bonding_curve_v2",
pda::pump::bonding_curve_v2(&mint).0,
false,
)?;
clone_one(
rpc,
out,
" bonding_curve_base_ata",
pda::associated_token(
&bonding_curve_key,
&constants::SPL_TOKEN_2022_PROGRAM_ID,
&mint,
)
.0,
false,
)?;
clone_one(
rpc,
out,
" bonding_curve_wsol_ata",
pda::associated_token(
&bonding_curve_key,
&constants::SPL_TOKEN_PROGRAM_ID,
&constants::NATIVE_MINT,
)
.0,
false,
)?;
if bc.complete {
let pool_creator = pda::pump::pool_authority(&mint).0;
let on_chain_quote_mint = if original_bc_quote_mint == Pubkey::default() {
constants::NATIVE_MINT
} else {
original_bc_quote_mint
};
let patched_quote_mint = if bc.quote_mint == Pubkey::default() {
constants::NATIVE_MINT
} else {
bc.quote_mint
};
let original_pool_key =
pda::pump_amm::pool(0, &pool_creator, &mint, &on_chain_quote_mint).0;
let new_pool_key = pda::pump_amm::pool(0, &pool_creator, &mint, &patched_quote_mint).0;
let pool_account = clone_one(rpc, out, " pool (source)", original_pool_key, false)?;
if let Some(mut pool_account) = pool_account {
let mut pool = decode_pool(&pool_account.data)?;
let original_pool_quote_ata = pool.pool_quote_token_account;
clone_one(rpc, out, " lp_mint", pool.lp_mint, true)?;
clone_one(
rpc,
out,
" pool_base_token_account",
pool.pool_base_token_account,
true,
)?;
clone_one(
rpc,
out,
" pool_quote_token_account",
pool.pool_quote_token_account,
true,
)?;
let cc_vault_authority =
pda::pump_amm::coin_creator_vault_authority(&pool.coin_creator).0;
clone_one(
rpc,
out,
" coin_creator_vault_authority",
cc_vault_authority,
false,
)?;
clone_one(
rpc,
out,
" coin_creator_vault_quote_ata",
pda::associated_token(
&cc_vault_authority,
&constants::SPL_TOKEN_PROGRAM_ID,
&constants::NATIVE_MINT,
)
.0,
false,
)?;
if patched_quote_mint != on_chain_quote_mint {
let new_pool_quote_ata = pda::associated_token(
&new_pool_key,
&constants::SPL_TOKEN_PROGRAM_ID,
&patched_quote_mint,
)
.0;
pool.quote_mint = patched_quote_mint;
pool.pool_quote_token_account = new_pool_quote_ata;
let mut new_pool_data = Vec::new();
pool.try_serialize(&mut new_pool_data)?;
if new_pool_data.len() < pool_account.data.len() {
new_pool_data.resize(pool_account.data.len(), 0);
}
pool_account.data = new_pool_data;
out.insert(new_pool_key, pool_account);
out.remove(&original_pool_key);
println!(
"🛠 Re-keyed pool {} -> {} (quote_mint -> {})",
original_pool_key, new_pool_key, patched_quote_mint
);
if let Some(mut quote_ata_acct) = out.remove(&original_pool_quote_ata) {
let mut token = spl_token::state::Account::unpack("e_ata_acct.data)?;
token.mint = patched_quote_mint;
let mut new_data = vec![0u8; spl_token::state::Account::LEN];
spl_token::state::Account::pack(token, &mut new_data)?;
quote_ata_acct.data = new_data;
out.insert(new_pool_quote_ata, quote_ata_acct);
println!(
"🛠 Re-keyed pool_quote_token_account {} -> {} (Token.mint -> {})",
original_pool_quote_ata, new_pool_quote_ata, patched_quote_mint
);
} else {
println!(
"ℹ️ pool_quote_token_account {} missing from snapshot — skipped re-key",
original_pool_quote_ata
);
}
}
}
}
Ok(())
}
fn whitelist_test_quote_mint_in_global(
out: &mut HashMap<Pubkey, Account>,
global: &Global,
) -> Result<(), Box<dyn std::error::Error>> {
if global
.whitelisted_quote_mints
.contains(&fixtures::USDC_QUOTE_MINT)
{
println!(
"🛠 Quote mint {} already whitelisted in Global — skipping",
fixtures::USDC_QUOTE_MINT
);
return Ok(());
}
let mut patched = global.clone();
patched.whitelisted_quote_mints[0] = fixtures::USDC_QUOTE_MINT;
let mut new_data = Vec::new();
patched.try_serialize(&mut new_data)?;
let key = pda::pump::global().0;
let entry = out
.get_mut(&key)
.expect("pump:global must be present in `out` before patching");
assert_eq!(
entry.data.len(),
new_data.len(),
"Global re-serialization changed data length ({} -> {}); IDL drift?",
entry.data.len(),
new_data.len()
);
entry.data = new_data;
println!(
"🛠 Whitelisted quote mint {} in Global",
fixtures::USDC_QUOTE_MINT
);
Ok(())
}
fn synthesize_quote_mint(out: &mut HashMap<Pubkey, Account>) {
let mint = spl_token::state::Mint {
mint_authority: solana_program::program_option::COption::Some(
fixtures::USDC_QUOTE_MINT_AUTHORITY,
),
supply: 0,
decimals: 6,
is_initialized: true,
freeze_authority: solana_program::program_option::COption::None,
};
let mut data = vec![0u8; spl_token::state::Mint::LEN];
spl_token::state::Mint::pack(mint, &mut data).expect("pack test quote Mint");
let acct = Account {
lamports: Rent::default().minimum_balance(spl_token::state::Mint::LEN),
data,
owner: spl_token::ID,
executable: false,
rent_epoch: 0,
};
out.insert(fixtures::USDC_QUOTE_MINT, acct);
println!(
"🛠 Synthesized test quote mint at {} (authority {})",
fixtures::USDC_QUOTE_MINT,
fixtures::USDC_QUOTE_MINT_AUTHORITY
);
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = dotenvy::dotenv();
let cli =
parse_cli().map_err(|msg| std::io::Error::new(std::io::ErrorKind::InvalidInput, msg))?;
let network = cli.network.unwrap_or_else(Network::from_env);
let rpc_url = cli
.rpc_url
.or_else(|| std::env::var("PUMP_CLONE_RPC").ok())
.unwrap_or_else(|| network.default_rpc().to_string());
let rpc = RpcClient::new(rpc_url.clone());
println!("🌐 Cloning {network:?} from: {rpc_url}");
let labeled = fixed_pdas(network);
let needs_mainnet = labeled.iter().any(|(_, _, _, from_mainnet)| *from_mainnet);
let mainnet_rpc: Option<RpcClient> = if needs_mainnet && !matches!(network, Network::Mainnet) {
let mainnet_url = std::env::var("PUMP_CLONE_MAINNET_RPC")
.unwrap_or_else(|_| Network::Mainnet.default_rpc().to_string());
println!("🌐 Mainnet override RPC: {mainnet_url}");
Some(RpcClient::new(mainnet_url))
} else {
None
};
let default_keys: Vec<Pubkey> = labeled
.iter()
.filter(|(_, _, _, from_mainnet)| !*from_mainnet)
.map(|(_, k, _, _)| *k)
.collect();
let mainnet_keys: Vec<Pubkey> = labeled
.iter()
.filter(|(_, _, _, from_mainnet)| *from_mainnet)
.map(|(_, k, _, _)| *k)
.collect();
let mut by_key: HashMap<Pubkey, Option<Account>> = HashMap::new();
if !default_keys.is_empty() {
println!(
"📡 Fetching {} key(s) from {network:?} ({rpc_url})",
default_keys.len()
);
for (k, a) in default_keys
.iter()
.copied()
.zip(rpc.get_multiple_accounts(&default_keys)?.into_iter())
{
by_key.insert(k, a);
}
}
if !mainnet_keys.is_empty() {
let client = mainnet_rpc.as_ref().unwrap_or(&rpc);
println!(
"📡 Fetching {} key(s) from mainnet override",
mainnet_keys.len()
);
for (k, a) in mainnet_keys
.iter()
.copied()
.zip(client.get_multiple_accounts(&mainnet_keys)?.into_iter())
{
by_key.insert(k, a);
}
}
let mut out: HashMap<Pubkey, Account> = HashMap::new();
let mut global_account: Option<Account> = None;
println!("📥 Fixed PDAs:");
for (label, key, required, from_mainnet) in labeled.iter() {
let suffix = if *from_mainnet { " (mainnet)" } else { "" };
match by_key.remove(key).flatten() {
Some(acct) => {
print_account(&format!("{label}{suffix}"), key, &acct);
if *label == "pump:global" {
global_account = Some(acct.clone());
}
out.insert(*key, acct);
}
None if *required => {
let source = if *from_mainnet {
"mainnet".to_string()
} else {
format!("{network:?}")
};
panic!("required account `{label}` missing on {source} at {key}");
}
None => {
println!(
" {:<40} {} (not on cluster — skipped)",
format!("{label}{suffix}"),
key
);
}
}
}
let global = decode_global(&global_account.expect("pump:global must be set above").data)?;
whitelist_test_quote_mint_in_global(&mut out, &global)?;
let mut recipients: Vec<Pubkey> = Vec::new();
recipients.push(global.fee_recipient);
recipients.extend(global.fee_recipients.iter().copied());
recipients.push(global.reserved_fee_recipient);
recipients.extend(global.reserved_fee_recipients.iter().copied());
recipients.extend(global.buyback_fee_recipients.iter().copied());
recipients.retain(|p| *p != Pubkey::default());
recipients.sort();
recipients.dedup();
println!(
"🔎 Global yielded {} distinct fee/buyback recipient(s)",
recipients.len()
);
if !recipients.is_empty() {
let recipient_accounts = rpc.get_multiple_accounts(&recipients)?;
println!("📥 Recipients:");
for (key, maybe_acct) in recipients.iter().zip(recipient_accounts.into_iter()) {
let acct = maybe_acct.unwrap_or_else(|| Account {
lamports: 0,
data: vec![],
owner: system_program::ID,
executable: false,
rent_epoch: 0,
});
print_account("recipient", key, &acct);
out.entry(*key).or_insert(acct);
}
}
for fixture in FIXTURE_MINTS {
clone_fixture_mint(&rpc, &mut out, fixture)?;
}
synthesize_quote_mint(&mut out);
let bytes = bincode::serialize(&out)?;
let compressed = zstd::stream::encode_all(&bytes[..], 3)?;
if let Some(parent) = std::path::Path::new(OUT_PATH).parent() {
fs::create_dir_all(parent)?;
}
fs::write(OUT_PATH, &compressed)?;
println!(
"✅ Wrote {} accounts to {} ({} bytes raw, {} bytes zstd)",
out.len(),
OUT_PATH,
bytes.len(),
compressed.len()
);
Ok(())
}