solagent_plugin_solana/
close_empty_token_accounts.rsuse serde::{Deserialize, Serialize};
use solagent_core::{
solana_client::rpc_request::TokenAccountsFilter,
solana_sdk::{instruction::Instruction, pubkey::Pubkey, transaction::Transaction},
SolanaAgentKit,
};
use spl_token::instruction::close_account;
pub const USDC: &str = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
#[derive(serde::Deserialize)]
pub struct Parsed {
pub info: SplToken,
}
#[derive(serde::Deserialize)]
pub struct SplToken {
pub mint: String,
#[serde(rename(deserialize = "tokenAmount"))]
pub token_amount: Amount,
}
#[allow(dead_code)]
#[derive(serde::Deserialize)]
pub struct Amount {
pub amount: String,
#[serde(rename(deserialize = "uiAmountString"))]
ui_amount_string: String,
#[serde(rename(deserialize = "uiAmount"))]
pub ui_amount: f64,
pub decimals: u8,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct CloseEmptyTokenAccountsData {
pub signature: String,
pub closed_size: usize,
}
impl CloseEmptyTokenAccountsData {
pub fn new(signature: String, closed_size: usize) -> Self {
CloseEmptyTokenAccountsData { signature, closed_size }
}
}
pub async fn close_empty_token_accounts(
agent: &SolanaAgentKit,
) -> Result<CloseEmptyTokenAccountsData, Box<dyn std::error::Error>> {
let max_instructions = 40_u32;
let mut transaction: Vec<Instruction> = vec![];
let mut closed_size = 0;
let token_programs = vec![spl_token::ID, spl_token_2022::ID];
for token_program in token_programs {
let accounts = agent
.connection
.get_token_accounts_by_owner(
&agent.wallet.address,
TokenAccountsFilter::ProgramId(token_program.to_owned()),
)
.expect("get_token_accounts_by_owner");
closed_size += accounts.len();
for account in accounts {
if transaction.len() >= max_instructions as usize {
break;
}
if let solana_account_decoder::UiAccountData::Json(d) = &account.account.data {
if let Ok(parsed) = serde_json::from_value::<Parsed>(d.parsed.clone()) {
if parsed.info.token_amount.amount.parse::<u32>().unwrap_or_default() == 0_u32
&& parsed.info.mint != USDC
{
let account_pubkey = Pubkey::from_str_const(&account.pubkey);
if let Ok(instruct) = close_account(
&token_program,
&account_pubkey,
&agent.wallet.address,
&agent.wallet.address,
&[&agent.wallet.address],
) {
transaction.push(instruct);
}
}
}
}
}
}
if transaction.is_empty() {
return Ok(CloseEmptyTokenAccountsData::default());
}
let recent_blockhash = agent.connection.get_latest_blockhash()?;
let transaction = Transaction::new_signed_with_payer(
&transaction,
Some(&agent.wallet.address),
&[&agent.wallet.wallet],
recent_blockhash,
);
let signature = agent.connection.send_and_confirm_transaction(&transaction)?;
let data = CloseEmptyTokenAccountsData::new(signature.to_string(), closed_size);
Ok(data)
}