1use anchor_lang::prelude::Pubkey;
2use anyhow::{anyhow, Result};
3use dateparser::DateTimeUtc;
4use serde::{Deserialize, Serialize};
5
6use super::{data::price_as_lamports, to_pubkey, to_string};
7
8#[derive(Serialize, Deserialize, Debug, Clone, Default)]
9pub struct CandyGuardData {
10 pub default: GuardSet,
11 pub groups: Option<Vec<Group>>,
12}
13
14impl CandyGuardData {
15 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::state::CandyGuardData> {
16 let groups = if let Some(groups) = &self.groups {
17 let mut group_vec = Vec::with_capacity(groups.len());
18
19 for group in groups {
20 group_vec.push(group.to_guard_format()?);
21 }
22
23 Some(group_vec)
24 } else {
25 None
26 };
27
28 Ok(mpl_candy_guard::state::CandyGuardData {
29 default: self.default.to_guard_format()?,
30 groups,
31 })
32 }
33}
34
35#[derive(Serialize, Deserialize, Debug, Clone, Default)]
36pub struct Group {
37 pub label: String,
38 pub guards: GuardSet,
39}
40
41impl Group {
42 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::state::Group> {
43 Ok(mpl_candy_guard::state::Group {
44 label: self.label.clone(),
45 guards: self.guards.to_guard_format()?,
46 })
47 }
48}
49
50#[derive(Serialize, Deserialize, Debug, Clone, Default)]
52#[serde(rename_all = "camelCase")]
53pub struct GuardSet {
54 pub bot_tax: Option<BotTax>,
56 pub sol_payment: Option<SolPayment>,
58 pub token_payment: Option<TokenPayment>,
60 pub start_date: Option<StartDate>,
62 pub third_party_signer: Option<ThirdPartySigner>,
64 pub token_gate: Option<TokenGate>,
66 pub gatekeeper: Option<Gatekeeper>,
68 pub end_date: Option<EndDate>,
70 pub allow_list: Option<AllowList>,
72 pub mint_limit: Option<MintLimit>,
74 pub nft_payment: Option<NftPayment>,
76 pub redeemed_amount: Option<RedeemedAmount>,
78 pub address_gate: Option<AddressGate>,
80 pub nft_gate: Option<NftGate>,
82 pub nft_burn: Option<NftBurn>,
84 pub token_burn: Option<TokenBurn>,
86 pub freeze_sol_payment: Option<FreezeSolPayment>,
88 pub freeze_token_payment: Option<FreezeTokenPayment>,
90}
91
92impl GuardSet {
93 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::GuardSet> {
94 let bot_tax = if let Some(bot_tax) = &self.bot_tax {
96 Some(bot_tax.to_guard_format()?)
97 } else {
98 None
99 };
100 let sol_payment = if let Some(sol_payment) = &self.sol_payment {
102 Some(sol_payment.to_guard_format()?)
103 } else {
104 None
105 };
106 let token_payment = if let Some(token_payment) = &self.token_payment {
108 Some(token_payment.to_guard_format()?)
109 } else {
110 None
111 };
112 let start_date = if let Some(start_date) = &self.start_date {
114 Some(start_date.to_guard_format()?)
115 } else {
116 None
117 };
118 let third_party_signer = if let Some(third_party_signer) = &self.third_party_signer {
120 Some(third_party_signer.to_guard_format()?)
121 } else {
122 None
123 };
124 let token_gate = if let Some(token_gate) = &self.token_gate {
126 Some(token_gate.to_guard_format()?)
127 } else {
128 None
129 };
130 let gatekeeper = if let Some(gatekeeper) = &self.gatekeeper {
132 Some(gatekeeper.to_guard_format()?)
133 } else {
134 None
135 };
136 let end_date = if let Some(end_date) = &self.end_date {
138 Some(end_date.to_guard_format()?)
139 } else {
140 None
141 };
142 let allow_list = if let Some(allow_list) = &self.allow_list {
144 Some(allow_list.to_guard_format()?)
145 } else {
146 None
147 };
148 let mint_limit = if let Some(mint_limit) = &self.mint_limit {
150 Some(mint_limit.to_guard_format()?)
151 } else {
152 None
153 };
154 let nft_payment = if let Some(nft_payment) = &self.nft_payment {
156 Some(nft_payment.to_guard_format()?)
157 } else {
158 None
159 };
160 let redeemed_amount = if let Some(redeemed_amount) = &self.redeemed_amount {
162 Some(redeemed_amount.to_guard_format()?)
163 } else {
164 None
165 };
166 let address_gate = if let Some(address_gate) = &self.address_gate {
168 Some(address_gate.to_guard_format()?)
169 } else {
170 None
171 };
172 let nft_gate = if let Some(nft_gate) = &self.nft_gate {
174 Some(nft_gate.to_guard_format()?)
175 } else {
176 None
177 };
178 let nft_burn = if let Some(nft_burn) = &self.nft_burn {
180 Some(nft_burn.to_guard_format()?)
181 } else {
182 None
183 };
184 let token_burn = if let Some(token_burn) = &self.token_burn {
186 Some(token_burn.to_guard_format()?)
187 } else {
188 None
189 };
190 let freeze_sol_payment = if let Some(freeze_sol_payment) = &self.freeze_sol_payment {
192 Some(freeze_sol_payment.to_guard_format()?)
193 } else {
194 None
195 };
196 let freeze_token_payment = if let Some(freeze_token_payment) = &self.freeze_token_payment {
198 Some(freeze_token_payment.to_guard_format()?)
199 } else {
200 None
201 };
202
203 Ok(mpl_candy_guard::guards::GuardSet {
204 bot_tax,
205 sol_payment,
206 token_payment,
207 start_date,
208 third_party_signer,
209 token_gate,
210 gatekeeper,
211 end_date,
212 allow_list,
213 mint_limit,
214 nft_payment,
215 redeemed_amount,
216 address_gate,
217 nft_gate,
218 nft_burn,
219 token_burn,
220 freeze_sol_payment,
221 freeze_token_payment,
222 program_gate: None,
223 allocation: None,
224 })
225 }
226}
227
228#[derive(Serialize, Deserialize, Debug, Clone, Default)]
231pub struct AddressGate {
232 #[serde(deserialize_with = "to_pubkey")]
233 #[serde(serialize_with = "to_string")]
234 pub address: Pubkey,
235}
236
237impl AddressGate {
238 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::AddressGate> {
239 Ok(mpl_candy_guard::guards::AddressGate {
240 address: self.address,
241 })
242 }
243}
244
245#[derive(Serialize, Deserialize, Debug, Clone, Default)]
248#[serde(rename_all = "camelCase")]
249pub struct AllowList {
250 pub merkle_root: String,
251}
252
253impl AllowList {
254 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::AllowList> {
255 let root: [u8; 32] = hex::decode(&self.merkle_root)?
256 .try_into()
257 .map_err(|_| anyhow!("Invalid merkle root value: {}", self.merkle_root))?;
258 Ok(mpl_candy_guard::guards::AllowList { merkle_root: root })
259 }
260}
261
262#[derive(Serialize, Deserialize, Debug, Clone, Default)]
265#[serde(rename_all = "camelCase")]
266pub struct BotTax {
267 pub value: f64,
268
269 pub last_instruction: bool,
270}
271
272impl BotTax {
273 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::BotTax> {
274 Ok(mpl_candy_guard::guards::BotTax {
275 lamports: price_as_lamports(self.value),
276 last_instruction: self.last_instruction,
277 })
278 }
279}
280
281#[derive(Serialize, Deserialize, Debug, Clone, Default)]
284pub struct EndDate {
285 pub date: String,
286}
287
288impl EndDate {
289 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::EndDate> {
290 let timestamp = self.date.parse::<DateTimeUtc>()?.0.timestamp();
291
292 Ok(mpl_candy_guard::guards::EndDate { date: timestamp })
293 }
294}
295
296#[derive(Serialize, Deserialize, Debug, Clone, Default)]
299#[serde(rename_all = "camelCase")]
300pub struct Gatekeeper {
301 #[serde(deserialize_with = "to_pubkey")]
302 #[serde(serialize_with = "to_string")]
303 pub gatekeeper_network: Pubkey,
304
305 pub expire_on_use: bool,
306}
307
308impl Gatekeeper {
309 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::Gatekeeper> {
310 Ok(mpl_candy_guard::guards::Gatekeeper {
311 gatekeeper_network: self.gatekeeper_network,
312 expire_on_use: self.expire_on_use,
313 })
314 }
315}
316
317#[derive(Serialize, Deserialize, Debug, Clone, Default)]
320pub struct MintLimit {
321 pub id: u8,
322
323 pub limit: u16,
324}
325
326impl MintLimit {
327 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::MintLimit> {
328 Ok(mpl_candy_guard::guards::MintLimit {
329 id: self.id,
330 limit: self.limit,
331 })
332 }
333}
334
335#[derive(Serialize, Deserialize, Debug, Clone, Default)]
338#[serde(rename_all = "camelCase")]
339pub struct NftBurn {
340 #[serde(deserialize_with = "to_pubkey")]
341 #[serde(serialize_with = "to_string")]
342 pub required_collection: Pubkey,
343}
344
345impl NftBurn {
346 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftBurn> {
347 Ok(mpl_candy_guard::guards::NftBurn {
348 required_collection: self.required_collection,
349 })
350 }
351}
352
353#[derive(Serialize, Deserialize, Debug, Clone, Default)]
356#[serde(rename_all = "camelCase")]
357pub struct NftGate {
358 #[serde(deserialize_with = "to_pubkey")]
359 #[serde(serialize_with = "to_string")]
360 pub required_collection: Pubkey,
361}
362
363impl NftGate {
364 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftGate> {
365 Ok(mpl_candy_guard::guards::NftGate {
366 required_collection: self.required_collection,
367 })
368 }
369}
370
371#[derive(Serialize, Deserialize, Debug, Clone, Default)]
374#[serde(rename_all = "camelCase")]
375pub struct NftPayment {
376 #[serde(deserialize_with = "to_pubkey")]
377 #[serde(serialize_with = "to_string")]
378 pub required_collection: Pubkey,
379
380 #[serde(deserialize_with = "to_pubkey")]
381 #[serde(serialize_with = "to_string")]
382 pub destination: Pubkey,
383}
384
385impl NftPayment {
386 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftPayment> {
387 Ok(mpl_candy_guard::guards::NftPayment {
388 required_collection: self.required_collection,
389 destination: self.destination,
390 })
391 }
392}
393
394#[derive(Serialize, Deserialize, Debug, Clone, Default)]
397pub struct RedeemedAmount {
398 pub maximum: u64,
399}
400
401impl RedeemedAmount {
402 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::RedeemedAmount> {
403 Ok(mpl_candy_guard::guards::RedeemedAmount {
404 maximum: self.maximum,
405 })
406 }
407}
408
409#[derive(Serialize, Deserialize, Debug, Clone, Default)]
412pub struct SolPayment {
413 pub value: f64,
414
415 #[serde(deserialize_with = "to_pubkey")]
416 #[serde(serialize_with = "to_string")]
417 pub destination: Pubkey,
418}
419
420impl SolPayment {
421 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::SolPayment> {
422 Ok(mpl_candy_guard::guards::SolPayment {
423 lamports: price_as_lamports(self.value),
424 destination: self.destination,
425 })
426 }
427}
428
429#[derive(Serialize, Deserialize, Debug, Clone, Default)]
430pub struct StartDate {
431 pub date: String,
432}
433
434impl StartDate {
435 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::StartDate> {
436 let timestamp = self.date.parse::<DateTimeUtc>()?.0.timestamp();
437 Ok(mpl_candy_guard::guards::StartDate { date: timestamp })
438 }
439}
440
441#[derive(Serialize, Deserialize, Debug, Clone, Default)]
444#[serde(rename_all = "camelCase")]
445pub struct ThirdPartySigner {
446 #[serde(deserialize_with = "to_pubkey")]
447 #[serde(serialize_with = "to_string")]
448 pub signer_key: Pubkey,
449}
450
451impl ThirdPartySigner {
452 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::ThirdPartySigner> {
453 Ok(mpl_candy_guard::guards::ThirdPartySigner {
454 signer_key: self.signer_key,
455 })
456 }
457}
458
459#[derive(Serialize, Deserialize, Debug, Clone, Default)]
462pub struct TokenBurn {
463 pub amount: u64,
464
465 #[serde(deserialize_with = "to_pubkey")]
466 #[serde(serialize_with = "to_string")]
467 pub mint: Pubkey,
468}
469
470impl TokenBurn {
471 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenBurn> {
472 Ok(mpl_candy_guard::guards::TokenBurn {
473 amount: self.amount,
474 mint: self.mint,
475 })
476 }
477}
478
479#[derive(Serialize, Deserialize, Debug, Clone, Default)]
482pub struct TokenGate {
483 pub amount: u64,
484
485 #[serde(deserialize_with = "to_pubkey")]
486 #[serde(serialize_with = "to_string")]
487 pub mint: Pubkey,
488}
489
490impl TokenGate {
491 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenGate> {
492 Ok(mpl_candy_guard::guards::TokenGate {
493 amount: self.amount,
494 mint: self.mint,
495 })
496 }
497}
498
499#[derive(Serialize, Deserialize, Debug, Clone, Default)]
502#[serde(rename_all = "camelCase")]
503pub struct TokenPayment {
504 pub amount: u64,
505
506 #[serde(deserialize_with = "to_pubkey")]
507 #[serde(serialize_with = "to_string")]
508 pub mint: Pubkey,
509
510 #[serde(deserialize_with = "to_pubkey")]
511 #[serde(serialize_with = "to_string")]
512 pub destination_ata: Pubkey,
513}
514
515impl TokenPayment {
516 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenPayment> {
517 Ok(mpl_candy_guard::guards::TokenPayment {
518 amount: self.amount,
519 mint: self.mint,
520 destination_ata: self.destination_ata,
521 })
522 }
523}
524
525#[derive(Serialize, Deserialize, Debug, Clone, Default)]
528#[serde(rename_all = "camelCase")]
529pub struct FreezeSolPayment {
530 pub value: f64,
531
532 #[serde(deserialize_with = "to_pubkey")]
533 #[serde(serialize_with = "to_string")]
534 pub destination: Pubkey,
535}
536
537impl FreezeSolPayment {
538 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::FreezeSolPayment> {
539 Ok(mpl_candy_guard::guards::FreezeSolPayment {
540 lamports: price_as_lamports(self.value),
541 destination: self.destination,
542 })
543 }
544}
545
546#[derive(Serialize, Deserialize, Debug, Clone, Default)]
549#[serde(rename_all = "camelCase")]
550pub struct FreezeTokenPayment {
551 pub amount: u64,
552
553 #[serde(deserialize_with = "to_pubkey")]
554 #[serde(serialize_with = "to_string")]
555 pub mint: Pubkey,
556
557 #[serde(deserialize_with = "to_pubkey")]
558 #[serde(serialize_with = "to_string")]
559 pub destination_ata: Pubkey,
560}
561
562impl FreezeTokenPayment {
563 pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::FreezeTokenPayment> {
564 Ok(mpl_candy_guard::guards::FreezeTokenPayment {
565 amount: self.amount,
566 mint: self.mint,
567 destination_ata: self.destination_ata,
568 })
569 }
570}