2025-08-03 22:51:11 +08:00
|
|
|
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
|
|
|
|
|
use solana_system_interface::instruction::transfer;
|
2025-07-10 18:14:21 +08:00
|
|
|
use spl_associated_token_account::get_associated_token_address;
|
|
|
|
|
use spl_token::instruction::close_account;
|
|
|
|
|
|
|
|
|
|
use crate::common::SolanaRpcClient;
|
|
|
|
|
use anyhow::anyhow;
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub async fn get_token_balance(
|
|
|
|
|
rpc: &SolanaRpcClient,
|
|
|
|
|
payer: &Pubkey,
|
|
|
|
|
mint: &Pubkey,
|
|
|
|
|
) -> Result<u64, anyhow::Error> {
|
|
|
|
|
let ata = get_associated_token_address(payer, mint);
|
|
|
|
|
let balance = rpc.get_token_account_balance(&ata).await?;
|
2025-08-03 22:51:11 +08:00
|
|
|
let balance_u64 =
|
|
|
|
|
balance.amount.parse::<u64>().map_err(|_| anyhow!("Failed to parse token balance"))?;
|
2025-07-10 18:14:21 +08:00
|
|
|
Ok(balance_u64)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub async fn get_sol_balance(
|
|
|
|
|
rpc: &SolanaRpcClient,
|
|
|
|
|
account: &Pubkey,
|
|
|
|
|
) -> Result<u64, anyhow::Error> {
|
|
|
|
|
let balance = rpc.get_balance(account).await?;
|
|
|
|
|
Ok(balance)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn transfer_sol(
|
|
|
|
|
rpc: &SolanaRpcClient,
|
|
|
|
|
payer: &Keypair,
|
|
|
|
|
receive_wallet: &Pubkey,
|
|
|
|
|
amount: u64,
|
|
|
|
|
) -> Result<(), anyhow::Error> {
|
|
|
|
|
if amount == 0 {
|
|
|
|
|
return Err(anyhow!("transfer_sol: Amount cannot be zero"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let balance = get_sol_balance(rpc, &payer.pubkey()).await?;
|
|
|
|
|
if balance < amount {
|
|
|
|
|
return Err(anyhow!("Insufficient balance"));
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 22:51:11 +08:00
|
|
|
let transfer_instruction = transfer(&payer.pubkey(), receive_wallet, amount);
|
2025-07-10 18:14:21 +08:00
|
|
|
|
|
|
|
|
let recent_blockhash = rpc.get_latest_blockhash().await?;
|
|
|
|
|
|
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
|
|
|
&[transfer_instruction],
|
|
|
|
|
Some(&payer.pubkey()),
|
|
|
|
|
&[payer],
|
|
|
|
|
recent_blockhash,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
rpc.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-18 18:00:14 +08:00
|
|
|
/// Close token account
|
2025-07-10 18:14:21 +08:00
|
|
|
///
|
2025-08-18 18:00:14 +08:00
|
|
|
/// This function is used to close the associated token account for a specified token,
|
|
|
|
|
/// transferring the token balance in the account to the account owner.
|
2025-07-10 18:14:21 +08:00
|
|
|
///
|
2025-08-18 18:00:14 +08:00
|
|
|
/// # Parameters
|
2025-07-10 18:14:21 +08:00
|
|
|
///
|
2025-08-18 18:00:14 +08:00
|
|
|
/// * `rpc` - Solana RPC client
|
|
|
|
|
/// * `payer` - Account that pays transaction fees
|
|
|
|
|
/// * `mint` - Token mint address
|
2025-07-10 18:14:21 +08:00
|
|
|
///
|
2025-08-18 18:00:14 +08:00
|
|
|
/// # Returns
|
2025-07-10 18:14:21 +08:00
|
|
|
///
|
2025-08-18 18:00:14 +08:00
|
|
|
/// Returns a Result, success returns (), failure returns error
|
2025-07-10 18:14:21 +08:00
|
|
|
pub async fn close_token_account(
|
|
|
|
|
rpc: &SolanaRpcClient,
|
|
|
|
|
payer: &Keypair,
|
|
|
|
|
mint: &Pubkey,
|
|
|
|
|
) -> Result<(), anyhow::Error> {
|
2025-08-18 18:00:14 +08:00
|
|
|
// Get associated token account address
|
2025-07-10 18:14:21 +08:00
|
|
|
let ata = get_associated_token_address(&payer.pubkey(), mint);
|
|
|
|
|
|
2025-08-18 18:00:14 +08:00
|
|
|
// Check if account exists
|
2025-07-10 18:14:21 +08:00
|
|
|
let account_exists = rpc.get_account(&ata).await.is_ok();
|
|
|
|
|
if !account_exists {
|
2025-08-18 18:00:14 +08:00
|
|
|
return Ok(()); // If account doesn't exist, return success directly
|
2025-07-10 18:14:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-18 18:00:14 +08:00
|
|
|
// Build close account instruction
|
2025-08-03 22:51:11 +08:00
|
|
|
let close_account_ix =
|
|
|
|
|
close_account(&spl_token::ID, &ata, &payer.pubkey(), &payer.pubkey(), &[&payer.pubkey()])?;
|
2025-07-10 18:14:21 +08:00
|
|
|
|
2025-08-18 18:00:14 +08:00
|
|
|
// Build transaction
|
2025-07-10 18:14:21 +08:00
|
|
|
let recent_blockhash = rpc.get_latest_blockhash().await?;
|
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
|
|
|
&[close_account_ix],
|
|
|
|
|
Some(&payer.pubkey()),
|
|
|
|
|
&[payer],
|
|
|
|
|
recent_blockhash,
|
|
|
|
|
);
|
|
|
|
|
|
2025-08-18 18:00:14 +08:00
|
|
|
// Send transaction
|
2025-07-10 18:14:21 +08:00
|
|
|
rpc.send_and_confirm_transaction(&transaction).await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|