sugar_cli/guard/
add.rs

1use std::str::FromStr;
2
3use anchor_client::solana_sdk::pubkey::Pubkey;
4use anyhow::Result;
5use console::style;
6use mpl_candy_guard::{
7    accounts::{Initialize as InitializeAccount, Update as UpdateAccount, Wrap as WrapAccount},
8    instruction::{Initialize, Update, Wrap},
9};
10use mpl_candy_machine_core::constants::EMPTY_STR;
11
12use crate::{cache::load_cache, candy_machine::*, common::*, config::get_config_data, utils::*};
13
14pub struct GuardAddArgs {
15    pub keypair: Option<String>,
16    pub rpc_url: Option<String>,
17    pub cache: String,
18    pub config: String,
19    pub candy_machine: Option<String>,
20    pub candy_guard: Option<String>,
21}
22
23pub fn process_guard_add(args: GuardAddArgs) -> Result<()> {
24    println!("[1/3] {}Looking up candy machine", LOOKING_GLASS_EMOJI);
25
26    let pb = spinner_with_style();
27    pb.set_message("Connecting...");
28
29    // the candy machine id specified takes precedence over the one from the cache
30
31    let (candy_machine_id, cache) = if let Some(candy_machine) = args.candy_machine {
32        (candy_machine, None)
33    } else {
34        let cache = load_cache(&args.cache, false)?;
35        (cache.program.candy_machine.clone(), Some(cache))
36    };
37
38    if candy_machine_id.is_empty() {
39        return Err(anyhow!("Missing candy machine id."));
40    }
41
42    let candy_machine_id = match Pubkey::from_str(&candy_machine_id) {
43        Ok(candy_machine_id) => candy_machine_id,
44        Err(_) => {
45            let error = anyhow!("Failed to parse candy machine id: {}", candy_machine_id);
46            error!("{:?}", error);
47            return Err(error);
48        }
49    };
50
51    pb.finish_and_clear();
52
53    println!(
54        "\n{} {}",
55        style("Candy machine ID:").bold(),
56        candy_machine_id
57    );
58
59    // decide whether to create a new candy guard or use an existing one
60
61    let candy_guard_id = if let Some(candy_guard) = args.candy_guard {
62        candy_guard
63    } else if let Some(ref cache) = cache {
64        cache.program.candy_guard.clone()
65    } else {
66        EMPTY_STR.to_string()
67    };
68
69    let sugar_config = sugar_setup(args.keypair, args.rpc_url)?;
70    let config_data = get_config_data(&args.config)?;
71    let client = setup_client(&sugar_config)?;
72    let payer = sugar_config.keypair;
73    let program = client.program(mpl_candy_guard::ID);
74
75    let candy_guard = if candy_guard_id.is_empty() {
76        println!("\n[2/3] {}Initializing a candy guard", GUARD_EMOJI);
77        let pb = spinner_with_style();
78        pb.set_message("Initializing...");
79
80        let data = if let Some(guards) = &config_data.guards {
81            guards.to_guard_format()?
82        } else {
83            return Err(anyhow!("Missing guards configuration."));
84        };
85
86        let base = Keypair::new();
87        let (candy_guard, _) = Pubkey::find_program_address(
88            &[b"candy_guard", base.pubkey().as_ref()],
89            &mpl_candy_guard::ID,
90        );
91
92        let mut serialized_data = vec![0; data.size()];
93        data.save(&mut serialized_data)?;
94
95        let tx = program
96            .request()
97            .accounts(InitializeAccount {
98                candy_guard,
99                base: base.pubkey(),
100                authority: payer.pubkey(),
101                payer: payer.pubkey(),
102                system_program: system_program::id(),
103            })
104            .args(Initialize {
105                data: serialized_data,
106            })
107            .signer(&base);
108
109        let sig = tx.send()?;
110
111        pb.finish_and_clear();
112        println!("{} {}", style("Signature:").bold(), sig);
113
114        candy_guard
115    } else {
116        println!("\n[2/3] {}Loading candy guard", COMPUTER_EMOJI);
117
118        let candy_guard_id = match Pubkey::from_str(&candy_guard_id) {
119            Ok(candy_guard_id) => candy_guard_id,
120            Err(_) => {
121                let error = anyhow!("Failed to parse candy guard id: {}", candy_guard_id);
122                error!("{:?}", error);
123                return Err(error);
124            }
125        };
126
127        let pb = spinner_with_style();
128        pb.set_message("Connecting...");
129
130        // validates that the account exists
131        let _candy_guard = program.rpc().get_account(&candy_guard_id)?;
132
133        let data = if let Some(guards) = &config_data.guards {
134            guards.to_guard_format()?
135        } else {
136            return Err(anyhow!("Missing guards configuration."));
137        };
138
139        let mut serialized_data = vec![0; data.size()];
140        data.save(&mut serialized_data)?;
141        println!("\n{}\n", serialized_data.len());
142
143        // synchronizes the guards config with the on-chain account
144        let tx = program
145            .request()
146            .accounts(UpdateAccount {
147                candy_guard: candy_guard_id,
148                authority: payer.pubkey(),
149                payer: payer.pubkey(),
150                system_program: system_program::ID,
151            })
152            .args(Update {
153                data: serialized_data,
154            });
155
156        tx.send()?;
157
158        pb.finish_with_message("Done");
159
160        candy_guard_id
161    };
162
163    println!("\n{} {}", style("Candy guard ID:").bold(), candy_guard);
164
165    // wraps the candy machine
166
167    println!("\n[3/3] {}Wrapping", WRAP_EMOJI);
168
169    let pb = spinner_with_style();
170    pb.set_message("Connecting...");
171
172    let tx = program
173        .request()
174        .accounts(WrapAccount {
175            candy_guard,
176            authority: payer.pubkey(),
177            candy_machine: candy_machine_id,
178            candy_machine_program: CANDY_MACHINE_ID,
179            candy_machine_authority: payer.pubkey(),
180        })
181        .args(Wrap {});
182
183    let sig = tx.send()?;
184
185    pb.finish_and_clear();
186    println!("{} {}", style("Signature:").bold(), sig);
187
188    println!("\nThe candy guard is now the mint authority of the candy machine.");
189
190    // if we created a new candy guard from the candy machine on the cache file,
191    // we store the reference of the candy guard on the cache
192
193    if cache.is_some() {
194        let mut cache = load_cache(&args.cache, false)?;
195        cache.program.candy_guard = candy_guard.to_string();
196        cache.sync_file()?;
197    }
198
199    Ok(())
200}