nifty_cli/
transaction.rs

1use anyhow::Result;
2use nifty_asset::MAX_TX_SIZE;
3use retry::{delay::Exponential, retry};
4use solana_client::rpc_client::RpcClient;
5use solana_program::instruction::Instruction;
6use solana_sdk::{
7    pubkey::Pubkey,
8    signature::{Keypair, Signature},
9    signer::Signer,
10    transaction::Transaction,
11};
12
13#[macro_export]
14macro_rules! transaction {
15    ($signers:expr, $instructions:expr, $client:expr) => {
16        Transaction::new_signed_with_payer(
17            $instructions,
18            Some(&$signers[0].pubkey()),
19            $signers,
20            $client.get_latest_blockhash()?,
21        )
22    };
23}
24
25pub fn send_and_confirm_tx(
26    client: &RpcClient,
27    signers: &[&Keypair],
28    ixs: &[Instruction],
29) -> Result<Signature> {
30    let tx = transaction!(signers, ixs, client);
31
32    let signature = client.send_and_confirm_transaction(&tx)?;
33
34    Ok(signature)
35}
36
37pub fn send_and_confirm_tx_with_retries(
38    client: &RpcClient,
39    signers: &[&Keypair],
40    ixs: &[Instruction],
41) -> Result<Signature> {
42    let tx = transaction!(signers, ixs, client);
43
44    // Send tx with retries.
45    let res = retry(
46        Exponential::from_millis_with_factor(250, 2.0).take(3),
47        || client.send_and_confirm_transaction_with_spinner(&tx),
48    )?;
49
50    Ok(res)
51}
52
53pub fn pack_instructions<'a>(
54    num_signers: u32,
55    payer: &'a Pubkey,
56    ixs: &'a [Instruction],
57) -> Vec<Vec<Instruction>> {
58    // This contains the instructions that will be sent in each transaction.
59    let mut transactions: Vec<Vec<Instruction>> = vec![];
60    // Batch instructions for each tx into this vector, ensuring we don't exceed max payload size.
61    let mut tx_instructions: Vec<Instruction> = vec![];
62
63    // 64 bytes for each signature + Message size
64    let max_payload_size = MAX_TX_SIZE - std::mem::size_of::<Signature>() * num_signers as usize;
65
66    for ix in ixs {
67        tx_instructions.push(ix.clone());
68        let tx = Transaction::new_with_payer(tx_instructions.as_slice(), Some(payer));
69        let tx_len = bincode::serialize(&tx).unwrap().len();
70
71        if tx_len > max_payload_size {
72            let last_ix = tx_instructions.pop().unwrap();
73            transactions.push(tx_instructions.clone());
74            tx_instructions.clear();
75            tx_instructions.push(last_ix);
76        }
77    }
78    transactions.push(tx_instructions);
79
80    transactions
81}