1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
use std::collections::BTreeMap;

pub use anchor_lang::prelude::*;
use mpl_core::types::PluginAuthorityPair;

pub use crate::{errors::CandyGuardError, state::GuardSet};
use crate::{
    instructions::{MintAccounts, Route, RouteContext},
    state::CandyGuardData,
};

pub use address_gate::AddressGate;
pub use allocation::Allocation;
pub use allow_list::AllowList;
pub use bot_tax::BotTax;
pub use edition::Edition;
pub use end_date::EndDate;
pub use freeze_sol_payment::{FreezeEscrow, FreezeInstruction, FreezeSolPayment};
pub use freeze_token_payment::FreezeTokenPayment;
pub use gatekeeper::Gatekeeper;
pub use mint_limit::{MintCounter, MintLimit};
pub use nft_burn::NftBurn;
pub use nft_gate::NftGate;
pub use nft_mint_limit::NftMintLimit;
pub use nft_payment::NftPayment;
pub use program_gate::ProgramGate;
pub use redeemed_amount::RedeemedAmount;
pub use sol_fixed_fee::SolFixedFee;
pub use sol_payment::SolPayment;
pub use start_date::StartDate;
pub use third_party_signer::ThirdPartySigner;
pub use token2022_payment::Token2022Payment;
pub use token_burn::TokenBurn;
pub use token_gate::TokenGate;
pub use token_payment::TokenPayment;

mod address_gate;
mod allocation;
mod allow_list;
mod bot_tax;
mod edition;
mod end_date;
mod freeze_sol_payment;
mod freeze_token_payment;
mod gatekeeper;
mod mint_limit;
mod nft_burn;
mod nft_gate;
mod nft_mint_limit;
mod nft_payment;
mod program_gate;
mod redeemed_amount;
mod sol_fixed_fee;
mod sol_payment;
mod start_date;
mod third_party_signer;
mod token2022_payment;
mod token_burn;
mod token_gate;
mod token_payment;

pub trait Condition {
    /// Validate the condition of the guard. When the guard condition is
    /// not satisfied, it will return an error.
    ///
    /// This function should not perform any modification to accounts, since
    /// other guards might fail, causing the transaction to be aborted.
    ///
    /// Intermediary evaluation data can be stored in the `evaluation_context`,
    /// which will be shared with other guards and reused in the `actions` step
    /// of the process.
    fn validate(
        &self,
        ctx: &mut EvaluationContext,
        guard_set: &GuardSet,
        mint_args: &[u8],
    ) -> Result<()>;

    /// Perform the action associated with the guard before the CPI `mint` instruction.
    ///
    /// This function only gets called when all guards have been successfuly validated.
    /// Any error generated will make the transaction to fail.
    fn pre_actions(
        &self,
        _ctx: &mut EvaluationContext,
        _guard_set: &GuardSet,
        _mint_args: &[u8],
    ) -> Result<()> {
        Ok(())
    }

    /// Perform the action associated with the guard after the CPI `mint` instruction.
    ///
    /// This function only gets called when all guards have been successfuly validated.
    /// Any error generated will make the transaction to fail.
    fn post_actions(
        &self,
        _ctx: &mut EvaluationContext,
        _guard_set: &GuardSet,
        _mint_args: &[u8],
    ) -> Result<()> {
        Ok(())
    }
}

pub trait Guard: Condition + AnchorSerialize + AnchorDeserialize {
    /// Returns the number of bytes used by the guard configuration.
    fn size() -> usize;

    /// Returns the feature mask for the guard.
    fn mask() -> u64;

    /// Executes an instruction. This function is called from the `route` instruction
    /// handler.
    fn instruction<'info>(
        _ctx: &Context<'_, '_, '_, 'info, Route<'info>>,
        _route_context: RouteContext<'info>,
        _data: Vec<u8>,
    ) -> Result<()> {
        err!(CandyGuardError::InstructionNotFound)
    }

    /// Returns whether the guards is enabled or not on the specified features.
    fn is_enabled(features: u64) -> bool {
        features & Self::mask() > 0
    }

    /// Enables the guard on the specified `features` value.
    fn enable(features: u64) -> u64 {
        features | Self::mask()
    }

    /// Disables the guard on the specified `features` value.
    fn disable(features: u64) -> u64 {
        features & !Self::mask()
    }

    /// Serializes the guard into the specified data array.
    fn save(&self, data: &mut [u8], offset: usize) -> Result<()> {
        let mut result = Vec::with_capacity(Self::size());
        self.serialize(&mut result)?;

        data[offset..(result.len() + offset)].copy_from_slice(&result[..]);

        Ok(())
    }

    /// Deserializes the guard from a slice of data. Only attempts the deserialization
    /// if the data slice is large enough.
    fn load(data: &[u8], offset: usize) -> Result<Option<Self>> {
        if offset <= data.len() {
            let mut slice = &data[offset - Self::size()..offset];
            let guard = Self::deserialize(&mut slice)?;
            Ok(Some(guard))
        } else {
            Ok(None)
        }
    }

    /// Verifies that the candy guard configuration is valid according to the rules
    /// of the guard.
    fn verify(_data: &CandyGuardData) -> Result<()> {
        Ok(())
    }
}
pub struct EvaluationContext<'b, 'c, 'info> {
    /// Accounts required to mint an NFT.
    pub(crate) accounts: MintAccounts<'b, 'c, 'info>,

    /// The cursor for the remaining account list. When a guard "consumes" one of the
    /// remaining accounts, it should increment the cursor.
    pub account_cursor: usize,

    /// The cursor for the remaining bytes on the mint args. When a guard "consumes" one
    /// argument, it should increment the number of bytes read.
    pub args_cursor: usize,

    /// Convenience mapping of remaining account indices.
    pub indices: BTreeMap<&'info str, usize>,

    /// Any plugins to be used when minting
    pub plugins: Vec<PluginAuthorityPair>,
}

/// Utility function to try to get the account from the remaining accounts
/// array at the specified index.
pub fn try_get_account_info<T>(remaining_accounts: &[T], index: usize) -> Result<&T> {
    if index < remaining_accounts.len() {
        Ok(&remaining_accounts[index])
    } else {
        err!(CandyGuardError::MissingRemainingAccount)
    }
}

/// Utility function to try to get the account from the remaining accounts
/// array at the specified index.
pub fn get_account_info<T>(remaining_accounts: &[T], index: usize) -> Option<&T> {
    if index < remaining_accounts.len() {
        Some(&remaining_accounts[index])
    } else {
        None
    }
}