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 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}