tuktuk_cli/cmd/
mod.rs

1use std::path::PathBuf;
2
3use clap::Args;
4use dirs::home_dir;
5use serde::Serialize;
6use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::EncodableKey};
7use tuktuk_program::{CompiledTransactionV0, TransactionSourceV0};
8
9use crate::{client::CliClient, result::Result, serde::serde_pubkey};
10
11pub mod cron;
12pub mod cron_transaction;
13pub mod task;
14pub mod task_queue;
15pub mod tuktuk_config;
16
17/// Common options for commands
18#[derive(Debug, Args, Clone)]
19pub struct Opts {
20    /// Anchor wallet keypair
21    #[arg(short = 'w', long)]
22    wallet: Option<PathBuf>,
23
24    /// Solana RPC URL to use.
25    #[arg(long, short)]
26    url: String,
27}
28
29impl Opts {
30    pub fn default_wallet_path() -> PathBuf {
31        let mut path = home_dir().unwrap_or_else(|| PathBuf::from("/"));
32        path.push(".config/solana/id.json");
33        path
34    }
35
36    pub fn load_solana_keypair(&self) -> Result<Keypair> {
37        let path = self
38            .wallet
39            .as_ref()
40            .cloned()
41            .unwrap_or_else(Opts::default_wallet_path);
42        Keypair::read_from_file(path).map_err(|_| anyhow::anyhow!("Failed to read keypair"))
43    }
44
45    pub fn ws_url(&self) -> String {
46        self.url
47            .replace("https", "wss")
48            .replace("http", "ws")
49            .replace("127.0.0.1:8899", "127.0.0.1:8900")
50    }
51
52    pub fn rpc_url(&self) -> String {
53        self.url.clone()
54    }
55
56    pub async fn client(&self) -> Result<CliClient> {
57        CliClient::new(self).await
58    }
59}
60
61#[derive(Serialize)]
62pub enum TransactionSource {
63    RemoteV0 {
64        url: String,
65        #[serde(with = "serde_pubkey")]
66        signer: Pubkey,
67    },
68    CompiledV0 {
69        transaction: CompiledTransaction,
70    },
71}
72
73#[derive(Serialize)]
74pub struct CompiledTransaction {
75    pub instructions: Vec<Instruction>,
76}
77
78#[derive(Serialize)]
79pub struct Instruction {
80    pub data: Vec<u8>,
81    #[serde(with = "serde_pubkey")]
82    pub program_id: Pubkey,
83    pub accounts: Vec<Account>,
84}
85
86#[derive(Serialize)]
87pub struct Account {
88    #[serde(with = "serde_pubkey")]
89    pub pubkey: Pubkey,
90    pub is_signer: bool,
91    pub is_writable: bool,
92}
93
94impl From<TransactionSourceV0> for TransactionSource {
95    fn from(source: TransactionSourceV0) -> Self {
96        match source {
97            TransactionSourceV0::RemoteV0 { url, signer } => Self::RemoteV0 { url, signer },
98            TransactionSourceV0::CompiledV0(transaction) => Self::CompiledV0 {
99                transaction: transaction.into(),
100            },
101        }
102    }
103}
104
105impl From<CompiledTransactionV0> for CompiledTransaction {
106    fn from(transaction: CompiledTransactionV0) -> Self {
107        Self {
108            instructions: transaction
109                .instructions
110                .into_iter()
111                .map(|i| Instruction {
112                    data: i.data,
113                    program_id: transaction.accounts[i.program_id_index as usize],
114                    accounts: i
115                        .accounts
116                        .into_iter()
117                        .map(|index| Account {
118                            pubkey: transaction.accounts[index as usize],
119                            is_signer: index
120                                < transaction.num_ro_signers + transaction.num_rw_signers,
121                            is_writable: index < transaction.num_rw_signers
122                                || (index
123                                    >= (transaction.num_rw_signers + transaction.num_ro_signers)
124                                    && index
125                                        < (transaction.num_rw_signers
126                                            + transaction.num_ro_signers
127                                            + transaction.num_rw)),
128                        })
129                        .collect(),
130                })
131                .collect(),
132        }
133    }
134}