use anchor_lang::prelude::Pubkey;
use anyhow::{anyhow, Result};
use dateparser::DateTimeUtc;
use serde::{Deserialize, Serialize};
use super::{data::price_as_lamports, to_pubkey, to_string};
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct CandyGuardData {
pub default: GuardSet,
pub groups: Option<Vec<Group>>,
}
impl CandyGuardData {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::state::CandyGuardData> {
let groups = if let Some(groups) = &self.groups {
let mut group_vec = Vec::with_capacity(groups.len());
for group in groups {
group_vec.push(group.to_guard_format()?);
}
Some(group_vec)
} else {
None
};
Ok(mpl_candy_guard::state::CandyGuardData {
default: self.default.to_guard_format()?,
groups,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Group {
pub label: String,
pub guards: GuardSet,
}
impl Group {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::state::Group> {
Ok(mpl_candy_guard::state::Group {
label: self.label.clone(),
guards: self.guards.to_guard_format()?,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct GuardSet {
pub bot_tax: Option<BotTax>,
pub sol_payment: Option<SolPayment>,
pub token_payment: Option<TokenPayment>,
pub start_date: Option<StartDate>,
pub third_party_signer: Option<ThirdPartySigner>,
pub token_gate: Option<TokenGate>,
pub gatekeeper: Option<Gatekeeper>,
pub end_date: Option<EndDate>,
pub allow_list: Option<AllowList>,
pub mint_limit: Option<MintLimit>,
pub nft_payment: Option<NftPayment>,
pub redeemed_amount: Option<RedeemedAmount>,
pub address_gate: Option<AddressGate>,
pub nft_gate: Option<NftGate>,
pub nft_burn: Option<NftBurn>,
pub token_burn: Option<TokenBurn>,
pub freeze_sol_payment: Option<FreezeSolPayment>,
pub freeze_token_payment: Option<FreezeTokenPayment>,
}
impl GuardSet {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::GuardSet> {
let bot_tax = if let Some(bot_tax) = &self.bot_tax {
Some(bot_tax.to_guard_format()?)
} else {
None
};
let sol_payment = if let Some(sol_payment) = &self.sol_payment {
Some(sol_payment.to_guard_format()?)
} else {
None
};
let token_payment = if let Some(token_payment) = &self.token_payment {
Some(token_payment.to_guard_format()?)
} else {
None
};
let start_date = if let Some(start_date) = &self.start_date {
Some(start_date.to_guard_format()?)
} else {
None
};
let third_party_signer = if let Some(third_party_signer) = &self.third_party_signer {
Some(third_party_signer.to_guard_format()?)
} else {
None
};
let token_gate = if let Some(token_gate) = &self.token_gate {
Some(token_gate.to_guard_format()?)
} else {
None
};
let gatekeeper = if let Some(gatekeeper) = &self.gatekeeper {
Some(gatekeeper.to_guard_format()?)
} else {
None
};
let end_date = if let Some(end_date) = &self.end_date {
Some(end_date.to_guard_format()?)
} else {
None
};
let allow_list = if let Some(allow_list) = &self.allow_list {
Some(allow_list.to_guard_format()?)
} else {
None
};
let mint_limit = if let Some(mint_limit) = &self.mint_limit {
Some(mint_limit.to_guard_format()?)
} else {
None
};
let nft_payment = if let Some(nft_payment) = &self.nft_payment {
Some(nft_payment.to_guard_format()?)
} else {
None
};
let redeemed_amount = if let Some(redeemed_amount) = &self.redeemed_amount {
Some(redeemed_amount.to_guard_format()?)
} else {
None
};
let address_gate = if let Some(address_gate) = &self.address_gate {
Some(address_gate.to_guard_format()?)
} else {
None
};
let nft_gate = if let Some(nft_gate) = &self.nft_gate {
Some(nft_gate.to_guard_format()?)
} else {
None
};
let nft_burn = if let Some(nft_burn) = &self.nft_burn {
Some(nft_burn.to_guard_format()?)
} else {
None
};
let token_burn = if let Some(token_burn) = &self.token_burn {
Some(token_burn.to_guard_format()?)
} else {
None
};
let freeze_sol_payment = if let Some(freeze_sol_payment) = &self.freeze_sol_payment {
Some(freeze_sol_payment.to_guard_format()?)
} else {
None
};
let freeze_token_payment = if let Some(freeze_token_payment) = &self.freeze_token_payment {
Some(freeze_token_payment.to_guard_format()?)
} else {
None
};
Ok(mpl_candy_guard::guards::GuardSet {
bot_tax,
sol_payment,
token_payment,
start_date,
third_party_signer,
token_gate,
gatekeeper,
end_date,
allow_list,
mint_limit,
nft_payment,
redeemed_amount,
address_gate,
nft_gate,
nft_burn,
token_burn,
freeze_sol_payment,
freeze_token_payment,
program_gate: None,
allocation: None,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AddressGate {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub address: Pubkey,
}
impl AddressGate {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::AddressGate> {
Ok(mpl_candy_guard::guards::AddressGate {
address: self.address,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct AllowList {
pub merkle_root: String,
}
impl AllowList {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::AllowList> {
let root: [u8; 32] = hex::decode(&self.merkle_root)?
.try_into()
.map_err(|_| anyhow!("Invalid merkle root value: {}", self.merkle_root))?;
Ok(mpl_candy_guard::guards::AllowList { merkle_root: root })
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct BotTax {
pub value: f64,
pub last_instruction: bool,
}
impl BotTax {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::BotTax> {
Ok(mpl_candy_guard::guards::BotTax {
lamports: price_as_lamports(self.value),
last_instruction: self.last_instruction,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct EndDate {
pub date: String,
}
impl EndDate {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::EndDate> {
let timestamp = self.date.parse::<DateTimeUtc>()?.0.timestamp();
Ok(mpl_candy_guard::guards::EndDate { date: timestamp })
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct Gatekeeper {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub gatekeeper_network: Pubkey,
pub expire_on_use: bool,
}
impl Gatekeeper {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::Gatekeeper> {
Ok(mpl_candy_guard::guards::Gatekeeper {
gatekeeper_network: self.gatekeeper_network,
expire_on_use: self.expire_on_use,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct MintLimit {
pub id: u8,
pub limit: u16,
}
impl MintLimit {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::MintLimit> {
Ok(mpl_candy_guard::guards::MintLimit {
id: self.id,
limit: self.limit,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct NftBurn {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub required_collection: Pubkey,
}
impl NftBurn {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftBurn> {
Ok(mpl_candy_guard::guards::NftBurn {
required_collection: self.required_collection,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct NftGate {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub required_collection: Pubkey,
}
impl NftGate {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftGate> {
Ok(mpl_candy_guard::guards::NftGate {
required_collection: self.required_collection,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct NftPayment {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub required_collection: Pubkey,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub destination: Pubkey,
}
impl NftPayment {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::NftPayment> {
Ok(mpl_candy_guard::guards::NftPayment {
required_collection: self.required_collection,
destination: self.destination,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct RedeemedAmount {
pub maximum: u64,
}
impl RedeemedAmount {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::RedeemedAmount> {
Ok(mpl_candy_guard::guards::RedeemedAmount {
maximum: self.maximum,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct SolPayment {
pub value: f64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub destination: Pubkey,
}
impl SolPayment {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::SolPayment> {
Ok(mpl_candy_guard::guards::SolPayment {
lamports: price_as_lamports(self.value),
destination: self.destination,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct StartDate {
pub date: String,
}
impl StartDate {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::StartDate> {
let timestamp = self.date.parse::<DateTimeUtc>()?.0.timestamp();
Ok(mpl_candy_guard::guards::StartDate { date: timestamp })
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct ThirdPartySigner {
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub signer_key: Pubkey,
}
impl ThirdPartySigner {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::ThirdPartySigner> {
Ok(mpl_candy_guard::guards::ThirdPartySigner {
signer_key: self.signer_key,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct TokenBurn {
pub amount: u64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub mint: Pubkey,
}
impl TokenBurn {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenBurn> {
Ok(mpl_candy_guard::guards::TokenBurn {
amount: self.amount,
mint: self.mint,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct TokenGate {
pub amount: u64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub mint: Pubkey,
}
impl TokenGate {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenGate> {
Ok(mpl_candy_guard::guards::TokenGate {
amount: self.amount,
mint: self.mint,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct TokenPayment {
pub amount: u64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub mint: Pubkey,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub destination_ata: Pubkey,
}
impl TokenPayment {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::TokenPayment> {
Ok(mpl_candy_guard::guards::TokenPayment {
amount: self.amount,
mint: self.mint,
destination_ata: self.destination_ata,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct FreezeSolPayment {
pub value: f64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub destination: Pubkey,
}
impl FreezeSolPayment {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::FreezeSolPayment> {
Ok(mpl_candy_guard::guards::FreezeSolPayment {
lamports: price_as_lamports(self.value),
destination: self.destination,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct FreezeTokenPayment {
pub amount: u64,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub mint: Pubkey,
#[serde(deserialize_with = "to_pubkey")]
#[serde(serialize_with = "to_string")]
pub destination_ata: Pubkey,
}
impl FreezeTokenPayment {
pub fn to_guard_format(&self) -> Result<mpl_candy_guard::guards::FreezeTokenPayment> {
Ok(mpl_candy_guard::guards::FreezeTokenPayment {
amount: self.amount,
mint: self.mint,
destination_ata: self.destination_ata,
})
}
}