#![allow(dead_code)]
use std::sync::Arc;
use std::time::Duration;
use anchor_spl::associated_token::spl_associated_token_account::instruction::create_associated_token_account_idempotent;
use anchor_spl::token::spl_token::instruction::{close_account, sync_native};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::address_lookup_table::state::AddressLookupTable;
use solana_sdk::address_lookup_table::AddressLookupTableAccount;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::compute_budget::ComputeBudgetInstruction;
use solana_sdk::instruction::Instruction;
use solana_sdk::message::{v0, VersionedMessage};
use solana_sdk::native_token::LAMPORTS_PER_SOL;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signature, Signer};
use solana_sdk::system_instruction;
use solana_sdk::transaction::{Transaction, VersionedTransaction};
use pump_rust_client::{constants, pda, AsyncPumpClient};
pub const LOCAL_RPC: &str = "http://127.0.0.1:8899";
pub fn make_rpc() -> Arc<RpcClient> {
Arc::new(RpcClient::new_with_commitment(
LOCAL_RPC.to_string(),
CommitmentConfig::confirmed(),
))
}
pub fn make_client() -> AsyncPumpClient {
AsyncPumpClient::new(make_rpc())
}
pub fn pick_first_nonzero(xs: &[Pubkey]) -> Option<Pubkey> {
xs.iter().copied().find(|p| *p != Pubkey::default())
}
pub async fn fee_recipients(client: &AsyncPumpClient) -> (Pubkey, Pubkey) {
let global = client.fetch_global().await.expect(
"fetch_global on local validator — did you run clone_devnet_accounts and start the validator?",
);
let fee_recipient = pick_first_nonzero(&[global.fee_recipient])
.or_else(|| pick_first_nonzero(&global.fee_recipients))
.expect("Global has no fee_recipient set");
let buyback_fee_recipient = pick_first_nonzero(&global.buyback_fee_recipients)
.expect("Global has no buyback_fee_recipients[] entry set");
(fee_recipient, buyback_fee_recipient)
}
pub async fn airdrop_blocking(rpc: &RpcClient, to: &Pubkey, lamports: u64) {
let sig = rpc
.request_airdrop(to, lamports)
.await
.expect("request_airdrop");
let deadline = std::time::Instant::now() + Duration::from_secs(30);
loop {
if rpc.confirm_transaction(&sig).await.unwrap_or(false) {
break;
}
assert!(
std::time::Instant::now() < deadline,
"airdrop did not confirm within 30s"
);
tokio::time::sleep(Duration::from_millis(500)).await;
}
let bal = rpc.get_balance(to).await.expect("get_balance");
assert!(bal >= lamports, "airdrop confirmed but balance={bal}");
}
pub async fn funded_user(rpc: &RpcClient, lamports: u64) -> Keypair {
let user = Keypair::new();
airdrop_blocking(rpc, &user.pubkey(), lamports).await;
user
}
pub async fn load_alt(rpc: &RpcClient, key: Pubkey) -> AddressLookupTableAccount {
let acct = rpc
.get_account(&key)
.await
.expect("ALT account missing on local validator — re-run clone_devnet_accounts");
let parsed = AddressLookupTable::deserialize(&acct.data)
.expect("deserialize ALT — was the cloned ALT properly re-serialized for tests?");
AddressLookupTableAccount {
key,
addresses: parsed.addresses.into_owned(),
}
}
pub async fn send_v0_tx(
rpc: &RpcClient,
ixs: &[Instruction],
payer: &Keypair,
signers: &[&Keypair],
alt: &AddressLookupTableAccount,
) -> Signature {
let blockhash = rpc.get_latest_blockhash().await.expect("latest_blockhash");
let msg = v0::Message::try_compile(&payer.pubkey(), ixs, std::slice::from_ref(alt), blockhash)
.expect("compile v0 message");
let signers_dyn: Vec<&dyn Signer> = signers.iter().map(|kp| *kp as &dyn Signer).collect();
let tx = VersionedTransaction::try_new(VersionedMessage::V0(msg), &signers_dyn)
.expect("sign versioned tx");
let size = bincode::serialize(&tx).expect("serialize v0 tx").len();
println!("v0 tx size: {size} bytes (limit 1232)");
rpc.send_and_confirm_transaction(&tx)
.await
.expect("send_and_confirm_transaction — check local-validator logs for program error")
}
pub fn user_wsol_ata(user: &Pubkey) -> Pubkey {
pda::associated_token(
user,
&constants::SPL_TOKEN_PROGRAM_ID,
&constants::NATIVE_MINT,
)
.0
}
pub fn unwrap_sol_ix(user: &Pubkey) -> Instruction {
close_account(
&constants::SPL_TOKEN_PROGRAM_ID,
&user_wsol_ata(user),
user,
user,
&[],
)
.expect("close_account: token program id is constant")
}
pub async fn build_wsol_setup_tx(
rpc: &RpcClient,
user: &Keypair,
bonding_curve: Pubkey,
buyback_fee_recipient: Pubkey,
wrap_lamports: u64,
) -> Transaction {
let quote_mint = constants::NATIVE_MINT;
let quote_token_program = constants::SPL_TOKEN_PROGRAM_ID;
let user_ata = user_wsol_ata(&user.pubkey());
let ixs = vec![
ComputeBudgetInstruction::set_compute_unit_limit(200_000),
create_associated_token_account_idempotent(
&user.pubkey(),
&bonding_curve,
"e_mint,
"e_token_program,
),
create_associated_token_account_idempotent(
&user.pubkey(),
&user.pubkey(),
"e_mint,
"e_token_program,
),
create_associated_token_account_idempotent(
&user.pubkey(),
&buyback_fee_recipient,
"e_mint,
"e_token_program,
),
system_instruction::transfer(&user.pubkey(), &user_ata, wrap_lamports),
sync_native("e_token_program, &user_ata).expect("sync_native"),
];
let blockhash = rpc.get_latest_blockhash().await.expect("latest_blockhash");
Transaction::new_signed_with_payer(&ixs, Some(&user.pubkey()), &[user], blockhash)
}
pub const DEFAULT_USER_LAMPORTS: u64 = 50 * LAMPORTS_PER_SOL;