394 lines
12 KiB
Rust
Executable File
394 lines
12 KiB
Rust
Executable File
use solana_program::{
|
|
address_lookup_table::{
|
|
instruction::{
|
|
create_lookup_table as create_lookup_table_instruction,
|
|
extend_lookup_table as extend_lookup_table_instruction,
|
|
freeze_lookup_table as freeze_lookup_table_instruction
|
|
},
|
|
state::AddressLookupTable,
|
|
},
|
|
instruction::Instruction,
|
|
pubkey::Pubkey,
|
|
};
|
|
use solana_sdk::{
|
|
message::{v0::Message as MessageV0, AddressLookupTableAccount, VersionedMessage},
|
|
signature::{Keypair, Signer},
|
|
transaction::{Transaction, VersionedTransaction},
|
|
};
|
|
use std::{error::Error, sync::Arc};
|
|
|
|
use crate::{common::SolanaRpcClient, constants};
|
|
|
|
/// 创建地址查找表(如果不存在)
|
|
pub async fn create_lookup_table_if_not_exists(
|
|
client: Arc<SolanaRpcClient>,
|
|
authority: &Keypair,
|
|
payer: &Keypair,
|
|
) -> Result<Pubkey, Box<dyn std::error::Error>> {
|
|
// 1. 计算预期的查找表地址
|
|
let recent_slot = client.get_slot().await?;
|
|
let (create_ix, lookup_table_address) = create_lookup_table_instruction(
|
|
authority.pubkey(),
|
|
payer.pubkey(),
|
|
recent_slot
|
|
);
|
|
|
|
// 2. 创建新表
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[create_ix],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
blockhash,
|
|
);
|
|
|
|
client.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
Ok(lookup_table_address)
|
|
}
|
|
|
|
/// 向查找表添加地址
|
|
pub async fn extend_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
payer: &Keypair,
|
|
authority: &Keypair,
|
|
lookup_table_address: &Pubkey,
|
|
addresses: Vec<Pubkey>,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let extend_ix = extend_lookup_table_instruction(
|
|
*lookup_table_address,
|
|
authority.pubkey(),
|
|
Some(payer.pubkey()),
|
|
addresses.clone(),
|
|
);
|
|
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[extend_ix],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
blockhash,
|
|
);
|
|
|
|
client.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 冻结查找表,防止进一步修改
|
|
pub async fn freeze_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
payer: &Keypair,
|
|
authority: &Keypair,
|
|
lookup_table_address: &Pubkey,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let freeze_ix = freeze_lookup_table_instruction(
|
|
*lookup_table_address,
|
|
authority.pubkey(),
|
|
);
|
|
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[freeze_ix],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
blockhash,
|
|
);
|
|
|
|
client.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取查找表信息
|
|
pub async fn get_address_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
lookup_table_address: &Pubkey,
|
|
) -> Result<AddressLookupTableAccount, Box<dyn Error>> {
|
|
let account = client.get_account(lookup_table_address).await?;
|
|
let lookup_table = AddressLookupTable::deserialize(&account.data)?;
|
|
|
|
let address_lookup_table_account = AddressLookupTableAccount {
|
|
key: *lookup_table_address,
|
|
addresses: lookup_table.addresses.to_vec(),
|
|
};
|
|
|
|
for (i, addr) in address_lookup_table_account.addresses.iter().enumerate() {
|
|
println!("地址 {}: {}", i, addr);
|
|
}
|
|
|
|
Ok(address_lookup_table_account)
|
|
}
|
|
|
|
/// 使用查找表发送交易
|
|
pub async fn send_transaction_with_lut(
|
|
client: Arc<SolanaRpcClient>,
|
|
instructions: Vec<Instruction>,
|
|
payer: &Keypair,
|
|
signers: Vec<&Keypair>,
|
|
address_lookup_tables: Vec<AddressLookupTableAccount>,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
|
|
let message = VersionedMessage::V0(MessageV0::try_compile(
|
|
&payer.pubkey(),
|
|
&instructions,
|
|
&address_lookup_tables,
|
|
blockhash,
|
|
)?);
|
|
|
|
let tx = VersionedTransaction::try_new(message, &signers)?;
|
|
|
|
let signature = client.send_and_confirm_transaction(&tx).await?;
|
|
|
|
println!("交易已确认: {}", signature);
|
|
Ok(())
|
|
}
|
|
|
|
/// 使用查找表的特定地址子集发送交易
|
|
pub async fn send_transaction_with_filtered_lut(
|
|
client: Arc<SolanaRpcClient>,
|
|
instructions: Vec<Instruction>,
|
|
payer: &Keypair,
|
|
signers: Vec<&Keypair>,
|
|
lookup_table: AddressLookupTableAccount,
|
|
address_indices_to_use: &[usize], // 要使用的地址索引列表
|
|
) -> Result<(), Box<dyn Error>> {
|
|
// 创建只包含选定地址的新查找表账户
|
|
let filtered_addresses: Vec<Pubkey> = address_indices_to_use
|
|
.iter()
|
|
.filter_map(|&index| lookup_table.addresses.get(index).copied())
|
|
.collect();
|
|
|
|
println!(
|
|
"从查找表中选择了 {} 个地址用于交易",
|
|
filtered_addresses.len()
|
|
);
|
|
for (i, addr) in filtered_addresses.iter().enumerate() {
|
|
println!("使用地址 {}: {}", i, addr);
|
|
}
|
|
|
|
let filtered_lookup_table = AddressLookupTableAccount {
|
|
key: lookup_table.key,
|
|
addresses: filtered_addresses,
|
|
};
|
|
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
|
|
let message = VersionedMessage::V0(MessageV0::try_compile(
|
|
&payer.pubkey(),
|
|
&instructions,
|
|
&[filtered_lookup_table],
|
|
blockhash,
|
|
)?);
|
|
|
|
let tx = VersionedTransaction::try_new(message, &signers)?;
|
|
|
|
let signature = client.send_and_confirm_transaction(&tx).await?;
|
|
|
|
println!("交易已确认: {}", signature);
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取最近的区块槽位,用于创建查找表
|
|
pub async fn get_recent_slot(client: Arc<SolanaRpcClient>) -> Result<u64, Box<dyn Error>> {
|
|
let slot = client.get_slot().await?;
|
|
Ok(slot)
|
|
}
|
|
|
|
/// 使用指定的地址列表发送交易
|
|
///
|
|
/// 这个方法接受一组目标地址,自动查找它们在查找表中的索引,
|
|
/// 然后使用这些地址创建一个过滤后的查找表来发送交易
|
|
///
|
|
/// # 参数
|
|
/// * `instructions` - 交易指令
|
|
/// * `payer` - 支付交易费用的账户
|
|
/// * `signers` - 交易签名者
|
|
/// * `lookup_table` - 地址查找表
|
|
/// * `addresses_to_use` - 要使用的地址列表
|
|
///
|
|
/// # 返回值
|
|
/// 成功返回交易签名,失败返回错误
|
|
pub async fn send_transaction_with_addresses(
|
|
client: Arc<SolanaRpcClient>,
|
|
instructions: Vec<Instruction>,
|
|
payer: &Keypair,
|
|
signers: Vec<&Keypair>,
|
|
lookup_table: AddressLookupTableAccount,
|
|
addresses_to_use: &[Pubkey],
|
|
) -> Result<String, Box<dyn Error>> {
|
|
// 构建地址到索引的映射
|
|
let mut address_to_index = std::collections::HashMap::new();
|
|
for (i, addr) in lookup_table.addresses.iter().enumerate() {
|
|
address_to_index.insert(*addr, i);
|
|
}
|
|
|
|
// 查找所有存在的地址的索引
|
|
let mut indices_to_use = Vec::new();
|
|
let mut found_addresses = Vec::new();
|
|
let mut missing_addresses = Vec::new();
|
|
|
|
for addr in addresses_to_use {
|
|
if let Some(&index) = address_to_index.get(addr) {
|
|
indices_to_use.push(index);
|
|
found_addresses.push(*addr);
|
|
} else {
|
|
missing_addresses.push(*addr);
|
|
}
|
|
}
|
|
|
|
// 检查是否有地址未找到
|
|
if !missing_addresses.is_empty() {
|
|
println!("警告: {} 个地址未在查找表中找到", missing_addresses.len());
|
|
for (i, addr) in missing_addresses.iter().enumerate() {
|
|
println!("未找到的地址 {}: {}", i, addr);
|
|
}
|
|
}
|
|
|
|
// 如果没有找到任何地址,返回错误
|
|
if indices_to_use.is_empty() {
|
|
return Err(Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::NotFound,
|
|
"没有在查找表中找到任何指定的地址",
|
|
)));
|
|
}
|
|
|
|
// 创建只包含选定地址的新查找表账户
|
|
let filtered_addresses: Vec<Pubkey> = indices_to_use
|
|
.iter()
|
|
.filter_map(|&index| lookup_table.addresses.get(index).copied())
|
|
.collect();
|
|
|
|
println!(
|
|
"从查找表中选择了 {} 个地址用于交易",
|
|
filtered_addresses.len()
|
|
);
|
|
for (i, addr) in filtered_addresses.iter().enumerate() {
|
|
println!("使用地址 {}: {}", i, addr);
|
|
}
|
|
|
|
let filtered_lookup_table = AddressLookupTableAccount {
|
|
key: lookup_table.key,
|
|
addresses: filtered_addresses,
|
|
};
|
|
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
|
|
let message = VersionedMessage::V0(MessageV0::try_compile(
|
|
&payer.pubkey(),
|
|
&instructions,
|
|
&[filtered_lookup_table],
|
|
blockhash,
|
|
)?);
|
|
|
|
let tx = VersionedTransaction::try_new(message, &signers)?;
|
|
|
|
let signature = client.send_and_confirm_transaction(&tx).await?;
|
|
|
|
println!("交易已确认: {}", signature);
|
|
Ok(signature.to_string())
|
|
}
|
|
|
|
pub async fn create_pumpfun_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
payer: &Keypair,
|
|
authority: &Keypair,
|
|
) -> Result<Pubkey, Box<dyn Error>> {
|
|
let recent_slot = client.get_slot().await?;
|
|
let (create_ix, lookup_table_address) =
|
|
create_lookup_table_instruction(authority.pubkey(), payer.pubkey(), recent_slot);
|
|
|
|
let blockhash = client.get_latest_blockhash().await?;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[create_ix],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
blockhash,
|
|
);
|
|
|
|
client.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
Ok(lookup_table_address)
|
|
}
|
|
|
|
pub async fn add_pumpfun_address_to_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
payer: &Keypair,
|
|
authority: &Keypair,
|
|
lookup_table_address: &Pubkey,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let addresses = get_pumpfun_addresses(payer.pubkey(), vec![]);
|
|
extend_lookup_table(
|
|
client,
|
|
payer,
|
|
authority,
|
|
lookup_table_address,
|
|
addresses
|
|
).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn extend_pumpfun_address_to_lookup_table(
|
|
client: Arc<SolanaRpcClient>,
|
|
payer: &Keypair,
|
|
authority: &Keypair,
|
|
lookup_table_address: &Pubkey,
|
|
addresses: Vec<Pubkey>,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
extend_lookup_table(
|
|
client,
|
|
payer,
|
|
authority,
|
|
lookup_table_address,
|
|
addresses
|
|
).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get_pumpfun_addresses(payer: Pubkey, include_addresses: Vec<Pubkey>) -> Vec<Pubkey> {
|
|
let mut addresses = vec![
|
|
payer,
|
|
constants::pumpfun::accounts::PUMPFUN,
|
|
constants::pumpfun::accounts::SYSTEM_PROGRAM,
|
|
constants::pumpfun::accounts::TOKEN_PROGRAM,
|
|
constants::pumpfun::accounts::RENT,
|
|
constants::pumpfun::accounts::EVENT_AUTHORITY,
|
|
constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM,
|
|
constants::pumpfun::global_constants::GLOBAL_ACCOUNT,
|
|
constants::pumpfun::global_constants::FEE_RECIPIENT,
|
|
];
|
|
|
|
addresses.extend(include_addresses);
|
|
|
|
addresses
|
|
}
|
|
|
|
pub fn get_pumpfun_filtered_addresses(payer: Pubkey, include_addresses: Vec<Pubkey>) -> Vec<Pubkey> {
|
|
let mut addresses = vec![
|
|
payer,
|
|
constants::pumpfun::accounts::PUMPFUN,
|
|
constants::pumpfun::accounts::SYSTEM_PROGRAM,
|
|
constants::pumpfun::accounts::TOKEN_PROGRAM,
|
|
constants::pumpfun::accounts::RENT,
|
|
constants::pumpfun::accounts::EVENT_AUTHORITY,
|
|
constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM,
|
|
constants::pumpfun::global_constants::GLOBAL_ACCOUNT,
|
|
constants::pumpfun::global_constants::FEE_RECIPIENT,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_1,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_2,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_3,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_4,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_5,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_6,
|
|
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_7,
|
|
// constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_8,
|
|
];
|
|
|
|
addresses.extend(include_addresses);
|
|
|
|
addresses
|
|
}
|