1use crate::cli::ProgramOutput;
2use crate::wallet_commands::{
3 create_cold_wallet, get_plotnft_ready_state, migrate_plot_nft, migrate_plot_nft_with_owner_key,
4};
5use crate::wallets::plotnft_utils::{get_plotnft_by_launcher_id, scrounge_for_plotnfts};
6use blst::min_pk::SecretKey;
7use clap::Parser;
8use cli::{prompt_for_mnemonic, Cli, RootCommands, WalletAction};
9use dg_logger::DruidGardenLogger;
10use dg_xch_clients::api::full_node::{FullnodeAPI, FullnodeExtAPI};
11use dg_xch_clients::api::pool::create_pool_login_url;
12use dg_xch_clients::rpc::full_node::FullnodeClient;
13use dg_xch_clients::ClientSSLConfig;
14use dg_xch_core::blockchain::sized_bytes::{Bytes32, Bytes48};
15use dg_xch_core::blockchain::spend_bundle::SpendBundle;
16use dg_xch_core::clvm::assemble::{assemble_text, is_hex};
17use dg_xch_core::clvm::program::SerializedProgram;
18use dg_xch_core::clvm::utils::INFINITE_COST;
19use dg_xch_core::consensus::constants::{CONSENSUS_CONSTANTS_MAP, MAINNET};
20use dg_xch_keys::{
21 encode_puzzle_hash, key_from_mnemonic, master_sk_to_farmer_sk, master_sk_to_pool_sk,
22 master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened,
23};
24use dg_xch_puzzles::clvm_puzzles::launcher_id_to_p2_puzzle_hash;
25use dg_xch_puzzles::p2_delegated_puzzle_or_hidden_puzzle::puzzle_hash_for_pk;
26use dg_xch_serialize::{ChiaProtocolVersion, ChiaSerialize};
27use hex::{decode, encode};
28use log::{error, info, Level};
29use std::env;
30use std::io::{Cursor, Error, ErrorKind};
31use std::path::Path;
32use std::str::FromStr;
33use std::sync::Arc;
34
35pub mod cli;
36pub mod commands;
37pub mod simulator;
38pub mod wallet_commands;
39pub mod wallets;
40
41#[allow(clippy::too_many_lines)]
42#[allow(clippy::cast_sign_loss)]
43pub async fn run_cli() -> Result<(), Error> {
44 let cli = Cli::parse();
45 let _logger = DruidGardenLogger::build()
46 .use_colors(true)
47 .current_level(Level::Info)
48 .init()
49 .map_err(|e| Error::other(format!("{e:?}")))?;
50 let host = cli
51 .fullnode_host
52 .unwrap_or(env::var("FULLNODE_HOST").unwrap_or("localhost".to_string()));
53 let port = cli.fullnode_port.unwrap_or(
54 env::var("FULLNODE_PORT")
55 .map(|s| s.parse().unwrap_or(8555))
56 .unwrap_or(8555),
57 );
58 let timeout = cli.timeout.unwrap_or(60);
59 let ssl = cli.ssl_path.map(|v| ClientSSLConfig {
60 ssl_crt_path: format!("{}/{}", v, "full_node/private_full_node.crt"),
61 ssl_key_path: format!("{}/{}", v, "full_node/private_full_node.crt"),
62 ssl_ca_crt_path: format!("{}/{}", v, "full_node/private_full_node.crt"),
63 });
64 let constants = if let Some(network) = cli.network {
65 CONSENSUS_CONSTANTS_MAP
66 .get(&network)
67 .cloned()
68 .unwrap_or_else(|| MAINNET.clone())
69 } else {
70 MAINNET.clone()
71 };
72 match cli.action {
73 RootCommands::PrintPlottingInfo { launcher_id } => {
74 let client = Arc::new(FullnodeClient::new(&host, port, timeout, ssl, &None)?);
75 let master_key = key_from_mnemonic(&prompt_for_mnemonic()?)?;
76 let mut page = 0;
77 let mut plotnfts = vec![];
78 if let Some(launcher_id) = launcher_id {
79 info!("Searching for NFT with LauncherID: {launcher_id}");
80 if let Some(plotnft) =
81 get_plotnft_by_launcher_id(client.clone(), launcher_id, None).await?
82 {
83 plotnfts.push(plotnft);
84 } else {
85 return Err(Error::new(
86 ErrorKind::NotFound,
87 "Failed to find a plotNFT with LauncherID: {launcher_id}",
88 ));
89 }
90 } else {
91 info!("No LauncherID Specified, Searching for PlotNFTs...");
92 while page < 50 && plotnfts.is_empty() {
93 let mut puzzle_hashes = vec![];
94 for index in page * 50..(page + 1) * 50 {
95 let wallet_sk = master_sk_to_wallet_sk_unhardened(&master_key, index)
96 .map_err(|e| {
97 Error::new(
98 ErrorKind::InvalidInput,
99 format!("Failed to parse Wallet SK: {e:?}"),
100 )
101 })?;
102 let pub_key: Bytes48 = wallet_sk.sk_to_pk().to_bytes().into();
103 puzzle_hashes.push(puzzle_hash_for_pk(pub_key)?);
104 let hardened_wallet_sk = master_sk_to_wallet_sk(&master_key, index)
105 .map_err(|e| {
106 Error::new(
107 ErrorKind::InvalidInput,
108 format!("Failed to parse Wallet SK: {e:?}"),
109 )
110 })?;
111 let pub_key: Bytes48 = hardened_wallet_sk.sk_to_pk().to_bytes().into();
112 puzzle_hashes.push(puzzle_hash_for_pk(pub_key)?);
113 }
114 plotnfts.extend(scrounge_for_plotnfts(client.clone(), &puzzle_hashes).await?);
115 page += 1;
116 }
117 }
118 let farmer_key =
119 Bytes48::from(master_sk_to_farmer_sk(&master_key)?.sk_to_pk().to_bytes());
120 let pool_key = Bytes48::from(master_sk_to_pool_sk(&master_key)?.sk_to_pk().to_bytes());
121 info!("{{");
122 info!("\tFarmerPublicKey(All Plots): {farmer_key},");
123 info!("\tPoolPublicKey(OG Plots): {pool_key},");
124 info!("\tPlotNfts(NFT Plots): {{");
125 let total = plotnfts.len();
126 for (index, plot_nft) in plotnfts.into_iter().enumerate() {
127 info!("\t {{");
128 info!("\t LauncherID: {},", plot_nft.launcher_id);
129 info!(
130 "\t ContractAddress: {}",
131 encode_puzzle_hash(
132 &launcher_id_to_p2_puzzle_hash(
133 plot_nft.launcher_id,
134 plot_nft.delay_time as u64,
135 plot_nft.delay_puzzle_hash,
136 )?,
137 "xch"
138 )?
139 );
140 info!("\t }}{}", if index == total - 1 { "" } else { "," });
141 }
142 info!("\t}}");
143 info!("}}");
144 }
145 RootCommands::GetBlockchainState => {
146 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
147 let results = client.get_blockchain_state().await?;
148 match serde_json::to_string_pretty(&results) {
149 Ok(json) => {
150 println!("{json}");
151 }
152 Err(e) => {
153 error!("Failed to convert value to JSON: {e:?}");
154 }
155 }
156 }
157 RootCommands::GetBlock { header_hash } => {
158 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
159 let results = client.get_block(&header_hash).await?;
160 match serde_json::to_string_pretty(&results) {
161 Ok(json) => {
162 info!("{json}");
163 }
164 Err(e) => {
165 error!("Failed to convert value to JSON: {e:?}");
166 }
167 }
168 }
169 RootCommands::GetBlockCountMetrics => {
170 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
171 let results = client.get_block_count_metrics().await?;
172 match serde_json::to_string_pretty(&results) {
173 Ok(json) => {
174 info!("{json}");
175 }
176 Err(e) => {
177 error!("Failed to convert value to JSON: {e:?}");
178 }
179 }
180 }
181 RootCommands::GetBlocks {
182 start,
183 end,
184 exclude_header_hash,
185 exclude_reorged,
186 } => {
187 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
188 let results = client
189 .get_blocks(start, end, exclude_header_hash, exclude_reorged)
190 .await?;
191 match serde_json::to_string_pretty(&results) {
192 Ok(json) => {
193 info!("{json}");
194 }
195 Err(e) => {
196 error!("Failed to convert value to JSON: {e:?}");
197 }
198 }
199 }
200 RootCommands::GetAllBlocks { start, end } => {
201 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
202 let results = client.get_all_blocks(start, end).await?;
203 match serde_json::to_string_pretty(&results) {
204 Ok(json) => {
205 info!("{json}");
206 }
207 Err(e) => {
208 error!("Failed to convert value to JSON: {e:?}");
209 }
210 }
211 }
212 RootCommands::GetBlockRecord { header_hash } => {
213 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
214 let results = client.get_block_record(&header_hash).await?;
215 match serde_json::to_string_pretty(&results) {
216 Ok(json) => {
217 info!("{json}");
218 }
219 Err(e) => {
220 error!("Failed to convert value to JSON: {e:?}");
221 }
222 }
223 }
224 RootCommands::GetBlockRecordByHeight { height } => {
225 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
226 let results = client.get_block_record_by_height(height).await?;
227 match serde_json::to_string_pretty(&results) {
228 Ok(json) => {
229 info!("{json}");
230 }
231 Err(e) => {
232 error!("Failed to convert value to JSON: {e:?}");
233 }
234 }
235 }
236 RootCommands::GetBlockRecords { start, end } => {
237 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
238 let results = client.get_block_records(start, end).await?;
239 match serde_json::to_string_pretty(&results) {
240 Ok(json) => {
241 info!("{json}");
242 }
243 Err(e) => {
244 error!("Failed to convert value to JSON: {e:?}");
245 }
246 }
247 }
248 RootCommands::GetUnfinishedBlocks => {
249 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
250 let results = client.get_unfinished_block_headers().await?;
251 match serde_json::to_string_pretty(&results) {
252 Ok(json) => {
253 info!("{json}");
254 }
255 Err(e) => {
256 error!("Failed to convert value to JSON: {e:?}");
257 }
258 }
259 }
260 RootCommands::GetNetworkSpace {
261 older_block_header_hash,
262 newer_block_header_hash,
263 } => {
264 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
265 let results = client
266 .get_network_space(&older_block_header_hash, &newer_block_header_hash)
267 .await?;
268 match serde_json::to_string_pretty(&results) {
269 Ok(json) => {
270 info!("{json}");
271 }
272 Err(e) => {
273 error!("Failed to convert value to JSON: {e:?}");
274 }
275 }
276 }
277 RootCommands::GetNetworkSpaceaByHeight { start, end } => {
278 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
279 let results = client.get_network_space_by_height(start, end).await?;
280 match serde_json::to_string_pretty(&results) {
281 Ok(json) => {
282 info!("{json}");
283 }
284 Err(e) => {
285 error!("Failed to convert value to JSON: {e:?}");
286 }
287 }
288 }
289 RootCommands::GetAdditionsAndRemovals { header_hash } => {
290 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
291 let results = client.get_additions_and_removals(&header_hash).await?;
292 match serde_json::to_string_pretty(&results) {
293 Ok(json) => {
294 info!("{json}");
295 }
296 Err(e) => {
297 error!("Failed to convert value to JSON: {e:?}");
298 }
299 }
300 }
301 RootCommands::GetInitialFreezePeriod => {
302 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
303 let results = client.get_initial_freeze_period().await?;
304 match serde_json::to_string_pretty(&results) {
305 Ok(json) => {
306 info!("{json}");
307 }
308 Err(e) => {
309 error!("Failed to convert value to JSON: {e:?}");
310 }
311 }
312 }
313 RootCommands::GetNetworkInfo => {
314 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
315 let results = client.get_network_info().await?;
316 match serde_json::to_string_pretty(&results) {
317 Ok(json) => {
318 info!("{json}");
319 }
320 Err(e) => {
321 error!("Failed to convert value to JSON: {e:?}");
322 }
323 }
324 }
325 RootCommands::GetSignagePointOrEOS {
326 sp_hash,
327 challenge_hash,
328 } => {
329 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
330 let results = client
331 .get_recent_signage_point_or_eos(sp_hash.as_ref(), challenge_hash.as_ref())
332 .await?;
333 match serde_json::to_string_pretty(&results) {
334 Ok(json) => {
335 info!("{json}");
336 }
337 Err(e) => {
338 error!("Failed to convert value to JSON: {e:?}");
339 }
340 }
341 }
342 RootCommands::GetCoinRecords {
343 puzzle_hashes,
344 include_spent_coins,
345 start_height,
346 end_height,
347 } => {
348 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
349 let results = client
350 .get_coin_records_by_puzzle_hashes(
351 &puzzle_hashes,
352 include_spent_coins,
353 start_height,
354 end_height,
355 )
356 .await?;
357 match serde_json::to_string_pretty(&results) {
358 Ok(json) => {
359 info!("{json}");
360 }
361 Err(e) => {
362 error!("Failed to convert value to JSON: {e:?}");
363 }
364 }
365 }
366 RootCommands::GetCoinRecordByName { name } => {
367 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
368 let results = client.get_coin_record_by_name(&name).await?;
369 match serde_json::to_string_pretty(&results) {
370 Ok(json) => {
371 info!("{json}");
372 }
373 Err(e) => {
374 error!("Failed to convert value to JSON: {e:?}");
375 }
376 }
377 }
378 RootCommands::GetCoinRecordsByNames {
379 names,
380 include_spent_coins,
381 start_height,
382 end_height,
383 } => {
384 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
385 let results = client
386 .get_coin_records_by_names(
387 &names,
388 Some(include_spent_coins),
389 Some(start_height),
390 Some(end_height),
391 )
392 .await?;
393 match serde_json::to_string_pretty(&results) {
394 Ok(json) => {
395 info!("{json}");
396 }
397 Err(e) => {
398 error!("Failed to convert value to JSON: {e:?}");
399 }
400 }
401 }
402 RootCommands::GetCoinRecordsByParentIds {
403 parent_ids,
404 include_spent_coins,
405 start_height,
406 end_height,
407 } => {
408 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
409 let results = client
410 .get_coin_records_by_parent_ids(
411 &parent_ids,
412 Some(include_spent_coins),
413 Some(start_height),
414 Some(end_height),
415 )
416 .await?;
417 match serde_json::to_string_pretty(&results) {
418 Ok(json) => {
419 info!("{json}");
420 }
421 Err(e) => {
422 error!("Failed to convert value to JSON: {e:?}");
423 }
424 }
425 }
426 RootCommands::GetCoinRecordsByhint {
427 hint,
428 include_spent_coins,
429 start_height,
430 end_height,
431 } => {
432 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
433 let results = client
434 .get_coin_records_by_hint(
435 &hint,
436 Some(include_spent_coins),
437 Some(start_height),
438 Some(end_height),
439 )
440 .await?;
441 match serde_json::to_string_pretty(&results) {
442 Ok(json) => {
443 info!("{json}");
444 }
445 Err(e) => {
446 error!("Failed to convert value to JSON: {e:?}");
447 }
448 }
449 }
450 RootCommands::GetPuzzleAndSolution { coin_id, height } => {
451 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
452 let results = client.get_puzzle_and_solution(&coin_id, height).await?;
453 match serde_json::to_string_pretty(&results) {
454 Ok(json) => {
455 info!("{json}");
456 }
457 Err(e) => {
458 error!("Failed to convert value to JSON: {e:?}");
459 }
460 }
461 }
462 RootCommands::GetCoinSpend { coin_id, height } => {
463 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
464 let results = client.get_puzzle_and_solution(&coin_id, height).await?;
465 match serde_json::to_string_pretty(&results) {
466 Ok(json) => {
467 info!("{json}");
468 }
469 Err(e) => {
470 error!("Failed to convert value to JSON: {e:?}");
471 }
472 }
473 }
474 RootCommands::GetAllMempoolTxIds => {
475 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
476 let results = client.get_all_mempool_tx_ids().await?;
477 match serde_json::to_string_pretty(&results) {
478 Ok(json) => {
479 info!("{json}");
480 }
481 Err(e) => {
482 error!("Failed to convert value to JSON: {e:?}");
483 }
484 }
485 }
486 RootCommands::GetAllMempoolItems => {
487 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
488 let results = client.get_all_mempool_items().await?;
489 match serde_json::to_string_pretty(&results) {
490 Ok(json) => {
491 info!("{json}");
492 }
493 Err(e) => {
494 error!("Failed to convert value to JSON: {e:?}");
495 }
496 }
497 }
498 RootCommands::GetMempoolItemByTxID { tx_id } => {
499 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
500 let results = client.get_mempool_item_by_tx_id(&tx_id).await?;
501 match serde_json::to_string_pretty(&results) {
502 Ok(json) => {
503 info!("{json}");
504 }
505 Err(e) => {
506 error!("Failed to convert value to JSON: {e:?}");
507 }
508 }
509 }
510 RootCommands::GetMempoolItemByName { coin_name } => {
511 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
512 let results = client.get_mempool_items_by_coin_name(&coin_name).await?;
513 match serde_json::to_string_pretty(&results) {
514 Ok(json) => {
515 info!("{json}");
516 }
517 Err(e) => {
518 error!("Failed to convert value to JSON: {e:?}");
519 }
520 }
521 }
522 RootCommands::GetFeeEstimate {
523 cost,
524 spend_bundle,
525 spend_type,
526 target_times,
527 } => {
528 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
529 let results = client
530 .get_fee_estimate(
531 cost,
532 spend_bundle.map(|s| {
533 if s.starts_with("0x") {
534 let mut cur = Cursor::new(
535 decode(s).expect("String is not valid SpendBundle Hex"),
536 );
537 SpendBundle::from_bytes(&mut cur, ChiaProtocolVersion::default())
538 .expect("String is not valid SpendBundle Hex")
539 } else {
540 serde_json::from_str(&s).expect("String is not a valid SpendBundle")
541 }
542 }),
543 spend_type,
544 &target_times,
545 )
546 .await?;
547 match serde_json::to_string_pretty(&results) {
548 Ok(json) => {
549 info!("{json}");
550 }
551 Err(e) => {
552 error!("Failed to convert value to JSON: {e:?}");
553 }
554 }
555 }
556 RootCommands::GetSingletonByLauncherId { launcher_id } => {
558 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
559 let results = client.get_singleton_by_launcher_id(&launcher_id).await?;
560 match serde_json::to_string_pretty(&results) {
561 Ok(json) => {
562 info!("{json}");
563 }
564 Err(e) => {
565 error!("Failed to convert value to JSON: {e:?}");
566 }
567 }
568 }
569 RootCommands::GetAdditionsAndRemovalsWithHints { header_hash } => {
570 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
571 let results = client
572 .get_additions_and_removals_with_hints(&header_hash)
573 .await?;
574 match serde_json::to_string_pretty(&results) {
575 Ok(json) => {
576 info!("{json}");
577 }
578 Err(e) => {
579 error!("Failed to convert value to JSON: {e:?}");
580 }
581 }
582 }
583 RootCommands::GetCoinRecordsByHints {
584 hints,
585 include_spent_coins,
586 start_height,
587 end_height,
588 } => {
589 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
590 let results = client
591 .get_coin_records_by_hints(
592 &hints,
593 Some(include_spent_coins),
594 Some(start_height),
595 Some(end_height),
596 )
597 .await?;
598 match serde_json::to_string_pretty(&results) {
599 Ok(json) => {
600 info!("{json}");
601 }
602 Err(e) => {
603 error!("Failed to convert value to JSON: {e:?}");
604 }
605 }
606 }
607 RootCommands::GetCoinRecordsByHintsPaginated {
608 hints,
609 include_spent_coins,
610 start_height,
611 end_height,
612 page_size,
613 last_id,
614 } => {
615 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
616 let results = client
617 .get_coin_records_by_hints_paginated(
618 &hints,
619 include_spent_coins,
620 start_height,
621 end_height,
622 page_size,
623 last_id,
624 )
625 .await?;
626 match serde_json::to_string_pretty(&results) {
627 Ok(json) => {
628 info!("{json}");
629 }
630 Err(e) => {
631 error!("Failed to convert value to JSON: {e:?}");
632 }
633 }
634 }
635 RootCommands::GetCoinRecordsByPuzzleHashesPaginated {
636 puzzle_hashes,
637 include_spent_coins,
638 start_height,
639 end_height,
640 page_size,
641 last_id,
642 } => {
643 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
644 let results = client
645 .get_coin_records_by_puzzle_hashes_paginated(
646 &puzzle_hashes,
647 include_spent_coins,
648 start_height,
649 end_height,
650 page_size,
651 last_id,
652 )
653 .await?;
654 match serde_json::to_string_pretty(&results) {
655 Ok(json) => {
656 info!("{json}");
657 }
658 Err(e) => {
659 error!("Failed to convert value to JSON: {e:?}");
660 }
661 }
662 }
663 RootCommands::GetHintsByCoinIds { coin_ids } => {
664 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
665 let results = client.get_hints_by_coin_ids(&coin_ids).await?;
666 match serde_json::to_string_pretty(&results) {
667 Ok(json) => {
668 info!("{json}");
669 }
670 Err(e) => {
671 error!("Failed to convert value to JSON: {e:?}");
672 }
673 }
674 }
675 RootCommands::GetPuzzleAndSoultionsByNames {
676 names,
677 include_spent_coins,
678 start_height,
679 end_height,
680 } => {
681 let client = FullnodeClient::new(&host, port, timeout, ssl, &None)?;
682 let results = client
683 .get_puzzles_and_solutions_by_names(
684 &names,
685 include_spent_coins,
686 start_height,
687 end_height,
688 )
689 .await?;
690 match serde_json::to_string_pretty(&results) {
691 Ok(json) => {
692 info!("{json}");
693 }
694 Err(e) => {
695 error!("Failed to convert value to JSON: {e:?}");
696 }
697 }
698 }
699 RootCommands::MovePlotNFT {
701 target_pool,
702 launcher_id,
703 target_address,
704 mnemonic,
705 fee,
706 } => {
707 let client = Arc::new(FullnodeClient::new(&host, port, timeout, ssl, &None)?);
708 migrate_plot_nft(
709 client,
710 &target_pool,
711 launcher_id,
712 target_address,
713 &mnemonic,
714 constants.clone(),
715 fee.unwrap_or_default(),
716 )
717 .await?;
718 }
719 RootCommands::MovePlotNFTWithOwnerKey {
720 target_pool,
721 launcher_id,
722 target_address,
723 owner_key,
724 } => {
725 let client = Arc::new(FullnodeClient::new(&host, port, timeout, ssl, &None)?);
726 let owner_key = SecretKey::from_bytes(Bytes32::from_str(&owner_key)?.as_ref())
727 .expect("Failed to Parse Owner Secret Key");
728 migrate_plot_nft_with_owner_key(
729 client,
730 &target_pool,
731 launcher_id,
732 target_address,
733 &owner_key,
734 )
735 .await?;
736 }
737 RootCommands::GetPlotnftState { launcher_id } => {
738 let client = Arc::new(FullnodeClient::new(&host, port, timeout, ssl, &None)?);
739 get_plotnft_ready_state(client, launcher_id, None)
740 .await
741 .map(|_| ())?;
742 }
743 RootCommands::CreatePoolLoginLink {
744 target_pool,
745 launcher_id,
746 auth_key,
747 } => {
748 let url =
749 create_pool_login_url(&target_pool, &[(auth_key.into(), launcher_id)]).await?;
750 println!("{url}");
751 }
752 RootCommands::CreateWallet { action } => match action {
753 WalletAction::WithNFT { .. } => {}
754 WalletAction::Cold => create_cold_wallet()?,
755 },
756 RootCommands::Curry {
757 program,
758 args,
759 output,
760 } => {
761 let prog_as_path = Path::new(&program);
762 let asrg_as_path = Path::new(&args);
763 let program = if prog_as_path.exists() {
764 SerializedProgram::from_file(prog_as_path)
765 .await?
766 .to_program()
767 } else if is_hex(program.as_bytes()) {
768 SerializedProgram::from_bytes(program.as_bytes()).to_program()
769 } else {
770 assemble_text(&program)?.to_program()
771 };
772 let args = if asrg_as_path.exists() {
773 SerializedProgram::from_file(asrg_as_path)
774 .await?
775 .to_program()
776 } else if is_hex(args.as_bytes()) {
777 SerializedProgram::from_bytes(args.as_bytes()).to_program()
778 } else {
779 assemble_text(&args)?.to_program()
780 };
781 let curried_program = program.curry(&args.as_list())?;
782 match output.unwrap_or_default() {
783 ProgramOutput::Hex => {
784 println!("{}", encode(&curried_program.serialized))
785 }
786 ProgramOutput::String => {
787 println!("{curried_program}")
788 }
789 }
790 }
791 RootCommands::Run {
792 program,
793 args,
794 output,
795 } => {
796 let prog_as_path = Path::new(&program);
797 let asrg_as_path = Path::new(&args);
798 let program = if prog_as_path.exists() {
799 SerializedProgram::from_file(prog_as_path)
800 .await?
801 .to_program()
802 } else if is_hex(program.as_bytes()) {
803 SerializedProgram::from_bytes(program.as_bytes()).to_program()
804 } else {
805 assemble_text(&program)?.to_program()
806 };
807 let args = if asrg_as_path.exists() {
808 SerializedProgram::from_file(asrg_as_path)
809 .await?
810 .to_program()
811 } else if is_hex(args.as_bytes()) {
812 SerializedProgram::from_bytes(args.as_bytes()).to_program()
813 } else {
814 assemble_text(&args)?.to_program()
815 };
816 let (_cost, program_output) = program.run(INFINITE_COST, 0, &args)?;
817 match output.unwrap_or_default() {
818 ProgramOutput::Hex => {
819 println!("{}", encode(&program_output.serialized))
820 }
821 ProgramOutput::String => {
822 println!("{program_output}")
823 }
824 }
825 }
826 }
827 Ok(())
828}