1use anchor_client::solana_sdk::{
2 pubkey::Pubkey,
3 signature::{Keypair, Signature, Signer},
4 system_instruction, system_program,
5};
6use anyhow::Result;
7use mpl_candy_machine_core::{
8 accounts as nft_accounts, instruction as nft_instruction, CandyMachineData, ConfigLineSettings,
9 Creator as CandyCreator,
10};
11pub use mpl_token_metadata::state::{
12 MAX_CREATOR_LIMIT, MAX_NAME_LENGTH, MAX_SYMBOL_LENGTH, MAX_URI_LENGTH,
13};
14use mpl_token_metadata::{
15 instruction::MetadataDelegateRole, pda::find_metadata_delegate_record_account,
16 state::TokenStandard,
17};
18use solana_program::native_token::LAMPORTS_PER_MLN;
19
20use crate::{
21 common::*,
22 config::data::*,
23 deploy::errors::*,
24 pdas::{find_candy_machine_creator_pda, find_master_edition_pda, find_metadata_pda},
25};
26
27pub fn create_candy_machine_data(
29 _client: &Client,
30 config: &ConfigData,
31 cache: &Cache,
32) -> Result<CandyMachineData> {
33 let mut creators: Vec<CandyCreator> = Vec::new();
34 let mut share = 0u32;
35
36 for creator in &config.creators {
37 let c = creator.to_candy_format()?;
38 share += c.percentage_share as u32;
39
40 creators.push(c);
41 }
42
43 if creators.is_empty() || creators.len() > (MAX_CREATOR_LIMIT - 1) {
44 return Err(anyhow!(
45 "The number of creators must be between 1 and {}.",
46 MAX_CREATOR_LIMIT - 1,
47 ));
48 }
49
50 if share != 100 {
51 return Err(anyhow!(
52 "Creator(s) share must add up to 100, current total {}.",
53 share,
54 ));
55 }
56
57 let config_line_settings = if config.hidden_settings.is_some() {
58 None
59 } else {
60 let mut name_pair = [String::new(), String::new(), String::new()];
66 let mut uri_pair = [String::new(), String::new(), String::new()];
68 let compare_pair = |value: &String, pair: &mut [String; 3]| {
70 if pair[0].is_empty() || value < &pair[0] {
72 pair[0] = value.to_string();
73 }
74 if value > &pair[1] {
76 pair[1] = value.to_string();
77 }
78 if value.len() > pair[2].len() {
80 pair[2] = value.to_string();
81 }
82 };
83 let common_prefix = |value1: &str, value2: &str| {
84 let bytes1 = value1.as_bytes();
85 let bytes2 = value2.as_bytes();
86 let mut index = 0;
87
88 while (index < bytes1.len() && index < bytes2.len()) && bytes1[index] == bytes2[index] {
89 index += 1;
90 }
91
92 value1[..index].to_string()
93 };
94
95 for (index, item) in cache.items.iter() {
96 if i64::from_str(index)? > -1 {
97 compare_pair(&item.name, &mut name_pair);
98 compare_pair(&item.metadata_link, &mut uri_pair);
99 }
100 }
101
102 let name_prefix = common_prefix(&name_pair[0], &name_pair[1]);
103 let uri_prefix = common_prefix(&uri_pair[0], &uri_pair[1]);
104
105 Some(ConfigLineSettings {
106 name_length: (name_pair[2].len() - name_prefix.len()) as u32,
107 prefix_name: name_prefix,
108 uri_length: (uri_pair[2].len() - uri_prefix.len()) as u32,
109 prefix_uri: uri_prefix,
110 is_sequential: config.is_sequential,
111 })
112 };
113
114 let hidden_settings = config.hidden_settings.as_ref().map(|s| s.to_candy_format());
115
116 let data = CandyMachineData {
117 items_available: config.number,
118 symbol: config.symbol.clone(),
119 seller_fee_basis_points: config.seller_fee_basis_points,
120 max_supply: 0,
121 is_mutable: config.is_mutable,
122 creators,
123 config_line_settings,
124 hidden_settings,
125 };
126
127 Ok(data)
128}
129
130pub fn initialize_candy_machine(
132 config_data: &ConfigData,
133
134 candy_account: &Keypair,
135 candy_machine_data: CandyMachineData,
136 collection_mint: Pubkey,
137 collection_update_authority: Pubkey,
138 program: Program,
139) -> Result<Signature> {
140 let payer = program.payer();
141 let candy_account_size = candy_machine_data.get_space_for_candy()?;
142
143 info!(
144 "Initializing candy machine with account size of: {} and address of: {}",
145 candy_account_size,
146 candy_account.pubkey().to_string()
147 );
148
149 let lamports = program
150 .rpc()
151 .get_minimum_balance_for_rent_exemption(candy_account_size)?;
152
153 let balance = program.rpc().get_account(&payer)?.lamports;
154
155 if lamports > balance {
156 return Err(DeployError::BalanceTooLow(
157 format!("{:.3}", (balance as f64 / LAMPORTS_PER_MLN as f64)),
158 format!("{:.3}", (lamports as f64 / LAMPORTS_PER_MLN as f64)),
159 )
160 .into());
161 }
162
163 let (authority_pda, _) = find_candy_machine_creator_pda(&candy_account.pubkey());
166
167 let collection_metadata = find_metadata_pda(&collection_mint);
168 let collection_master_edition = find_master_edition_pda(&collection_mint);
169 let (collection_delegate_record, _) = find_metadata_delegate_record_account(
170 &collection_mint,
171 MetadataDelegateRole::Collection,
172 &collection_update_authority,
173 &authority_pda,
174 );
175
176 let tx = program
177 .request()
178 .instruction(system_instruction::create_account(
179 &payer,
180 &candy_account.pubkey(),
181 lamports,
182 candy_account_size as u64,
183 &program.id(),
184 ))
185 .signer(candy_account)
186 .accounts(nft_accounts::InitializeV2 {
187 candy_machine: candy_account.pubkey(),
188 authority: payer,
189 authority_pda,
190 payer,
191 collection_metadata,
192 collection_mint,
193 collection_master_edition,
194 collection_update_authority,
195 collection_delegate_record,
196 rule_set: config_data.rule_set,
197 token_metadata_program: mpl_token_metadata::ID,
198 system_program: system_program::id(),
199 sysvar_instructions: sysvar::instructions::ID,
200 authorization_rules_program: None,
201 authorization_rules: None,
202 })
203 .args(nft_instruction::InitializeV2 {
204 data: candy_machine_data,
205 token_standard: <crate::config::data::TokenStandard as std::convert::Into<
206 TokenStandard,
207 >>::into(config_data.token_standard) as u8,
208 });
209
210 let sig = tx.send()?;
211
212 Ok(sig)
213}