1
2use forge_utils::AccountDeserialize;
3use solana_program::{
4 account_info::AccountInfo,
5 program_error::ProgramError,
6 program_pack::Pack,
7 pubkey::Pubkey,
8 system_program,
9 sysvar,
10 msg,
11};
12use spl_token::state::Mint;
13use mpl_core::{Asset, types::UpdateAuthority};
14
15use crate::{
16 consts::*, state::{Config, Enhancer, Treasury}, utils::Discriminator
17};
18
19pub fn load_signer<'a, 'info>(info: &'a AccountInfo<'info>) -> Result<(), ProgramError> {
22 if !info.is_signer {
23 return Err(ProgramError::MissingRequiredSignature);
24 }
25
26 Ok(())
27}
28
29pub fn load_mint<'a, 'info>(
36 info: &'a AccountInfo<'info>,
37 address: Pubkey,
38 is_writable: bool,
39) -> Result<(), ProgramError> {
40 if info.owner.ne(&spl_token::id()) {
41 return Err(ProgramError::InvalidAccountOwner);
42 }
43
44 if info.key.ne(&address) {
45 return Err(ProgramError::InvalidSeeds);
46 }
47
48 if info.data_is_empty() {
49 return Err(ProgramError::UninitializedAccount);
50 }
51
52 Mint::unpack(&info.data.borrow())?;
53
54 if is_writable && !info.is_writable {
55 return Err(ProgramError::InvalidAccountData);
56 }
57
58 Ok(())
59}
60
61pub fn load_token_account<'a, 'info>(
69 info: &'a AccountInfo<'info>,
70 owner: Option<&Pubkey>,
71 mint: &Pubkey,
72 is_writable: bool,
73) -> Result<(), ProgramError> {
74 if info.owner.ne(&spl_token::id()) {
75 return Err(ProgramError::InvalidAccountOwner);
76 }
77
78 if info.data_is_empty() {
79 return Err(ProgramError::UninitializedAccount);
80 }
81
82 let account_data = info.data.borrow();
83 let account = spl_token::state::Account::unpack(&account_data)?;
84
85 if account.mint.ne(&mint) {
86 msg!("Invalid mint: {:?} == {:?}", account.mint, mint);
87 return Err(ProgramError::InvalidAccountData);
88 }
89
90 if let Some(owner) = owner {
91 if account.owner.ne(owner) {
92 msg!("Invalid owner: {:?} == {:?}", account.owner, owner);
93 return Err(ProgramError::InvalidAccountData);
94 }
95 }
96
97 if is_writable && !info.is_writable {
98 msg!("Invalid writable: {:?} == {:?}", info.is_writable, is_writable);
99 return Err(ProgramError::InvalidAccountData);
100 }
101
102 Ok(())
103}
104
105pub fn load_treasury_token_account<'a, 'info>(
109 info: &'a AccountInfo<'info>,
110 mint: Pubkey,
111 is_writable: bool,
112) -> Result<(), ProgramError> {
113 let treasury_tokens = spl_associated_token_account::get_associated_token_address(&TREASURY_ADDRESS, &mint);
114
115 if info.key.ne(&treasury_tokens) {
116 return Err(ProgramError::InvalidSeeds);
117 }
118
119 load_token_account(info, Some(&TREASURY_ADDRESS), &mint, is_writable)
120}
121
122pub fn load_collection_authority<'a, 'info>(
123 info: &'a AccountInfo<'info>,
124 seeds: &[&[u8]],
125 bump: u8,
126 program_id: &Pubkey,
127) -> Result<(), ProgramError> {
128 let pda = Pubkey::find_program_address(seeds, program_id);
129
130 if info.key.ne(&pda.0) {
131 return Err(ProgramError::InvalidSeeds);
132 }
133
134 if bump.ne(&pda.1) {
135 return Err(ProgramError::InvalidSeeds);
136 }
137
138 Ok(())
139}
140
141pub fn load_uninitialized_pda<'a, 'info>(
145 info: &'a AccountInfo<'info>,
146 seeds: &[&[u8]],
147 bump: u8,
148 program_id: &Pubkey,
149) -> Result<(), ProgramError> {
150 let pda = Pubkey::find_program_address(seeds, program_id);
151
152 if info.key.ne(&pda.0) {
153 return Err(ProgramError::InvalidSeeds);
154 }
155
156 if bump.ne(&pda.1) {
157 return Err(ProgramError::InvalidSeeds);
158 }
159
160 load_system_account(info, true)
161}
162
163pub fn load_system_account<'a, 'info>(
168 info: &'a AccountInfo<'info>,
169 is_writable: bool,
170) -> Result<(), ProgramError> {
171 if info.owner.ne(&system_program::id()) {
172 return Err(ProgramError::InvalidAccountOwner);
173 }
174
175 if !info.data_is_empty() {
176 return Err(ProgramError::AccountAlreadyInitialized);
177 }
178
179 if is_writable && !info.is_writable {
180 return Err(ProgramError::InvalidAccountData);
181 }
182
183 Ok(())
184}
185
186pub fn load_program<'a, 'info>(
190 info: &'a AccountInfo<'info>,
191 key: Pubkey,
192) -> Result<(), ProgramError> {
193 if info.key.ne(&key) {
194 return Err(ProgramError::IncorrectProgramId);
195 }
196
197 if !info.executable {
198 return Err(ProgramError::InvalidAccountData);
199 }
200
201 Ok(())
202}
203
204pub fn load_config<'a, 'info>(
211 info: &'a AccountInfo<'info>,
212 collection: Pubkey,
213 is_writable: bool,
214) -> Result<(), ProgramError> {
215 msg!("config_info: {:?}", info.key);
216 if info.owner.ne(&crate::id()) {
217 return Err(ProgramError::InvalidAccountOwner);
218 }
219
220 let pda = Pubkey::find_program_address(&[CONFIG_SEED, collection.as_ref()], &crate::id()).0;
221 if info.key.ne(&pda) {
222 return Err(ProgramError::InvalidSeeds);
223 }
224
225 if info.data_is_empty() {
226 return Err(ProgramError::UninitializedAccount);
227 }
228
229 if info.data.borrow()[0].ne(&(Config::discriminator() as u8)) {
230 return Err(solana_program::program_error::ProgramError::InvalidAccountData);
231 }
232
233 if is_writable && !info.is_writable {
234 return Err(ProgramError::InvalidAccountData);
235 }
236
237 Ok(())
238}
239
240pub fn load_treasury<'a, 'info>(
247 info: &'a AccountInfo<'info>,
248 is_writable: bool,
249) -> Result<(), ProgramError> {
250 if info.owner.ne(&crate::id()) {
251 return Err(ProgramError::InvalidAccountOwner);
252 }
253
254 if info.key.ne(&TREASURY_ADDRESS) {
255 return Err(ProgramError::InvalidSeeds);
256 }
257
258 if info.data_is_empty() {
259 return Err(ProgramError::UninitializedAccount);
260 }
261
262 if info.data.borrow()[0].ne(&(Treasury::discriminator() as u8)) {
263 return Err(solana_program::program_error::ProgramError::InvalidAccountData);
264 }
265
266 if is_writable && !info.is_writable {
267 return Err(ProgramError::InvalidAccountData);
268 }
269
270 Ok(())
271}
272
273pub fn load_enhance<'a, 'info>(
280 info: &'a AccountInfo<'info>,
281 authority: &Pubkey,
282 asset: &Pubkey,
283 is_writable: bool,
284) -> Result<(), ProgramError> {
285 if info.owner.ne(&crate::id()) {
286 return Err(ProgramError::InvalidAccountOwner);
287 }
288
289 if info.data_is_empty() {
290 return Err(ProgramError::UninitializedAccount);
291 }
292
293 let data = info.data.borrow();
294 let enhancer = Enhancer::try_from_bytes(&data)?;
295
296 if enhancer.authority.ne(&authority) {
297 return Err(ProgramError::InvalidAccountData);
298 }
299
300 if enhancer.asset.ne(&asset) {
301 return Err(ProgramError::InvalidAccountData);
302 }
303
304 if is_writable && !info.is_writable {
305 return Err(ProgramError::InvalidAccountData);
306 }
307
308 Ok(())
309}
310
311pub fn load_asset<'a, 'info>(
318 info: &'a AccountInfo<'info>,
319) -> Result<(f64, u64, String), ProgramError> {
320 if info.owner.ne(&mpl_core::ID) {
321 return Err(ProgramError::InvalidAccountOwner);
322 }
323
324 if info.data_is_empty() {
325 return Err(ProgramError::UninitializedAccount);
326 }
327
328 let asset = Asset::from_bytes(&info.data.borrow()).unwrap();
329
330 match asset.base.update_authority {
331 UpdateAuthority::Collection(address) => {
332 if address.ne(&COLLECTION) {
333 msg!("Invalid collection: {:?} == {:?}", address, COLLECTION);
334 return Err(ProgramError::InvalidAccountData);
335 }
336 }
337 _ => return Err(ProgramError::InvalidAccountData),
338 }
339
340 if asset.plugin_list.attributes.is_none() {
341 return Err(ProgramError::InvalidAccountData);
342 }
343
344 let attributes_plugin = asset.plugin_list.attributes.unwrap();
345 let durability_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "durability");
346 let multiplier_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "multiplier");
347 let resource_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "resource");
348 let durability = durability_attr.unwrap().value.parse::<f64>().unwrap();
349 let multiplier = multiplier_attr.unwrap().value.parse::<u64>().unwrap();
350 let resource = resource_attr.unwrap().value.clone();
351
352 Ok((durability, multiplier, resource))
353}
354
355pub fn load_sysvar<'a, 'info>(
359 info: &'a AccountInfo<'info>,
360 key: Pubkey,
361) -> Result<(), ProgramError> {
362 if info.owner.ne(&sysvar::id()) {
363 return Err(ProgramError::InvalidAccountOwner);
364 }
365
366 load_account(info, key, false)
367}
368
369pub fn load_account<'a, 'info>(
373 info: &'a AccountInfo<'info>,
374 key: Pubkey,
375 is_writable: bool,
376) -> Result<(), ProgramError> {
377 if info.key.ne(&key) {
378 return Err(ProgramError::InvalidAccountData);
379 }
380
381 if is_writable && !info.is_writable {
382 return Err(ProgramError::InvalidAccountData);
383 }
384
385 Ok(())
386}
387