tuktuk_cli/
client.rs

1use std::sync::Arc;
2
3use solana_client::{
4    nonblocking::{rpc_client::RpcClient, tpu_client::TpuClient},
5    send_and_confirm_transactions_in_parallel::{
6        send_and_confirm_transactions_in_parallel, SendAndConfirmConfig,
7    },
8    tpu_client::TpuClientConfig,
9};
10use solana_sdk::{
11    commitment_config::CommitmentConfig, instruction::Instruction, message::Message,
12    signature::Keypair, signer::Signer,
13};
14use solana_transaction_utils::{
15    pack::pack_instructions_into_transactions, priority_fee::auto_compute_limit_and_price,
16};
17
18use crate::{cmd::Opts, result::Result};
19
20pub struct CliClient {
21    pub rpc_client: Arc<RpcClient>,
22    pub payer: Keypair,
23    pub opts: Opts,
24}
25
26impl AsRef<RpcClient> for CliClient {
27    fn as_ref(&self) -> &RpcClient {
28        &self.rpc_client
29    }
30}
31
32impl CliClient {
33    pub async fn new(opts: &Opts) -> Result<Self> {
34        let rpc_client =
35            RpcClient::new_with_commitment(opts.rpc_url(), CommitmentConfig::confirmed());
36        let payer = opts.load_solana_keypair()?;
37        Ok(Self {
38            rpc_client: Arc::new(rpc_client),
39            payer,
40            opts: opts.clone(),
41        })
42    }
43}
44
45pub async fn send_instructions(
46    rpc_client: Arc<RpcClient>,
47    payer: &Keypair,
48    ws_url: &str,
49    ixs: Vec<Instruction>,
50    extra_signers: &[Keypair],
51) -> Result<()> {
52    let (blockhash, _) = rpc_client
53        .as_ref()
54        .get_latest_blockhash_with_commitment(CommitmentConfig::finalized())
55        .await
56        .expect("Failed to get latest blockhash");
57    let txs = pack_instructions_into_transactions(vec![ixs], payer, None)?;
58    let mut with_auto_compute: Vec<Message> = Vec::new();
59    let keys: Vec<&dyn Signer> = std::iter::once(&payer as &dyn Signer)
60        .chain(extra_signers.iter().map(|k| k as &dyn Signer))
61        .collect();
62    for tx in txs {
63        // This is just a tx with compute ixs. Skip it
64        if tx.is_empty() {
65            continue;
66        }
67
68        let (computed, _) = auto_compute_limit_and_price(
69            &rpc_client,
70            tx.instructions,
71            1.2,
72            &payer.pubkey(),
73            Some(blockhash),
74            None,
75        )
76        .await
77        .unwrap();
78        with_auto_compute.push(Message::new(&computed, Some(&payer.pubkey())));
79    }
80    if with_auto_compute.is_empty() {
81        return Ok(());
82    }
83
84    let tpu_client = TpuClient::new(
85        "tuktuk-cli",
86        rpc_client.clone(),
87        ws_url,
88        TpuClientConfig::default(),
89    )
90    .await?;
91
92    let results = send_and_confirm_transactions_in_parallel(
93        rpc_client.clone(),
94        Some(tpu_client),
95        &with_auto_compute,
96        &keys,
97        SendAndConfirmConfig {
98            with_spinner: true,
99            resign_txs_count: Some(5),
100        },
101    )
102    .await?;
103
104    if let Some(err) = results.into_iter().flatten().next() {
105        return Err(anyhow::Error::from(err));
106    }
107
108    Ok(())
109}