use crate::builder::{InstructionBuilder, TransactionBuilder};
use crate::instructions::{
program_ids::{system_program, token_program},
system::create_account,
token::transfer_checked,
};
use crate::types::instruction::AccountMeta;
use crate::types::VersionedTransaction;
use crate::Pubkey;
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
fn mint_pubkey() -> Pubkey {
Pubkey::from_base58("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap()
}
fn token_pubkey() -> Pubkey {
Pubkey::from_base58("4q2wPZuZwQTB1dEU9sMGsJK1d8NSL1hpBjTGHBsLQNDh").unwrap()
}
fn authority_pubkey() -> Pubkey {
Pubkey::from_base58("Hozo7TadHq6PMMiGLGNvgk79Hvj5VTAM7Ny2bamQ2m8q").unwrap()
}
fn payer_pubkey() -> Pubkey {
Pubkey::from_base58("7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z").unwrap()
}
fn new_account_pubkey() -> Pubkey {
Pubkey::from_base58("DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n").unwrap()
}
fn random_pubkey() -> Pubkey {
let mut bytes = [0u8; 32];
bytes
.iter_mut()
.enumerate()
.for_each(|(i, byte)| *byte = i as u8);
Pubkey::new(bytes)
}
fn test_blockhash() -> [u8; 32] {
let mut bytes = [0u8; 32];
bytes
.iter_mut()
.enumerate()
.for_each(|(i, byte)| *byte = i as u8);
bytes
}
#[test]
fn test_instruction_builder() {
let program_id = token_program();
let source = token_pubkey();
let dest = random_pubkey();
let owner = authority_pubkey();
let mint = mint_pubkey();
let amount = 1_000_000;
let decimals = 6;
let builder_ix = InstructionBuilder::new(program_id)
.account(source, false, true)
.account(mint, false, false)
.account(dest, false, true)
.account(owner, true, false)
.build();
let ix = transfer_checked(&source, &mint, &dest, &owner, amount, decimals);
assert_eq!(builder_ix.program_id, token_program());
assert_eq!(ix.program_id, token_program());
}
#[test]
fn test_transaction_builder() {
let recent_blockhash = "9U2ogLjDt479wubHbEtPLGBF84DijmWggA4KoXSwcivd";
let recent_blockhash_bytes = bs58::decode(recent_blockhash).into_vec().unwrap();
let fee_payer: Pubkey = "A21o4asMbFHYadqXdLusT9Bvx9xaC5YV9gcaidjqtdXC"
.parse()
.unwrap();
let program_id = Pubkey::from_base58("J88B7gmadHzTNGiy54c9Ms8BsEXNdB2fntFyhKpk3qoT").unwrap();
let data = hex::decode("a3265ce2f3698dc400000070000000000100000014000000514bcb1f9aabb904e6106bd1052b66d2706dbbb701000000006c000000000a00000085fba93ee29c604fa858a351688c01290841eafb19c63a70a475d3c7bc3bef9f000000000000000000008489b9cc07af97add00300000000000000000000000000001e83d2972d3dca3a330d60c2777ee5b8d25683c63fa359116985609830f42054050004002d16000000f0314f0cffdf8d00b6a7ce61f86164ca47c1b8b1bc2e").unwrap();
let instruction = InstructionBuilder::new(program_id)
.data(data)
.accounts(vec![
AccountMeta::new_readonly(
"ACLMuTFvDAb3oecQQGkTVqpUbhCKHG3EZ9uNXHK1W9ka"
.parse()
.unwrap(),
),
AccountMeta::new_writable(
"3tJ67qa2GDfvv2wcMYNUfN5QBZrFpTwcU8ASZKMvCTVU"
.parse()
.unwrap(),
),
AccountMeta::new_signer_writable(
"A21o4asMbFHYadqXdLusT9Bvx9xaC5YV9gcaidjqtdXC"
.parse()
.unwrap(),
),
AccountMeta::new_writable(
"E8p6aiwuSDWEzQnjGjkNiMZrd1rpSsntWsaZCivdFz51"
.parse()
.unwrap(),
),
AccountMeta::new_writable(
"FmAcjWaRFUxGWBfGT7G3CzcFeJFsewQ4KPJVG4f6fcob"
.parse()
.unwrap(),
),
AccountMeta::new_readonly(system_program()),
]);
let mut tx_builder =
TransactionBuilder::new(fee_payer, recent_blockhash_bytes.try_into().unwrap());
tx_builder.add_instruction(instruction.build());
let transaction = tx_builder.build().unwrap();
println!("Transaction: {transaction:#?}");
let tx_wire_bytes = transaction
.serialize_legacy()
.expect("Failed to serialize transaction with wire format");
let base64_tx = STANDARD.encode(&tx_wire_bytes);
println!("Base64 transaction: {base64_tx}");
let deserialized_vt = VersionedTransaction::deserialize_with_version(&tx_wire_bytes)
.expect("Failed to deserialize wire bytes into VersionedTransaction");
match deserialized_vt {
VersionedTransaction::Legacy {
signatures: deserialized_signatures,
message: deserialized_legacy_message,
} => {
assert_eq!(
deserialized_signatures, transaction.signatures,
"Signatures mismatch after round trip"
);
assert_eq!(
deserialized_legacy_message.header, transaction.message.header,
"Message header mismatch"
);
assert_eq!(
deserialized_legacy_message.account_keys, transaction.message.account_keys,
"Account keys mismatch"
);
assert_eq!(
deserialized_legacy_message.recent_blockhash, transaction.message.recent_blockhash,
"Recent blockhash mismatch"
);
assert_eq!(
deserialized_legacy_message.instructions, transaction.message.instructions,
"Instructions mismatch"
);
}
_ => panic!("Deserialized transaction is not the expected Legacy variant"),
}
}
#[test]
fn test_complex_transaction() {
let payer = payer_pubkey();
let blockhash = test_blockhash();
let mut tx_builder = TransactionBuilder::new(payer, blockhash);
let from = payer_pubkey();
let new_account = new_account_pubkey();
let owner = system_program(); let lamports = 1_000_000_000; let space = 165;
let create_account_ix = create_account(&from, &new_account, lamports, space, &owner);
tx_builder.add_instruction(create_account_ix);
let source = token_pubkey();
let dest = random_pubkey();
let owner = authority_pubkey();
let mint = mint_pubkey();
let amount = 1_000_000;
let decimals = 6;
let transfer_ix = transfer_checked(&source, &mint, &dest, &owner, amount, decimals);
tx_builder.add_instruction(transfer_ix);
let transaction = tx_builder.build().unwrap();
assert!(transaction.signatures.len() >= 2);
let account_keys = &transaction.message.account_keys;
assert!(account_keys.contains(&payer_pubkey())); assert!(account_keys.contains(&new_account)); assert!(account_keys.contains(&system_program())); assert!(account_keys.contains(&source)); assert!(account_keys.contains(&dest)); assert!(account_keys.contains(&owner)); assert!(account_keys.contains(&mint)); assert!(account_keys.contains(&token_program()));
assert_eq!(transaction.message.instructions.len(), 2);
let message_data = &transaction.message;
assert!(!message_data.account_keys.is_empty());
}