1use bip39::Mnemonic;
2use clap::{Parser, Subcommand, ValueEnum};
3use dg_xch_core::blockchain::sized_bytes::Bytes32;
4use dialoguer::theme::ColorfulTheme;
5use dialoguer::Input;
6use std::io::{Error, ErrorKind};
7use std::str::FromStr;
8
9#[derive(Parser, Debug)]
10#[command(author, version, about, long_about = None)]
11pub struct Cli {
12 #[arg(short, long, value_name = "Path to the chia ssl folder")]
13 pub ssl_path: Option<String>,
14 #[arg(short, long, value_name = "Timeout When Connecting to Fullnode")]
15 pub timeout: Option<u64>,
16
17 #[arg(long, value_name = "Fullnode Hostname")]
18 pub fullnode_host: Option<String>,
19 #[arg(long, value_name = "Fullnode Port")]
20 pub fullnode_port: Option<u16>,
21
22 #[arg(long, value_name = "Wallet Hostname")]
23 pub wallet_host: Option<String>,
24 #[arg(long, value_name = "Wallet Port")]
25 pub wallet_port: Option<u16>,
26
27 #[arg(long, value_name = "Network to use, default to Mainnet")]
28 pub network: Option<String>,
29
30 #[command(subcommand)]
31 pub action: RootCommands,
32}
33
34#[derive(Debug, Subcommand)]
35pub enum RootCommands {
36 #[command(about = "Get the current BlockchainState", long_about = None)]
38 PrintPlottingInfo {
39 #[arg(long)]
40 launcher_id: Option<Bytes32>,
41 },
42 #[command(about = "Get the current BlockchainState", long_about = None)]
44 GetBlockchainState,
45 #[command(about = "Loads a FullBlock by header_hash", long_about = None)]
46 GetBlock {
47 #[arg(long)]
48 header_hash: Bytes32,
49 },
50 #[command(about = "Loads current BlockStateMetrics", long_about = None)]
51 GetBlockCountMetrics,
52 #[command(about = "Loads FullBlocks between start and end", long_about = None)]
53 GetBlocks {
54 #[arg(long)]
55 start: u32,
56 #[arg(long)]
57 end: u32,
58 #[arg(long)]
59 exclude_header_hash: bool,
60 #[arg(long)]
61 exclude_reorged: bool,
62 },
63 #[command(about = "Loads all FullBlocks between start and end", long_about = None)]
64 GetAllBlocks {
65 #[arg(long)]
66 start: u32,
67 #[arg(long)]
68 end: u32,
69 },
70 #[command(about = "Loads a BlockRecord by header_hash", long_about = None)]
71 GetBlockRecord {
72 #[arg(long)]
73 header_hash: Bytes32,
74 },
75 #[command(about = "Loads a BlockRecord by height", long_about = None)]
76 GetBlockRecordByHeight {
77 #[arg(long)]
78 height: u32,
79 },
80 #[command(about = "Loads all BlockRecords between start and end", long_about = None)]
81 GetBlockRecords {
82 #[arg(long)]
83 start: u32,
84 #[arg(long)]
85 end: u32,
86 },
87 #[command(about = "Loads UnfinishedBlocks", long_about = None)]
88 GetUnfinishedBlocks,
89 #[command(about = "Get Est network Space between two header_hashes", long_about = None)]
90 GetNetworkSpace {
91 #[arg(long)]
92 older_block_header_hash: Bytes32,
93 #[arg(long)]
94 newer_block_header_hash: Bytes32,
95 },
96 #[command(about = "Get Est network Space between two heights", long_about = None)]
97 GetNetworkSpaceaByHeight {
98 #[arg(long)]
99 start: u32,
100 #[arg(long)]
101 end: u32,
102 },
103 #[command(about = "Get additions and removals by header_hash", long_about = None)]
104 GetAdditionsAndRemovals {
105 #[arg(long)]
106 header_hash: Bytes32,
107 },
108 #[command(about = "Loads InitialFreezePeriod", long_about = None)]
109 GetInitialFreezePeriod,
110 #[command(about = "Loads InitialFreezePeriod", long_about = None)]
111 GetNetworkInfo,
112 #[command(about = "Get SignagePoint or End Of Subslot, Only Provide one of sp_hash or challenge_hash", long_about = None)]
113 GetSignagePointOrEOS {
114 #[arg(long)]
115 sp_hash: Option<Bytes32>,
116 #[arg(long)]
117 challenge_hash: Option<Bytes32>,
118 },
119 #[command(about = "Get CoinRecords by puzzle_hashs", long_about = None)]
120 GetCoinRecords {
121 #[arg(long)]
122 puzzle_hashes: Vec<Bytes32>,
123 #[arg(long)]
124 include_spent_coins: Option<bool>,
125 #[arg(long)]
126 start_height: Option<u32>,
127 #[arg(long)]
128 end_height: Option<u32>,
129 },
130 #[command(about = "Get CoinRecord by name", long_about = None)]
131 GetCoinRecordByName {
132 #[arg(long)]
133 name: Bytes32,
134 },
135 #[command(about = "Get CoinRecords by names", long_about = None)]
136 GetCoinRecordsByNames {
137 #[arg(long)]
138 names: Vec<Bytes32>,
139 #[arg(long)]
140 include_spent_coins: bool,
141 #[arg(long)]
142 start_height: u32,
143 #[arg(long)]
144 end_height: u32,
145 },
146 #[command(about = "Get CoinRecords by parent ids", long_about = None)]
147 GetCoinRecordsByParentIds {
148 #[arg(long)]
149 parent_ids: Vec<Bytes32>,
150 #[arg(long)]
151 include_spent_coins: bool,
152 #[arg(long)]
153 start_height: u32,
154 #[arg(long)]
155 end_height: u32,
156 },
157 #[command(about = "Get CoinRecords by hint", long_about = None)]
158 GetCoinRecordsByhint {
159 #[arg(long)]
160 hint: Bytes32,
161 #[arg(long)]
162 include_spent_coins: bool,
163 #[arg(long)]
164 start_height: u32,
165 #[arg(long)]
166 end_height: u32,
167 },
168 #[command(about = "Get CoinSpend for coin_id at height", long_about = None)]
169 GetPuzzleAndSolution {
170 #[arg(long)]
171 coin_id: Bytes32,
172 #[arg(long)]
173 height: u32,
174 },
175 #[command(about = "Get CoinSpend for coin_id at height", long_about = None)]
176 GetCoinSpend {
177 #[arg(long)]
178 coin_id: Bytes32,
179 #[arg(long)]
180 height: u32,
181 },
182 #[command(about = "Loads All Mempool Transaction Ids", long_about = None)]
183 GetAllMempoolTxIds,
184 #[command(about = "Loads All Mempool Items", long_about = None)]
185 GetAllMempoolItems,
186 #[command(about = "Get MempoolItem with TxID", long_about = None)]
187 GetMempoolItemByTxID {
188 #[arg(long)]
189 tx_id: String,
190 },
191 #[command(about = "Get MempoolItem by name", long_about = None)]
192 GetMempoolItemByName {
193 #[arg(long)]
194 coin_name: Bytes32,
195 },
196 #[command(about = "Get MempoolItem by name", long_about = None)]
197 GetFeeEstimate {
198 #[arg(long)]
199 cost: Option<u64>,
200 #[arg(long)]
201 spend_bundle: Option<String>,
202 #[arg(long)]
203 spend_type: Option<String>,
204 #[arg(long)]
205 target_times: Vec<u64>,
206 },
207 #[command(about = "Get Singleton by LauncherID", long_about = None)]
210 GetSingletonByLauncherId {
211 #[arg(long)]
212 launcher_id: Bytes32,
213 },
214 #[command(about = "Get additions and removals with hints by header_hash", long_about = None)]
215 GetAdditionsAndRemovalsWithHints {
216 #[arg(long)]
217 header_hash: Bytes32,
218 },
219 #[command(about = "Get CoinRecords by hint", long_about = None)]
220 GetCoinRecordsByHints {
221 #[arg(long)]
222 hints: Vec<Bytes32>,
223 #[arg(long)]
224 include_spent_coins: bool,
225 #[arg(long)]
226 start_height: u32,
227 #[arg(long)]
228 end_height: u32,
229 },
230 #[command(about = "Get CoinRecords by hint", long_about = None)]
231 GetCoinRecordsByHintsPaginated {
232 #[arg(long)]
233 hints: Vec<Bytes32>,
234 #[arg(long)]
235 include_spent_coins: Option<bool>,
236 #[arg(long)]
237 start_height: Option<u32>,
238 #[arg(long)]
239 end_height: Option<u32>,
240 #[arg(long)]
241 page_size: u32,
242 #[arg(long)]
243 last_id: Option<Bytes32>,
244 },
245 #[command(about = "Get CoinRecords by hint", long_about = None)]
246 GetCoinRecordsByPuzzleHashesPaginated {
247 #[arg(long)]
248 puzzle_hashes: Vec<Bytes32>,
249 #[arg(long)]
250 include_spent_coins: Option<bool>,
251 #[arg(long)]
252 start_height: Option<u32>,
253 #[arg(long)]
254 end_height: Option<u32>,
255 #[arg(long)]
256 page_size: u32,
257 #[arg(long)]
258 last_id: Option<Bytes32>,
259 },
260 #[command(about = "Get Hints by CoinIds", long_about = None)]
261 GetHintsByCoinIds {
262 #[arg(long)]
263 coin_ids: Vec<Bytes32>,
264 },
265 #[command(about = "Get Hints by CoinIds", long_about = None)]
266 GetPuzzleAndSoultionsByNames {
267 #[arg(long)]
268 names: Vec<Bytes32>,
269 #[arg(long)]
270 include_spent_coins: Option<bool>,
271 #[arg(long)]
272 start_height: Option<u32>,
273 #[arg(long)]
274 end_height: Option<u32>,
275 },
276 #[command(about = "Migrates a PlotNFT using a mnemonic", long_about = None)]
278 MovePlotNFT {
279 #[arg(long)]
280 target_pool: String,
281 #[arg(long)]
282 launcher_id: Bytes32,
283 #[arg(long)]
284 target_address: Bytes32,
285 #[arg(long)]
286 mnemonic: String,
287 #[arg(long)]
288 fee: Option<u64>,
289 },
290 #[command(about = "Migrates a PlotNFT using an owner_secrey_key", long_about = None)]
291 MovePlotNFTWithOwnerKey {
292 #[arg(long)]
293 target_pool: String,
294 #[arg(long)]
295 launcher_id: Bytes32,
296 #[arg(long)]
297 target_address: Bytes32,
298 #[arg(long)]
299 owner_key: String,
300 },
301 #[command(about = "Gets plotnft state for launcher_id", long_about = None)]
302 GetPlotnftState {
303 #[arg(long)]
304 launcher_id: Bytes32,
305 },
306 #[command(about = "Create Login link for Pool", long_about = None)]
307 CreatePoolLoginLink {
308 #[arg(short, long)]
309 target_pool: String,
310 #[arg(short, long)]
311 launcher_id: Bytes32,
312 #[arg(short, long)]
313 auth_key: Bytes32,
314 },
315 #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
316 CreateWallet {
317 #[command(subcommand)]
318 action: WalletAction,
319 },
320 #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
321 Curry {
322 #[arg(short = 'p', long = "program")]
323 program: String,
324 #[arg(short = 'a', long = "args")]
325 args: String,
326 #[arg(short = 'o', long = "output")]
327 output: Option<ProgramOutput>,
328 },
329 #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
330 Run {
331 #[arg(short = 'p', long = "program")]
332 program: String,
333 #[arg(short = 'a', long = "args")]
334 args: String,
335 #[arg(short = 'o', long = "output")]
336 output: Option<ProgramOutput>,
337 },
338}
339
340#[derive(Default, ValueEnum, Copy, Clone, Debug)]
341pub enum ProgramOutput {
342 #[default]
343 Hex,
344 String,
345}
346
347#[derive(Debug, Subcommand)]
348pub enum WalletAction {
349 #[command(about = "Creates a wallet with a plotnft", long_about = None)]
350 WithNFT {
351 #[arg(long)]
352 pool_url: Option<String>,
353 faucet_request_url: Option<String>,
354 faucet_request_payload: Option<String>,
355 },
356 #[command(about = "Creates a Cold wallet", long_about = None)]
357 Cold,
358}
359
360pub fn prompt_for_mnemonic() -> Result<Mnemonic, Error> {
361 Mnemonic::from_str(
362 &Input::<String>::with_theme(&ColorfulTheme::default())
363 .with_prompt("Please Input Your Mnemonic: ")
364 .validate_with(|input: &String| -> Result<(), &str> {
365 if Mnemonic::from_str(input).is_ok() {
366 Ok(())
367 } else {
368 Err("You did not input a valid Mnemonic, Please try again.")
369 }
370 })
371 .interact_text()
372 .map_err(|e| {
373 Error::new(
374 ErrorKind::InvalidInput,
375 format!("Failed to read user Input for Mnemonic: {e:?}"),
376 )
377 })?,
378 )
379 .map_err(|e| {
380 Error::new(
381 ErrorKind::InvalidInput,
382 format!("Failed to parse Mnemonic: {e:?}"),
383 )
384 })
385}