* Revert solana-tokens to RpcClient * Fixup check_payer_balances tests * Use RpcClient::new_with_commitment in other tests * Sneak in helper fn from #13820 Co-authored-by: Tyera Eulberg <tyera@solana.com> Co-authored-by: Greg Fitzgerald <greg@solana.com>
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -4948,14 +4948,11 @@ dependencies = [
|
||||
"pickledb",
|
||||
"serde",
|
||||
"solana-account-decoder",
|
||||
"solana-banks-client",
|
||||
"solana-banks-server",
|
||||
"solana-clap-utils",
|
||||
"solana-cli-config",
|
||||
"solana-client",
|
||||
"solana-core",
|
||||
"solana-logger 1.4.14",
|
||||
"solana-program-test",
|
||||
"solana-remote-wallet",
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
@ -4966,8 +4963,6 @@ dependencies = [
|
||||
"spl-token",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio 0.3.2",
|
||||
"url 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -19,7 +19,6 @@ indicatif = "0.15.0"
|
||||
pickledb = "0.4.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.14" }
|
||||
solana-banks-client = { path = "../banks-client", version = "1.4.14" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.14" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.4.14" }
|
||||
solana-client = { path = "../client", version = "1.4.14" }
|
||||
@ -33,13 +32,8 @@ spl-associated-token-account-v1-0 = { package = "spl-associated-token-account",
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.0.0", features = ["no-entrypoint"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "0.3", features = ["full"] }
|
||||
url = "2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
bincode = "1.3.1"
|
||||
solana-banks-server = { path = "../banks-server", version = "1.4.14" }
|
||||
solana-core = { path = "../core", version = "1.4.14" }
|
||||
solana-logger = { path = "../logger", version = "1.4.14" }
|
||||
solana-program-test = { path = "../program-test", version = "1.4.14" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.14" }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
use chrono::prelude::*;
|
||||
use pickledb::{error::Error, PickleDb, PickleDbDumpPolicy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_banks_client::TransactionStatus;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature, transaction::Transaction};
|
||||
use solana_transaction_status::TransactionStatus;
|
||||
use std::{cmp::Ordering, fs, io, path::Path};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
@ -306,6 +306,7 @@ mod tests {
|
||||
slot: 0,
|
||||
confirmations: Some(1),
|
||||
err: None,
|
||||
status: Ok(()),
|
||||
};
|
||||
assert_eq!(
|
||||
update_finalized_transaction(&mut db, &signature, Some(transaction_status), 0, 0)
|
||||
@ -332,6 +333,7 @@ mod tests {
|
||||
slot: 0,
|
||||
confirmations: None,
|
||||
err: Some(TransactionError::AccountNotFound),
|
||||
status: Ok(()),
|
||||
};
|
||||
assert_eq!(
|
||||
update_finalized_transaction(&mut db, &signature, Some(transaction_status), 0, 0)
|
||||
@ -355,6 +357,7 @@ mod tests {
|
||||
slot: 0,
|
||||
confirmations: None,
|
||||
err: None,
|
||||
status: Ok(()),
|
||||
};
|
||||
assert_eq!(
|
||||
update_finalized_transaction(&mut db, &signature, Some(transaction_status), 0, 0)
|
||||
|
@ -1,9 +1,7 @@
|
||||
use solana_banks_client::start_tcp_client;
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_tokens::{arg_parser::parse_args, args::Command, commands, spl_token};
|
||||
use std::{env, error::Error, path::Path, process};
|
||||
use tokio::runtime::Runtime;
|
||||
use url::Url;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let command_args = parse_args(env::args_os())?;
|
||||
@ -18,27 +16,16 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
Config::default()
|
||||
};
|
||||
let json_rpc_url = command_args.url.unwrap_or(config.json_rpc_url);
|
||||
let rpc_banks_url = Config::compute_rpc_banks_url(&json_rpc_url);
|
||||
let url = Url::parse(&rpc_banks_url)?;
|
||||
let host_port = (url.host_str().unwrap(), url.port().unwrap());
|
||||
|
||||
let runtime = Runtime::new().unwrap();
|
||||
let mut banks_client = runtime.block_on(start_tcp_client(&host_port))?;
|
||||
let client = RpcClient::new(json_rpc_url);
|
||||
|
||||
match command_args.command {
|
||||
Command::DistributeTokens(mut args) => {
|
||||
runtime.block_on(spl_token::update_token_args(
|
||||
&mut banks_client,
|
||||
&mut args.spl_token_args,
|
||||
))?;
|
||||
runtime.block_on(commands::process_allocations(&mut banks_client, &args))?;
|
||||
spl_token::update_token_args(&client, &mut args.spl_token_args)?;
|
||||
commands::process_allocations(&client, &args)?;
|
||||
}
|
||||
Command::Balances(mut args) => {
|
||||
runtime.block_on(spl_token::update_decimals(
|
||||
&mut banks_client,
|
||||
&mut args.spl_token_args,
|
||||
))?;
|
||||
runtime.block_on(commands::process_balances(&mut banks_client, &args))?;
|
||||
spl_token::update_decimals(&client, &mut args.spl_token_args)?;
|
||||
commands::process_balances(&client, &args)?;
|
||||
}
|
||||
Command::TransactionLog(args) => {
|
||||
commands::process_transaction_log(&args)?;
|
||||
|
@ -6,7 +6,7 @@ use console::style;
|
||||
use solana_account_decoder::parse_token::{
|
||||
pubkey_from_spl_token_v2_0, spl_token_v2_0_pubkey, token_amount_to_ui_amount,
|
||||
};
|
||||
use solana_banks_client::{BanksClient, BanksClientExt};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_sdk::{instruction::Instruction, native_token::lamports_to_sol};
|
||||
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
|
||||
use spl_associated_token_account_v1_0::{
|
||||
@ -17,32 +17,22 @@ use spl_token_v2_0::{
|
||||
state::{Account as SplTokenAccount, Mint},
|
||||
};
|
||||
|
||||
pub async fn update_token_args(
|
||||
client: &mut BanksClient,
|
||||
args: &mut Option<SplTokenArgs>,
|
||||
) -> Result<(), Error> {
|
||||
pub fn update_token_args(client: &RpcClient, args: &mut Option<SplTokenArgs>) -> Result<(), Error> {
|
||||
if let Some(spl_token_args) = args {
|
||||
let sender_account = client
|
||||
.get_account(spl_token_args.token_account_address)
|
||||
.await?
|
||||
.get_account(&spl_token_args.token_account_address)
|
||||
.unwrap_or_default();
|
||||
let mint_address =
|
||||
pubkey_from_spl_token_v2_0(&SplTokenAccount::unpack(&sender_account.data)?.mint);
|
||||
spl_token_args.mint = mint_address;
|
||||
update_decimals(client, args).await?;
|
||||
update_decimals(client, args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_decimals(
|
||||
client: &mut BanksClient,
|
||||
args: &mut Option<SplTokenArgs>,
|
||||
) -> Result<(), Error> {
|
||||
pub fn update_decimals(client: &RpcClient, args: &mut Option<SplTokenArgs>) -> Result<(), Error> {
|
||||
if let Some(spl_token_args) = args {
|
||||
let mint_account = client
|
||||
.get_account(spl_token_args.mint)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let mint_account = client.get_account(&spl_token_args.mint).unwrap_or_default();
|
||||
let mint = Mint::unpack(&mint_account.data)?;
|
||||
spl_token_args.decimals = mint.decimals;
|
||||
}
|
||||
@ -93,10 +83,10 @@ pub fn build_spl_token_instructions(
|
||||
instructions
|
||||
}
|
||||
|
||||
pub async fn check_spl_token_balances(
|
||||
pub fn check_spl_token_balances(
|
||||
num_signatures: usize,
|
||||
allocations: &[Allocation],
|
||||
client: &mut BanksClient,
|
||||
client: &RpcClient,
|
||||
args: &DistributeTokensArgs,
|
||||
created_accounts: u64,
|
||||
) -> Result<(), Error> {
|
||||
@ -106,16 +96,16 @@ pub async fn check_spl_token_balances(
|
||||
.expect("spl_token_args must be some");
|
||||
let allocation_amount: u64 = allocations.iter().map(|x| x.amount).sum();
|
||||
|
||||
let (fee_calculator, _blockhash, _last_valid_slot) = client.get_fees().await?;
|
||||
let fee_calculator = client.get_recent_blockhash()?.1;
|
||||
let fees = fee_calculator
|
||||
.lamports_per_signature
|
||||
.checked_mul(num_signatures as u64)
|
||||
.unwrap();
|
||||
|
||||
let rent = client.get_rent().await?;
|
||||
let token_account_rent_exempt_balance = rent.minimum_balance(SplTokenAccount::LEN);
|
||||
let token_account_rent_exempt_balance =
|
||||
client.get_minimum_balance_for_rent_exemption(SplTokenAccount::LEN)?;
|
||||
let account_creation_amount = created_accounts * token_account_rent_exempt_balance;
|
||||
let fee_payer_balance = client.get_balance(args.fee_payer.pubkey()).await?;
|
||||
let fee_payer_balance = client.get_balance(&args.fee_payer.pubkey())?;
|
||||
if fee_payer_balance < fees + account_creation_amount {
|
||||
return Err(Error::InsufficientFunds(
|
||||
vec![FundingSource::FeePayer].into(),
|
||||
@ -123,8 +113,7 @@ pub async fn check_spl_token_balances(
|
||||
));
|
||||
}
|
||||
let source_token_account = client
|
||||
.get_account(spl_token_args.token_account_address)
|
||||
.await?
|
||||
.get_account(&spl_token_args.token_account_address)
|
||||
.unwrap_or_default();
|
||||
let source_token = SplTokenAccount::unpack(&source_token_account.data)?;
|
||||
if source_token.amount < allocation_amount {
|
||||
@ -136,8 +125,8 @@ pub async fn check_spl_token_balances(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn print_token_balances(
|
||||
client: &mut BanksClient,
|
||||
pub fn print_token_balances(
|
||||
client: &RpcClient,
|
||||
allocation: &Allocation,
|
||||
spl_token_args: &SplTokenArgs,
|
||||
) -> Result<(), Error> {
|
||||
@ -148,8 +137,7 @@ pub async fn print_token_balances(
|
||||
&spl_token_v2_0_pubkey(&spl_token_args.mint),
|
||||
);
|
||||
let recipient_account = client
|
||||
.get_account(pubkey_from_spl_token_v2_0(&associated_token_address))
|
||||
.await?
|
||||
.get_account(&pubkey_from_spl_token_v2_0(&associated_token_address))
|
||||
.unwrap_or_default();
|
||||
let (actual, difference) = if let Ok(recipient_token) =
|
||||
SplTokenAccount::unpack(&recipient_account.data)
|
||||
@ -188,498 +176,15 @@ pub async fn print_token_balances(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
commands::{process_allocations, tests::tmp_file_path, Allocation},
|
||||
db::{self, check_output_file},
|
||||
};
|
||||
use solana_account_decoder::parse_token::{spl_token_id_v2_0, spl_token_v2_0_pubkey};
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
signature::{read_keypair_file, write_keypair_file, Keypair, Signer},
|
||||
system_instruction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
|
||||
use spl_associated_token_account_v1_0::{
|
||||
create_associated_token_account, get_associated_token_address,
|
||||
};
|
||||
use spl_token_v2_0::{
|
||||
instruction::{initialize_account, initialize_mint, mint_to},
|
||||
solana_program::pubkey::Pubkey,
|
||||
};
|
||||
use tempfile::{tempdir, NamedTempFile};
|
||||
|
||||
fn program_test() -> ProgramTest {
|
||||
// Add SPL Associated Token program
|
||||
let mut pc = ProgramTest::new(
|
||||
"spl_associated_token_account",
|
||||
pubkey_from_spl_token_v2_0(&spl_associated_token_account_v1_0::id()),
|
||||
None,
|
||||
);
|
||||
// Add SPL Token program
|
||||
pc.add_program("spl_token", spl_token_id_v2_0(), None);
|
||||
pc
|
||||
}
|
||||
|
||||
async fn initialize_test_mint(
|
||||
banks_client: &mut BanksClient,
|
||||
fee_payer: &Keypair,
|
||||
mint: &Keypair,
|
||||
decimals: u8,
|
||||
recent_blockhash: Hash,
|
||||
) {
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
let expected_mint_balance = rent.minimum_balance(Mint::LEN);
|
||||
let instructions = vec![
|
||||
system_instruction::create_account(
|
||||
&fee_payer.pubkey(),
|
||||
&mint.pubkey(),
|
||||
expected_mint_balance,
|
||||
Mint::LEN as u64,
|
||||
&spl_token_id_v2_0(),
|
||||
),
|
||||
spl_token_v2_0_instruction(
|
||||
initialize_mint(
|
||||
&spl_token_v2_0::id(),
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
None,
|
||||
decimals,
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
];
|
||||
let mut transaction = Transaction::new_with_payer(&instructions, Some(&fee_payer.pubkey()));
|
||||
transaction.sign(&[fee_payer, mint], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
}
|
||||
|
||||
async fn initialize_token_account(
|
||||
banks_client: &mut BanksClient,
|
||||
fee_payer: &Keypair,
|
||||
sender_account: &Keypair,
|
||||
mint: &Keypair,
|
||||
owner: &Keypair,
|
||||
recent_blockhash: Hash,
|
||||
) {
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
let expected_token_account_balance = rent.minimum_balance(SplTokenAccount::LEN);
|
||||
let instructions = vec![
|
||||
system_instruction::create_account(
|
||||
&fee_payer.pubkey(),
|
||||
&sender_account.pubkey(),
|
||||
expected_token_account_balance,
|
||||
SplTokenAccount::LEN as u64,
|
||||
&spl_token_id_v2_0(),
|
||||
),
|
||||
spl_token_v2_0_instruction(
|
||||
initialize_account(
|
||||
&spl_token_v2_0::id(),
|
||||
&spl_token_v2_0_pubkey(&sender_account.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&owner.pubkey()),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
];
|
||||
let mut transaction = Transaction::new_with_payer(&instructions, Some(&fee_payer.pubkey()));
|
||||
transaction.sign(&[fee_payer, sender_account], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
}
|
||||
|
||||
async fn mint_to_account(
|
||||
banks_client: &mut BanksClient,
|
||||
fee_payer: &Keypair,
|
||||
sender_account: &Keypair,
|
||||
mint: &Keypair,
|
||||
recent_blockhash: Hash,
|
||||
) {
|
||||
let instructions = vec![spl_token_v2_0_instruction(
|
||||
mint_to(
|
||||
&spl_token_v2_0::id(),
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&sender_account.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
&[],
|
||||
200_000,
|
||||
)
|
||||
.unwrap(),
|
||||
)];
|
||||
let mut transaction = Transaction::new_with_payer(&instructions, Some(&fee_payer.pubkey()));
|
||||
transaction.sign(&[fee_payer, mint], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
}
|
||||
|
||||
async fn test_process_distribute_spl_tokens_with_client(
|
||||
banks_client: &mut BanksClient,
|
||||
fee_payer: Keypair,
|
||||
transfer_amount: Option<u64>,
|
||||
recent_blockhash: Hash,
|
||||
) {
|
||||
// Initialize Token Mint
|
||||
let decimals = 2;
|
||||
let mint = Keypair::new();
|
||||
initialize_test_mint(banks_client, &fee_payer, &mint, decimals, recent_blockhash).await;
|
||||
|
||||
// Initialize Sender Token Account and Mint
|
||||
let sender_account = Keypair::new();
|
||||
let owner = Keypair::new();
|
||||
initialize_token_account(
|
||||
banks_client,
|
||||
&fee_payer,
|
||||
&sender_account,
|
||||
&mint,
|
||||
&owner,
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
|
||||
mint_to_account(
|
||||
banks_client,
|
||||
&fee_payer,
|
||||
&sender_account,
|
||||
&mint,
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Initialize one recipient Associated Token Account
|
||||
let wallet_address_0 = Pubkey::new_unique();
|
||||
let instructions = vec![spl_token_v2_0_instruction(create_associated_token_account(
|
||||
&spl_token_v2_0_pubkey(&fee_payer.pubkey()),
|
||||
&wallet_address_0,
|
||||
&spl_token_v2_0_pubkey(&mint.pubkey()),
|
||||
))];
|
||||
let mut transaction = Transaction::new_with_payer(&instructions, Some(&fee_payer.pubkey()));
|
||||
transaction.sign(&[&fee_payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
let wallet_address_1 = Pubkey::new_unique();
|
||||
|
||||
// Create allocations csv
|
||||
let allocation_amount = if let Some(amount) = transfer_amount {
|
||||
amount
|
||||
} else {
|
||||
100_000
|
||||
};
|
||||
let allocations_file = NamedTempFile::new().unwrap();
|
||||
let input_csv = allocations_file.path().to_str().unwrap().to_string();
|
||||
let mut wtr = csv::WriterBuilder::new().from_writer(allocations_file);
|
||||
wtr.write_record(&["recipient", "amount"]).unwrap();
|
||||
wtr.write_record(&[wallet_address_0.to_string(), allocation_amount.to_string()])
|
||||
.unwrap();
|
||||
wtr.write_record(&[wallet_address_1.to_string(), allocation_amount.to_string()])
|
||||
.unwrap();
|
||||
wtr.flush().unwrap();
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let transaction_db = dir
|
||||
.path()
|
||||
.join("transactions.db")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let output_file = NamedTempFile::new().unwrap();
|
||||
let output_path = output_file.path().to_str().unwrap().to_string();
|
||||
|
||||
let args = DistributeTokensArgs {
|
||||
sender_keypair: Box::new(owner),
|
||||
fee_payer: Box::new(fee_payer),
|
||||
dry_run: false,
|
||||
input_csv,
|
||||
transaction_db: transaction_db.clone(),
|
||||
output_path: Some(output_path.clone()),
|
||||
stake_args: None,
|
||||
spl_token_args: Some(SplTokenArgs {
|
||||
token_account_address: sender_account.pubkey(),
|
||||
mint: mint.pubkey(),
|
||||
decimals,
|
||||
}),
|
||||
transfer_amount,
|
||||
};
|
||||
|
||||
// Distribute Allocations
|
||||
let confirmations = process_allocations(banks_client, &args).await.unwrap();
|
||||
assert_eq!(confirmations, None);
|
||||
|
||||
let associated_token_address_0 =
|
||||
get_associated_token_address(&wallet_address_0, &spl_token_v2_0_pubkey(&mint.pubkey()));
|
||||
let associated_token_address_1 =
|
||||
get_associated_token_address(&wallet_address_1, &spl_token_v2_0_pubkey(&mint.pubkey()));
|
||||
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 2);
|
||||
assert!(transaction_infos
|
||||
.iter()
|
||||
.any(|info| info.recipient == pubkey_from_spl_token_v2_0(&wallet_address_0)));
|
||||
assert!(transaction_infos
|
||||
.iter()
|
||||
.any(|info| info.recipient == pubkey_from_spl_token_v2_0(&wallet_address_1)));
|
||||
assert_eq!(transaction_infos[0].amount, allocation_amount);
|
||||
assert_eq!(transaction_infos[1].amount, allocation_amount);
|
||||
|
||||
let recipient_account_0 = banks_client
|
||||
.get_account(pubkey_from_spl_token_v2_0(&associated_token_address_0))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
assert_eq!(
|
||||
SplTokenAccount::unpack(&recipient_account_0.data)
|
||||
.unwrap()
|
||||
.amount,
|
||||
allocation_amount,
|
||||
);
|
||||
let recipient_account_1 = banks_client
|
||||
.get_account(pubkey_from_spl_token_v2_0(&associated_token_address_1))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
assert_eq!(
|
||||
SplTokenAccount::unpack(&recipient_account_1.data)
|
||||
.unwrap()
|
||||
.amount,
|
||||
allocation_amount,
|
||||
);
|
||||
|
||||
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
||||
|
||||
// Now, run it again, and check there's no double-spend.
|
||||
process_allocations(banks_client, &args).await.unwrap();
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 2);
|
||||
assert!(transaction_infos
|
||||
.iter()
|
||||
.any(|info| info.recipient == pubkey_from_spl_token_v2_0(&wallet_address_0)));
|
||||
assert!(transaction_infos
|
||||
.iter()
|
||||
.any(|info| info.recipient == pubkey_from_spl_token_v2_0(&wallet_address_1)));
|
||||
assert_eq!(transaction_infos[0].amount, allocation_amount);
|
||||
assert_eq!(transaction_infos[1].amount, allocation_amount);
|
||||
|
||||
let recipient_account_0 = banks_client
|
||||
.get_account(pubkey_from_spl_token_v2_0(&associated_token_address_0))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
assert_eq!(
|
||||
SplTokenAccount::unpack(&recipient_account_0.data)
|
||||
.unwrap()
|
||||
.amount,
|
||||
allocation_amount,
|
||||
);
|
||||
let recipient_account_1 = banks_client
|
||||
.get_account(pubkey_from_spl_token_v2_0(&associated_token_address_1))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
assert_eq!(
|
||||
SplTokenAccount::unpack(&recipient_account_1.data)
|
||||
.unwrap()
|
||||
.amount,
|
||||
allocation_amount,
|
||||
);
|
||||
|
||||
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_process_spl_token_allocations() {
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||
test_process_distribute_spl_tokens_with_client(
|
||||
&mut banks_client,
|
||||
payer,
|
||||
None,
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_process_spl_token_transfer_amount_allocations() {
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||
test_process_distribute_spl_tokens_with_client(
|
||||
&mut banks_client,
|
||||
payer,
|
||||
Some(10550),
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_check_spl_token_balances() {
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||
|
||||
let (fee_calculator, _, _) = banks_client.get_fees().await.unwrap();
|
||||
let signatures = 2;
|
||||
let fees = fee_calculator.lamports_per_signature * signatures;
|
||||
let fees_in_sol = lamports_to_sol(fees);
|
||||
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
let expected_token_account_balance = rent.minimum_balance(SplTokenAccount::LEN);
|
||||
let expected_token_account_balance_sol = lamports_to_sol(expected_token_account_balance);
|
||||
|
||||
// Initialize Token Mint
|
||||
let decimals = 2;
|
||||
let mint = Keypair::new();
|
||||
initialize_test_mint(&mut banks_client, &payer, &mint, decimals, recent_blockhash).await;
|
||||
|
||||
// Initialize Sender Token Account and Mint
|
||||
let sender_account = Keypair::new();
|
||||
let owner = Keypair::new();
|
||||
let owner_keypair_file = tmp_file_path("keypair_file", &owner.pubkey());
|
||||
write_keypair_file(&owner, &owner_keypair_file).unwrap();
|
||||
|
||||
initialize_token_account(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&sender_account,
|
||||
&mint,
|
||||
&owner,
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
|
||||
let unfunded_fee_payer = Keypair::new();
|
||||
|
||||
let allocation_amount = 4200;
|
||||
let allocations = vec![Allocation {
|
||||
recipient: Pubkey::new_unique().to_string(),
|
||||
amount: allocation_amount,
|
||||
lockup_date: "".to_string(),
|
||||
}];
|
||||
let mut args = DistributeTokensArgs {
|
||||
sender_keypair: read_keypair_file(&owner_keypair_file).unwrap().into(),
|
||||
fee_payer: Box::new(unfunded_fee_payer),
|
||||
dry_run: false,
|
||||
input_csv: "".to_string(),
|
||||
transaction_db: "".to_string(),
|
||||
output_path: None,
|
||||
stake_args: None,
|
||||
spl_token_args: Some(SplTokenArgs {
|
||||
token_account_address: sender_account.pubkey(),
|
||||
mint: mint.pubkey(),
|
||||
decimals,
|
||||
}),
|
||||
transfer_amount: None,
|
||||
};
|
||||
|
||||
// Unfunded fee_payer
|
||||
let err_result = check_spl_token_balances(
|
||||
signatures as usize,
|
||||
&allocations,
|
||||
&mut banks_client,
|
||||
&args,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
if let Error::InsufficientFunds(sources, amount) = err_result {
|
||||
assert_eq!(sources, vec![FundingSource::FeePayer].into());
|
||||
assert!(
|
||||
(amount - (fees_in_sol + expected_token_account_balance_sol)).abs() < f64::EPSILON
|
||||
);
|
||||
} else {
|
||||
panic!("check_spl_token_balances should have errored");
|
||||
}
|
||||
|
||||
// Unfunded sender SPL Token account
|
||||
let fee_payer = Keypair::new();
|
||||
|
||||
let instruction = system_instruction::transfer(
|
||||
&payer.pubkey(),
|
||||
&fee_payer.pubkey(),
|
||||
fees + expected_token_account_balance,
|
||||
);
|
||||
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
|
||||
transaction.sign(&[&payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
args.fee_payer = Box::new(fee_payer);
|
||||
let err_result = check_spl_token_balances(
|
||||
signatures as usize,
|
||||
&allocations,
|
||||
&mut banks_client,
|
||||
&args,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
if let Error::InsufficientFunds(sources, amount) = err_result {
|
||||
assert_eq!(sources, vec![FundingSource::SplTokenAccount].into());
|
||||
assert!(
|
||||
(amount - token_amount_to_ui_amount(allocation_amount, decimals).ui_amount).abs()
|
||||
< f64::EPSILON
|
||||
);
|
||||
} else {
|
||||
panic!("check_spl_token_balances should have errored");
|
||||
}
|
||||
|
||||
// Fully funded payers
|
||||
mint_to_account(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&sender_account,
|
||||
&mint,
|
||||
recent_blockhash,
|
||||
)
|
||||
.await;
|
||||
|
||||
check_spl_token_balances(
|
||||
signatures as usize,
|
||||
&allocations,
|
||||
&mut banks_client,
|
||||
&args,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Partially-funded fee payer can afford fees, but not to create Associated Token Account
|
||||
let partially_funded_fee_payer = Keypair::new();
|
||||
|
||||
let instruction = system_instruction::transfer(
|
||||
&payer.pubkey(),
|
||||
&partially_funded_fee_payer.pubkey(),
|
||||
fees,
|
||||
);
|
||||
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
|
||||
transaction.sign(&[&payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
args.fee_payer = Box::new(partially_funded_fee_payer);
|
||||
let err_result = check_spl_token_balances(
|
||||
signatures as usize,
|
||||
&allocations,
|
||||
&mut banks_client,
|
||||
&args,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
if let Error::InsufficientFunds(sources, amount) = err_result {
|
||||
assert_eq!(sources, vec![FundingSource::FeePayer].into());
|
||||
assert!(
|
||||
(amount - (fees_in_sol + expected_token_account_balance_sol)).abs() < f64::EPSILON
|
||||
);
|
||||
} else {
|
||||
panic!("check_spl_token_balances should have errored");
|
||||
}
|
||||
|
||||
// Succeeds if no account creation required
|
||||
check_spl_token_balances(
|
||||
signatures as usize,
|
||||
&allocations,
|
||||
&mut banks_client,
|
||||
&args,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
// The following unit tests were written for v1.4 using the ProgramTest framework, passing its
|
||||
// BanksClient into the `solana-tokens` methods. With the revert to RpcClient in this module
|
||||
// (https://github.com/solana-labs/solana/pull/13623), that approach was no longer viable.
|
||||
// These tests were removed rather than rewritten to avoid accruing technical debt. Once a new
|
||||
// rpc/client framework is implemented, they should be restored.
|
||||
//
|
||||
// async fn test_process_spl_token_allocations()
|
||||
// async fn test_process_spl_token_transfer_amount_allocations()
|
||||
// async fn test_check_spl_token_balances()
|
||||
//
|
||||
// https://github.com/solana-labs/solana/blob/5511d52c6284013a24ced10966d11d8f4585799e/tokens/src/spl_token.rs#L490-L685
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
use solana_banks_client::start_tcp_client;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_tokens::commands::test_process_distribute_tokens_with_client;
|
||||
use std::fs::remove_dir_all;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[test]
|
||||
fn test_process_distribute_with_rpc_client() {
|
||||
@ -15,10 +14,8 @@ fn test_process_distribute_with_rpc_client() {
|
||||
..
|
||||
} = TestValidator::run();
|
||||
|
||||
Runtime::new().unwrap().block_on(async {
|
||||
let mut banks_client = start_tcp_client(leader_data.rpc_banks).await.unwrap();
|
||||
test_process_distribute_tokens_with_client(&mut banks_client, alice, None).await
|
||||
});
|
||||
let client = RpcClient::new_socket(leader_data.rpc);
|
||||
test_process_distribute_tokens_with_client(&client, alice, None);
|
||||
|
||||
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
||||
server.close().unwrap();
|
||||
|
Reference in New Issue
Block a user