mpl_token_auth_rules/
instruction.rs

1use crate::payload::Payload;
2use borsh::{BorshDeserialize, BorshSerialize};
3use mpl_token_metadata_context_derive::AccountContext;
4use shank::ShankInstruction;
5use solana_program::{
6    account_info::AccountInfo,
7    instruction::{AccountMeta, Instruction},
8};
9
10#[repr(C)]
11#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
12/// Args for `create` instruction.
13pub enum CreateOrUpdateArgs {
14    /// V1 implementation of the `create` instruction arguments.
15    V1 {
16        /// RuleSet pre-serialized by caller. Both MessagePack and Bytemuck formats
17        /// are supported.
18        serialized_rule_set: Vec<u8>,
19    },
20}
21
22#[repr(C)]
23#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
24/// Args for `validate` instruction.
25pub enum ValidateArgs {
26    /// V1 implementation of the `validate` instruction arguments.
27    V1 {
28        /// `Operation` to validate.
29        operation: String,
30        /// `Payload` data used for rule validation.
31        payload: Payload,
32        /// Update any relevant state stored in Rule, such as the Frequency `last_update` time value.
33        update_rule_state: bool,
34        /// Optional revision of the `RuleSet` to use.  If `None`, the latest revision is used.
35        rule_set_revision: Option<usize>,
36    },
37}
38
39#[repr(C)]
40#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
41/// Args for `append_to_rule_set` instruction.
42pub enum WriteToBufferArgs {
43    /// V1 implementation of the `create` instruction arguments.
44    V1 {
45        /// RuleSet pre-serialized by caller into the MessagePack format.
46        serialized_rule_set: Vec<u8>,
47        /// Whether the or not the any old data should be overwritten.
48        overwrite: bool,
49    },
50}
51
52#[repr(C)]
53#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
54/// Args for `append_to_rule_set` instruction.
55pub enum PuffRuleSetArgs {
56    /// V1 implementation of the `create` instruction arguments.
57    V1 {
58        /// RuleSet name.
59        rule_set_name: String,
60    },
61}
62
63#[derive(Debug, Clone, ShankInstruction, AccountContext, BorshSerialize, BorshDeserialize)]
64#[rustfmt::skip]
65/// Instructions available in this program.
66pub enum RuleSetInstruction {
67    /// This instruction stores a caller-pre-serialized `RuleSet` into the rule_set PDA account.
68    #[account(0, signer, writable, name="payer", desc="Payer and creator of the RuleSet")]
69    #[account(1, writable, name="rule_set_pda", desc = "The PDA account where the RuleSet is stored")]
70    #[account(2, name = "system_program", desc = "System program")]
71    #[account(3, optional, name="buffer_pda", desc = "The buffer to copy a complete ruleset from")]
72    CreateOrUpdate(CreateOrUpdateArgs),
73
74    /// This instruction executes the RuleSet stored in the rule_set PDA account by calling the
75    /// `RuleSet`'s `validate` method.  If any of the Rules contained in the RuleSet have state
76    /// information (such as the Frequency rule's `last_update` time value), the optional accounts
77    /// must be provided in order to save the updated stated in the RuleSet state PDA.  Note that
78    /// updating the state for a Rule requires that the `rule_authority` signer matches the Pubkey
79    /// stored in the Rule.
80    #[account(0, name="rule_set_pda", desc = "The PDA account where the RuleSet is stored")]
81    #[account(1, name="mint", desc="Mint of token asset")]
82    #[account(2, name = "system_program", desc = "System program")]
83    #[account(3, optional, signer, writable, name="payer", desc="Payer for RuleSet state PDA account")]
84    #[account(4, optional, signer, name="rule_authority", desc="Signing authority for any Rule state updates")]
85    #[account(5, optional, writable, name="rule_set_state_pda", desc = "The PDA account where any RuleSet state is stored")]
86    #[args(additional_rule_accounts: Vec<AccountMeta>)]
87    Validate(ValidateArgs),
88
89    /// This instruction appends a pre-serialized `RuleSet` chunk into the rule_set PDA account.
90    /// Needed with large `RuleSet`s to stay within transaction size limit.
91    #[account(0, signer, writable, name="payer", desc="Payer and creator of the RuleSet")]
92    #[account(1, writable, name="buffer_pda", desc = "The PDA account where the RuleSet buffer is stored")]
93    #[account(2, name = "system_program", desc = "System program")]
94    WriteToBuffer(WriteToBufferArgs),
95
96    /// Add space to the end of a rule set account.  Needed with large `RuleSet`s to pre-allocate
97    /// the space, to stay within PDA allocation limits.
98    #[account(0, signer, writable, name="payer", desc="Payer and creator of the RuleSet")]
99    #[account(1, writable, name="rule_set_pda", desc = "The PDA account where the RuleSet is stored")]
100    #[account(2, name = "system_program", desc = "System program")]
101    PuffRuleSet(PuffRuleSetArgs),
102}
103
104/// Builds a `CreateOrUpdate` instruction.
105impl InstructionBuilder for builders::CreateOrUpdate {
106    fn instruction(&self) -> solana_program::instruction::Instruction {
107        let mut accounts = vec![
108            AccountMeta::new(self.payer, true),
109            AccountMeta::new(self.rule_set_pda, false),
110            AccountMeta::new_readonly(solana_program::system_program::id(), false),
111        ];
112
113        if let Some(buffer_pda) = self.buffer_pda {
114            accounts.push(AccountMeta::new_readonly(buffer_pda, false));
115        } else {
116            accounts.push(AccountMeta::new_readonly(crate::ID, false));
117        }
118
119        Instruction {
120            program_id: crate::ID,
121            accounts,
122            data: RuleSetInstruction::CreateOrUpdate(self.args.clone())
123                .try_to_vec()
124                .unwrap(),
125        }
126    }
127}
128
129/// Builds a `Validate` instruction.
130impl InstructionBuilder for builders::Validate {
131    fn instruction(&self) -> solana_program::instruction::Instruction {
132        let mut accounts = vec![
133            AccountMeta::new_readonly(self.rule_set_pda, false),
134            AccountMeta::new_readonly(self.mint, false),
135            AccountMeta::new_readonly(solana_program::system_program::id(), false),
136        ];
137
138        // Add optional account or `crate::ID`.
139        if let Some(payer) = self.payer {
140            accounts.push(AccountMeta::new(payer, true));
141        } else {
142            accounts.push(AccountMeta::new_readonly(crate::ID, false));
143        }
144
145        // Add optional account or `crate::ID`.
146        if let Some(rule_authority) = self.rule_authority {
147            accounts.push(AccountMeta::new_readonly(rule_authority, true));
148        } else {
149            accounts.push(AccountMeta::new_readonly(crate::ID, false));
150        }
151
152        // Add optional account or `crate::ID`.
153        if let Some(rule_set_state_pda) = self.rule_set_state_pda {
154            accounts.push(AccountMeta::new(rule_set_state_pda, false));
155        } else {
156            accounts.push(AccountMeta::new_readonly(crate::ID, false));
157        }
158
159        accounts.extend(self.additional_rule_accounts.clone());
160
161        Instruction {
162            program_id: crate::ID,
163            accounts,
164            data: RuleSetInstruction::Validate(self.args.clone())
165                .try_to_vec()
166                .unwrap(),
167        }
168    }
169}
170
171/// Builds a `WriteToBuffer` instruction.
172impl InstructionBuilder for builders::WriteToBuffer {
173    fn instruction(&self) -> solana_program::instruction::Instruction {
174        let accounts = vec![
175            AccountMeta::new(self.payer, true),
176            AccountMeta::new(self.buffer_pda, false),
177            AccountMeta::new_readonly(solana_program::system_program::id(), false),
178        ];
179
180        Instruction {
181            program_id: crate::ID,
182            accounts,
183            data: RuleSetInstruction::WriteToBuffer(self.args.clone())
184                .try_to_vec()
185                .unwrap(),
186        }
187    }
188}
189
190/// Builds a `PuffRuleSet` instruction.
191impl InstructionBuilder for builders::PuffRuleSet {
192    fn instruction(&self) -> solana_program::instruction::Instruction {
193        let accounts = vec![
194            AccountMeta::new(self.payer, true),
195            AccountMeta::new(self.rule_set_pda, false),
196            AccountMeta::new_readonly(solana_program::system_program::id(), false),
197        ];
198
199        Instruction {
200            program_id: crate::ID,
201            accounts,
202            data: RuleSetInstruction::PuffRuleSet(self.args.clone())
203                .try_to_vec()
204                .unwrap(),
205        }
206    }
207}
208
209/// Account context holding the accounts used by various instructions.
210pub struct Context<'a, T> {
211    /// The struct holding the named accounts used by an instruction.
212    pub accounts: T,
213    /// All remaining accounts passed to an instruction.
214    pub remaining_accounts: Vec<&'a AccountInfo<'a>>,
215}
216
217/// A trait for building an instruction.
218pub trait InstructionBuilder {
219    /// The required function to return the built instruction.
220    fn instruction(&self) -> solana_program::instruction::Instruction;
221}