1use std::str::FromStr;
2
3use anchor_client::solana_sdk::pubkey::Pubkey;
4use anyhow::Result;
5use chrono::NaiveDateTime;
6use console::style;
7use mpl_candy_guard::state::{CandyGuard, CandyGuardData, GuardSet, DATA_OFFSET};
8use mpl_candy_machine_core::constants::EMPTY_STR;
9use solana_program::native_token::LAMPORTS_PER_MLN;
10
11use crate::{cache::load_cache, common::*, show::print_with_style, utils::*};
12
13pub struct GuardShowArgs {
14 pub keypair: Option<String>,
15 pub rpc_url: Option<String>,
16 pub cache: String,
17 pub candy_guard: Option<String>,
18}
19
20pub fn process_guard_show(args: GuardShowArgs) -> Result<()> {
21 println!("[1/1] {}Loading candy guard", LOOKING_GLASS_EMOJI);
22
23 let candy_guard_id = if let Some(candy_guard) = args.candy_guard {
26 candy_guard
27 } else {
28 let cache = load_cache(&args.cache, false)?;
29 cache.program.candy_guard
30 };
31
32 if candy_guard_id.is_empty() {
33 return Err(anyhow!("Missing candy guard id."));
34 }
35
36 let candy_guard_id = match Pubkey::from_str(&candy_guard_id) {
37 Ok(candy_guard_id) => candy_guard_id,
38 Err(_) => {
39 let error = anyhow!("Failed to parse candy guard id: {}", candy_guard_id);
40 error!("{:?}", error);
41 return Err(error);
42 }
43 };
44
45 let sugar_config = sugar_setup(args.keypair, args.rpc_url)?;
46 let client = setup_client(&sugar_config)?;
47 let program = client.program(mpl_candy_guard::ID);
48
49 let pb = spinner_with_style();
50 pb.set_message("Connecting...");
51
52 let account: CandyGuard = program.account(candy_guard_id)?;
53 let account_data = program.rpc().get_account_data(&candy_guard_id)?;
54 let candy_guard_data = CandyGuardData::load(&account_data[DATA_OFFSET..])?;
56
57 pb.finish_with_message("Done");
58
59 println!(
60 "\n{}{} {}",
61 GUARD_EMOJI,
62 style("Candy Guard ID:").dim(),
63 &candy_guard_id
64 );
65
66 println!(" {}", style(":").dim());
69 print_with_style("", "base", account.base.to_string());
70 print_with_style("", "bump", account.bump.to_string());
71 print_with_style("", "authority", account.authority.to_string());
72 print_with_style("", "data", EMPTY_STR.to_string());
73
74 print_with_style(" ", "default", EMPTY_STR.to_string());
76 print_guard_set(&candy_guard_data.default, " : ".to_string())?;
77
78 if let Some(groups) = candy_guard_data.groups {
80 println!(" {}", style(":").dim());
81 print_with_style(" ", "groups", EMPTY_STR.to_string());
82
83 for (index, group) in groups.iter().enumerate() {
84 if index > 0 {
85 println!(" {}", style(":").dim());
87 }
88 print_with_style(" ", "label", &group.label);
89 print_guard_set(
90 &group.guards,
91 if index == (groups.len() - 1) {
92 " ".to_string()
93 } else {
94 " : ".to_string()
95 },
96 )?;
97 }
98 } else {
99 print_with_style(" ", "groups", "none".to_string());
100 }
101
102 Ok(())
103}
104
105fn print_guard_set(guard_set: &GuardSet, padding: String) -> Result<()> {
106 if let Some(bot_tax) = &guard_set.bot_tax {
108 print_with_style(&padding, "bot tax", EMPTY_STR.to_string());
109 print_with_style(
110 &format!("{}: ", padding),
111 "lamports",
112 format!(
113 "{} (◎ {})",
114 bot_tax.lamports,
115 bot_tax.lamports as f64 / LAMPORTS_PER_MLN as f64
116 ),
117 );
118 print_with_style(
119 &format!("{}: ", padding),
120 "last instruction",
121 bot_tax.last_instruction.to_string(),
122 );
123 } else {
124 print_with_style(&padding, "bot tax", "none".to_string());
125 }
126
127 if let Some(sol_payment) = &guard_set.sol_payment {
129 print_with_style(&padding, "sol payment", EMPTY_STR.to_string());
130 print_with_style(
131 &format!("{}: ", padding),
132 "lamports",
133 format!(
134 "{} (◎ {})",
135 sol_payment.lamports,
136 sol_payment.lamports as f64 / LAMPORTS_PER_MLN as f64
137 ),
138 );
139 print_with_style(
140 &format!("{}: ", padding),
141 "destination",
142 sol_payment.destination.to_string(),
143 );
144 } else {
145 print_with_style(&padding, "sol payment", "none".to_string());
146 }
147
148 if let Some(token_payment) = &guard_set.token_payment {
150 print_with_style(&padding, "token payment", EMPTY_STR.to_string());
151 print_with_style(
152 &format!("{}: ", padding),
153 "amount",
154 token_payment.amount.to_string(),
155 );
156 print_with_style(
157 &format!("{}: ", padding),
158 "token mint",
159 token_payment.mint.to_string(),
160 );
161 print_with_style(
162 &format!("{}: ", padding),
163 "destination",
164 token_payment.destination_ata.to_string(),
165 );
166 } else {
167 print_with_style(&padding, "token payment", "none".to_string());
168 }
169
170 if let Some(start_date) = &guard_set.start_date {
172 print_with_style(&padding, "start date", EMPTY_STR.to_string());
173 if let Some(date) = NaiveDateTime::from_timestamp_opt(start_date.date, 0) {
174 print_with_style(
175 &format!("{}: ", padding),
176 "date",
177 date.format("%a %B %e %Y %H:%M:%S UTC").to_string(),
178 );
179 } else {
180 print_with_style(&format!("{}: ", padding), "date", "<parse error>");
183 }
184 } else {
185 print_with_style(&padding, "start date", "none".to_string());
186 }
187
188 if let Some(third_party_signer) = &guard_set.third_party_signer {
190 print_with_style(&padding, "third party signer", EMPTY_STR.to_string());
191 print_with_style(
192 &format!("{}: ", padding),
193 "signer key",
194 third_party_signer.signer_key.to_string(),
195 );
196 } else {
197 print_with_style(&padding, "third party signer", "none".to_string());
198 }
199
200 if let Some(token_gate) = &guard_set.token_gate {
202 print_with_style(&padding, "token gate", EMPTY_STR.to_string());
203 print_with_style(
204 &format!("{}: ", padding),
205 "amount",
206 token_gate.amount.to_string(),
207 );
208 print_with_style(
209 &format!("{}: ", padding),
210 "mint",
211 token_gate.mint.to_string(),
212 );
213 } else {
214 print_with_style(&padding, "token gate", "none".to_string());
215 }
216
217 if let Some(gatekeeper) = &guard_set.gatekeeper {
219 print_with_style(&padding, "gatekeeper", EMPTY_STR.to_string());
220 print_with_style(
221 &format!("{}: ", padding),
222 "gatekeeper network",
223 gatekeeper.gatekeeper_network.to_string(),
224 );
225 print_with_style(
226 &format!("{}: ", padding),
227 "expire_on_use",
228 gatekeeper.expire_on_use.to_string(),
229 );
230 } else {
231 print_with_style(&padding, "gatekeeper", "none".to_string());
232 }
233
234 if let Some(end_date) = &guard_set.end_date {
236 print_with_style(&padding, "end date", EMPTY_STR.to_string());
237 if let Some(date) = NaiveDateTime::from_timestamp_opt(end_date.date, 0) {
238 print_with_style(
239 &format!("{}: ", padding),
240 "date",
241 date.format("%a %B %e %Y %H:%M:%S UTC").to_string(),
242 );
243 } else {
244 print_with_style(&format!("{}: ", padding), "date", "<parse error>");
247 }
248 } else {
249 print_with_style(&padding, "end date", "none".to_string());
250 }
251
252 if let Some(allow_list) = &guard_set.allow_list {
254 print_with_style(&padding, "allow list", EMPTY_STR.to_string());
255 print_with_style(
256 &format!("{}: ", padding),
257 "merkle root",
258 hex::encode(allow_list.merkle_root),
259 );
260 } else {
261 print_with_style(&padding, "allow list", "none".to_string());
262 }
263
264 if let Some(mint_limit) = &guard_set.mint_limit {
266 print_with_style(&padding, "mint limit", EMPTY_STR.to_string());
267 print_with_style(&format!("{}: ", padding), "id", mint_limit.id.to_string());
268 print_with_style(
269 &format!("{}: ", padding),
270 "amount",
271 mint_limit.limit.to_string(),
272 );
273 } else {
274 print_with_style(&padding, "mint limit", "none".to_string());
275 }
276
277 if let Some(nft_payment) = &guard_set.nft_payment {
279 print_with_style(&padding, "nft payment", EMPTY_STR.to_string());
280 print_with_style(
281 &format!("{}: ", padding),
282 "required collection",
283 nft_payment.required_collection.to_string(),
284 );
285 print_with_style(
286 &format!("{}: ", padding),
287 "destination",
288 nft_payment.destination.to_string(),
289 );
290 } else {
291 print_with_style(&padding, "nft payment", "none".to_string());
292 }
293
294 if let Some(redeemed_amount) = &guard_set.redeemed_amount {
296 print_with_style(&padding, "redeemed amount", EMPTY_STR.to_string());
297 print_with_style(
298 &format!("{}: ", padding),
299 "amount",
300 redeemed_amount.maximum.to_string(),
301 );
302 } else {
303 print_with_style(&padding, "redeemed amount", "none".to_string());
304 }
305
306 if let Some(address_gate) = &guard_set.address_gate {
308 print_with_style(&padding, "address gate", EMPTY_STR.to_string());
309 print_with_style(
310 &format!("{}: ", padding),
311 "address",
312 address_gate.address.to_string(),
313 );
314 } else {
315 print_with_style(&padding, "address gate", "none".to_string());
316 }
317
318 if let Some(nft_gate) = &guard_set.nft_gate {
320 print_with_style(&padding, "nft gate", EMPTY_STR.to_string());
321 print_with_style(
322 &format!("{}: ", padding),
323 "required_collection",
324 nft_gate.required_collection.to_string(),
325 );
326 } else {
327 print_with_style(&padding, "nft gate", "none".to_string());
328 }
329
330 if let Some(nft_burn) = &guard_set.nft_burn {
332 print_with_style(&padding, "nft burn", EMPTY_STR.to_string());
333 print_with_style(
334 &format!("{}: ", padding),
335 "required_collection",
336 nft_burn.required_collection.to_string(),
337 );
338 } else {
339 print_with_style(&padding, "nft burn", "none".to_string());
340 }
341
342 if let Some(token_burn) = &guard_set.token_burn {
344 print_with_style(&padding, "token burn", EMPTY_STR.to_string());
345 print_with_style(
346 &format!("{} ", padding),
347 "amount",
348 token_burn.amount.to_string(),
349 );
350 print_with_style(
351 &format!("{} ", padding),
352 "mint",
353 token_burn.mint.to_string(),
354 );
355 } else {
356 print_with_style(&padding, "token burn", "none".to_string());
357 }
358
359 if let Some(freeze_sol_payment) = &guard_set.freeze_sol_payment {
361 print_with_style(&padding, "freeze sol payment", EMPTY_STR.to_string());
362 print_with_style(
363 &format!("{}: ", padding),
364 "lamports",
365 format!(
366 "{} (◎ {})",
367 freeze_sol_payment.lamports,
368 freeze_sol_payment.lamports as f64 / LAMPORTS_PER_MLN as f64
369 ),
370 );
371 print_with_style(
372 &format!("{}: ", padding),
373 "destination",
374 freeze_sol_payment.destination.to_string(),
375 );
376 } else {
377 print_with_style(&padding, "freeze sol payment", "none".to_string());
378 }
379
380 if let Some(freeze_token_payment) = &guard_set.freeze_token_payment {
382 print_with_style(&padding, "freeze token payment", EMPTY_STR.to_string());
383 print_with_style(
384 &format!("{}: ", padding),
385 "amount",
386 freeze_token_payment.amount.to_string(),
387 );
388 print_with_style(
389 &format!("{}: ", padding),
390 "token mint",
391 freeze_token_payment.mint.to_string(),
392 );
393 print_with_style(
394 &format!("{}: ", padding),
395 "destination",
396 freeze_token_payment.destination_ata.to_string(),
397 );
398 } else {
399 print_with_style(&padding, "freeze token payment", "none".to_string());
400 }
401
402 Ok(())
403}