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