mpl_candy_guard/guards/
mod.rs

1use std::collections::BTreeMap;
2
3pub use anchor_lang::prelude::*;
4
5pub use crate::{errors::CandyGuardError, instructions::mint::*, state::GuardSet};
6use crate::{
7    instructions::{MintAccounts, Route, RouteContext},
8    state::CandyGuardData,
9};
10
11pub use address_gate::AddressGate;
12pub use allocation::Allocation;
13pub use allow_list::AllowList;
14pub use bot_tax::BotTax;
15pub use end_date::EndDate;
16pub use freeze_sol_payment::{FreezeEscrow, FreezeInstruction, FreezeSolPayment};
17pub use freeze_token_payment::FreezeTokenPayment;
18pub use gatekeeper::Gatekeeper;
19pub use mint_limit::{MintCounter, MintLimit};
20pub use nft_burn::NftBurn;
21pub use nft_gate::NftGate;
22pub use nft_payment::NftPayment;
23pub use program_gate::ProgramGate;
24pub use redeemed_amount::RedeemedAmount;
25pub use sol_payment::SolPayment;
26pub use start_date::StartDate;
27pub use third_party_signer::ThirdPartySigner;
28pub use token_burn::TokenBurn;
29pub use token_gate::TokenGate;
30pub use token_payment::TokenPayment;
31
32mod address_gate;
33mod allocation;
34mod allow_list;
35mod bot_tax;
36mod end_date;
37mod freeze_sol_payment;
38mod freeze_token_payment;
39mod gatekeeper;
40mod mint_limit;
41mod nft_burn;
42mod nft_gate;
43mod nft_payment;
44mod program_gate;
45mod redeemed_amount;
46mod sol_payment;
47mod start_date;
48mod third_party_signer;
49mod token_burn;
50mod token_gate;
51mod token_payment;
52
53pub trait Condition {
54    /// Validate the condition of the guard. When the guard condition is
55    /// not satisfied, it will return an error.
56    ///
57    /// This function should not perform any modification to accounts, since
58    /// other guards might fail, causing the transaction to be aborted.
59    ///
60    /// Intermediary evaluation data can be stored in the `evaluation_context`,
61    /// which will be shared with other guards and reused in the `actions` step
62    /// of the process.
63    fn validate(
64        &self,
65        ctx: &mut EvaluationContext,
66        guard_set: &GuardSet,
67        mint_args: &[u8],
68    ) -> Result<()>;
69
70    /// Perform the action associated with the guard before the CPI `mint` instruction.
71    ///
72    /// This function only gets called when all guards have been successfuly validated.
73    /// Any error generated will make the transaction to fail.
74    fn pre_actions(
75        &self,
76        _ctx: &mut EvaluationContext,
77        _guard_set: &GuardSet,
78        _mint_args: &[u8],
79    ) -> Result<()> {
80        Ok(())
81    }
82
83    /// Perform the action associated with the guard after the CPI `mint` instruction.
84    ///
85    /// This function only gets called when all guards have been successfuly validated.
86    /// Any error generated will make the transaction to fail.
87    fn post_actions(
88        &self,
89        _ctx: &mut EvaluationContext,
90        _guard_set: &GuardSet,
91        _mint_args: &[u8],
92    ) -> Result<()> {
93        Ok(())
94    }
95}
96
97pub trait Guard: Condition + AnchorSerialize + AnchorDeserialize {
98    /// Returns the number of bytes used by the guard configuration.
99    fn size() -> usize;
100
101    /// Returns the feature mask for the guard.
102    fn mask() -> u64;
103
104    /// Executes an instruction. This function is called from the `route` instruction
105    /// handler.
106    fn instruction<'info>(
107        _ctx: &Context<'_, '_, '_, 'info, Route<'info>>,
108        _route_context: RouteContext<'info>,
109        _data: Vec<u8>,
110    ) -> Result<()> {
111        err!(CandyGuardError::InstructionNotFound)
112    }
113
114    /// Returns whether the guards is enabled or not on the specified features.
115    fn is_enabled(features: u64) -> bool {
116        features & Self::mask() > 0
117    }
118
119    /// Enables the guard on the specified `features` value.
120    fn enable(features: u64) -> u64 {
121        features | Self::mask()
122    }
123
124    /// Disables the guard on the specified `features` value.
125    fn disable(features: u64) -> u64 {
126        features & !Self::mask()
127    }
128
129    /// Serializes the guard into the specified data array.
130    fn save(&self, data: &mut [u8], offset: usize) -> Result<()> {
131        let mut result = Vec::with_capacity(Self::size());
132        self.serialize(&mut result)?;
133
134        data[offset..(result.len() + offset)].copy_from_slice(&result[..]);
135
136        Ok(())
137    }
138
139    /// Deserializes the guard from a slice of data. Only attempts the deserialization
140    /// if the data slice is large enough.
141    fn load(data: &[u8], offset: usize) -> Result<Option<Self>> {
142        if offset <= data.len() {
143            let mut slice = &data[offset - Self::size()..offset];
144            let guard = Self::deserialize(&mut slice)?;
145            Ok(Some(guard))
146        } else {
147            Ok(None)
148        }
149    }
150
151    /// Verifies that the candy guard configuration is valid according to the rules
152    /// of the guard.
153    fn verify(_data: &CandyGuardData) -> Result<()> {
154        Ok(())
155    }
156}
157pub struct EvaluationContext<'b, 'c, 'info> {
158    /// Accounts required to mint an NFT.
159    pub(crate) accounts: MintAccounts<'b, 'c, 'info>,
160
161    /// The cursor for the remaining account list. When a guard "consumes" one of the
162    /// remaining accounts, it should increment the cursor.
163    pub account_cursor: usize,
164
165    /// The cursor for the remaining bytes on the mint args. When a guard "consumes" one
166    /// argument, it should increment the number of bytes read.
167    pub args_cursor: usize,
168
169    /// Convenience mapping of remaining account indices.
170    pub indices: BTreeMap<&'info str, usize>,
171}
172
173/// Utility function to try to get the account from the remaining accounts
174/// array at the specified index.
175pub fn try_get_account_info<T>(remaining_accounts: &[T], index: usize) -> Result<&T> {
176    if index < remaining_accounts.len() {
177        Ok(&remaining_accounts[index])
178    } else {
179        err!(CandyGuardError::MissingRemainingAccount)
180    }
181}
182
183/// Utility function to try to get the account from the remaining accounts
184/// array at the specified index.
185pub fn get_account_info<T>(remaining_accounts: &[T], index: usize) -> Option<&T> {
186    if index < remaining_accounts.len() {
187        Some(&remaining_accounts[index])
188    } else {
189        None
190    }
191}