coal_api/
loaders.rs

1use solana_program::{
2    msg,
3    account_info::AccountInfo,
4    program_error::ProgramError,
5    program_pack::Pack,
6    pubkey::Pubkey,
7    system_program,
8    sysvar
9};
10use spl_token::state::Mint;
11use mpl_core::{Asset, types::UpdateAuthority};
12
13use crate::{
14    consts::*,
15    state::{Bus, Config, Proof, ProofV2, Reprocessor, Tool, Treasury, WoodConfig, WoodTool},
16    utils::{AccountDeserialize, Discriminator},
17};
18
19/// Errors if:
20/// - Account is not a signer.
21pub 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
29/// Errors if:
30/// - Owner is not Coal program.
31/// - Address does not match the expected bus address.
32/// - Data is empty.
33/// - Data cannot deserialize into a coal bus account.
34/// - Bus ID does not match the expected ID.
35/// - Expected to be writable, but is not.
36pub fn load_coal_bus<'a, 'info>(
37    info: &'a AccountInfo<'info>,
38    id: u64,
39    is_writable: bool,
40) -> Result<(), ProgramError> {
41    if info.owner.ne(&crate::id()) {
42        return Err(ProgramError::InvalidAccountOwner);
43    }
44
45    if info.key.ne(&COAL_BUS_ADDRESSES[id as usize]) {
46        return Err(ProgramError::InvalidSeeds);
47    }
48
49    if info.data_is_empty() {
50        return Err(ProgramError::UninitializedAccount);
51    }
52
53    let bus_data = info.data.borrow();
54    let bus = Bus::try_from_bytes(&bus_data)?;
55
56    if bus.id.ne(&id) {
57        return Err(ProgramError::InvalidAccountData);
58    }
59
60    if is_writable && !info.is_writable {
61        return Err(ProgramError::InvalidAccountData);
62    }
63
64    Ok(())
65}
66
67/// Errors if:
68/// - Owner is not Coal program.
69/// - Address does not match the expected bus address.
70/// - Data is empty.
71/// - Data cannot deserialize into a bus account.
72/// - Bus ID does not match the expected ID.
73/// - Expected to be writable, but is not.
74pub fn load_wood_bus<'a, 'info>(
75    info: &'a AccountInfo<'info>,
76    id: u64,
77    is_writable: bool,
78) -> Result<(), ProgramError> {
79    if info.owner.ne(&crate::id()) {
80        return Err(ProgramError::InvalidAccountOwner);
81    }
82
83    if info.key.ne(&WOOD_BUS_ADDRESSES[id as usize]) {
84        return Err(ProgramError::InvalidSeeds);
85    }
86
87    if info.data_is_empty() {
88        return Err(ProgramError::UninitializedAccount);
89    }
90
91    let bus_data = info.data.borrow();
92    let bus = Bus::try_from_bytes(&bus_data)?;
93
94    if bus.id.ne(&id) {
95        return Err(ProgramError::InvalidAccountData);
96    }
97
98    if is_writable && !info.is_writable {
99        return Err(ProgramError::InvalidAccountData);
100    }
101
102    Ok(())
103}
104
105/// Errors if:
106/// - Owner is not Ore program.
107/// - Data is empty.
108/// - Data cannot deserialize into a coal bus account.
109/// - Bus ID is not in the expected range.
110/// - Address is not in set of valid coal bus address.
111/// - Expected to be writable, but is not.
112pub fn load_any_coal_bus<'a, 'info>(
113    info: &'a AccountInfo<'info>,
114    is_writable: bool,
115) -> Result<(), ProgramError> {
116    if info.owner.ne(&crate::id()) {
117        return Err(ProgramError::InvalidAccountOwner);
118    }
119
120    if info.data_is_empty() {
121        return Err(ProgramError::UninitializedAccount);
122    }
123
124    if info.data.borrow()[0].ne(&(Bus::discriminator() as u8)) {
125        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
126    }
127
128    if !COAL_BUS_ADDRESSES.contains(info.key) {
129        return Err(ProgramError::InvalidSeeds);
130    }
131
132    if is_writable && !info.is_writable {
133        return Err(ProgramError::InvalidAccountData);
134    }
135
136    Ok(())
137}
138
139/// Errors if:
140/// - Owner is not COAL program.
141/// - Data is empty.
142/// - Data cannot deserialize into a wood bus account.
143/// - Bus ID is not in the expected range.
144/// - Address is not in set of valid wood bus address.
145/// - Expected to be writable, but is not.
146pub fn load_any_wood_bus<'a, 'info>(
147    info: &'a AccountInfo<'info>,
148    is_writable: bool,
149) -> Result<(), ProgramError> {
150    if info.owner.ne(&crate::id()) {
151        return Err(ProgramError::InvalidAccountOwner);
152    }
153
154    if info.data_is_empty() {
155        return Err(ProgramError::UninitializedAccount);
156    }
157
158    if info.data.borrow()[0].ne(&(Bus::discriminator() as u8)) {
159        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
160    }
161
162    if !WOOD_BUS_ADDRESSES.contains(info.key) {
163        return Err(ProgramError::InvalidSeeds);
164    }
165
166    if is_writable && !info.is_writable {
167        return Err(ProgramError::InvalidAccountData);
168    }
169
170    Ok(())
171}
172
173/// Errors if:
174/// - Owner is not Coal program.
175/// - Address does not match the expected address.
176/// - Data is empty.
177/// - Data cannot deserialize into a coal config account.
178/// - Expected to be writable, but is not.
179pub fn load_coal_config<'a, 'info>(
180    info: &'a AccountInfo<'info>,
181    is_writable: bool,
182) -> Result<(), ProgramError> {
183    if info.owner.ne(&crate::id()) {
184        return Err(ProgramError::InvalidAccountOwner);
185    }
186
187    if info.key.ne(&COAL_CONFIG_ADDRESS) {
188        return Err(ProgramError::InvalidSeeds);
189    }
190
191    if info.data_is_empty() {
192        return Err(ProgramError::UninitializedAccount);
193    }
194
195    if info.data.borrow()[0].ne(&(Config::discriminator() as u8)) {
196        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
197    }
198
199    if is_writable && !info.is_writable {
200        return Err(ProgramError::InvalidAccountData);
201    }
202
203    Ok(())
204}
205
206/// Errors if:
207/// - Owner is not Coal program.
208/// - Address does not match the expected address.
209/// - Data is empty.
210/// - Data cannot deserialize into a config account.
211/// - Expected to be writable, but is not.
212pub fn load_wood_config<'a, 'info>(
213    info: &'a AccountInfo<'info>,
214    is_writable: bool,
215) -> Result<(), ProgramError> {
216    if info.owner.ne(&crate::id()) {
217        return Err(ProgramError::InvalidAccountOwner);
218    }
219
220    if info.key.ne(&WOOD_CONFIG_ADDRESS) {
221        return Err(ProgramError::InvalidSeeds);
222    }
223
224    if info.data_is_empty() {
225        return Err(ProgramError::UninitializedAccount);
226    }
227
228    if info.data.borrow()[0].ne(&(WoodConfig::discriminator() as u8)) {
229        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
230    }
231
232    if is_writable && !info.is_writable {
233        return Err(ProgramError::InvalidAccountData);
234    }
235
236    Ok(())
237}
238
239/// Errors if:
240/// - Owner is not Coal program.
241/// - Data is empty.
242/// - Data cannot deserialize into a proof account.
243/// - Proof authority does not match the expected address.
244/// - Expected to be writable, but is not.
245pub fn load_coal_proof<'a, 'info>(
246    info: &'a AccountInfo<'info>,
247    authority: &Pubkey,
248    is_writable: bool,
249) -> Result<(), ProgramError> {
250    if info.owner.ne(&crate::id()) {
251        return Err(ProgramError::InvalidAccountOwner);
252    }
253
254    if info.data_is_empty() {
255        return Err(ProgramError::UninitializedAccount);
256    }
257
258    let proof_data = info.data.borrow();
259    let proof = Proof::try_from_bytes(&proof_data)?;
260
261    if proof.authority.ne(&authority) {
262        return Err(ProgramError::InvalidAccountData);
263    }
264
265    if is_writable && !info.is_writable {
266        return Err(ProgramError::InvalidAccountData);
267    }
268
269    Ok(())
270}
271
272/// Errors if:
273/// - Owner is not Coal program.
274/// - Data is empty.
275/// - Data cannot deserialize into a proof account.
276/// - Proof authority does not match the expected address.
277/// - Expected to be writable, but is not.
278pub fn load_reprocessor<'a, 'info>(
279    info: &'a AccountInfo<'info>,
280    authority: &Pubkey,
281    is_writable: bool,
282) -> Result<(), ProgramError> {
283    if info.owner.ne(&crate::id()) {
284        return Err(ProgramError::InvalidAccountOwner);
285    }
286
287    if info.data_is_empty() {
288        return Err(ProgramError::UninitializedAccount);
289    }
290
291    let data = info.data.borrow();
292    let reprocessor = Reprocessor::try_from_bytes(&data)?;
293
294    if reprocessor.authority.ne(&authority) {
295        return Err(ProgramError::InvalidAccountData);
296    }
297
298    if is_writable && !info.is_writable {
299        return Err(ProgramError::InvalidAccountData);
300    }
301
302    Ok(())
303}
304
305/// Errors if:
306/// - Owner is not Coal program.
307/// - Data is empty.
308/// - Data cannot deserialize into a proof account.
309/// - Proof authority does not match the expected address.
310/// - Expected to be writable, but is not.
311pub fn load_proof_v2<'a, 'info>(
312    info: &'a AccountInfo<'info>,
313    authority: &Pubkey,
314    resource: &Pubkey,
315    is_writable: bool,
316) -> Result<(), ProgramError> {
317    if info.owner.ne(&crate::id()) {
318        return Err(ProgramError::InvalidAccountOwner);
319    }
320
321    if info.data_is_empty() {
322        return Err(ProgramError::UninitializedAccount);
323    }
324
325    let proof_data = info.data.borrow();
326    let proof = ProofV2::try_from_bytes(&proof_data)?;
327
328    if proof.resource.ne(&resource) {
329        return Err(ProgramError::InvalidAccountData);
330    }
331
332    if proof.authority.ne(&authority) {
333        return Err(ProgramError::InvalidAccountData);
334    }
335
336    if is_writable && !info.is_writable {
337        return Err(ProgramError::InvalidAccountData);
338    }
339
340    Ok(())
341}
342
343/// Errors if:
344/// - Owner is not Coal program.
345/// - Data is empty.
346/// - Data cannot deserialize into a proof account.
347/// - Proof miner does not match the expected address.
348/// - Expected to be writable, but is not.
349pub fn load_coal_proof_with_miner<'a, 'info>(
350    info: &'a AccountInfo<'info>,
351    miner: &Pubkey,
352    is_writable: bool,
353) -> Result<(), ProgramError> {
354    if info.owner.ne(&crate::id()) {
355        return Err(ProgramError::InvalidAccountOwner);
356    }
357
358    if info.data_is_empty() {
359        return Err(ProgramError::UninitializedAccount);
360    }
361
362    let proof_data = info.data.borrow();
363    let proof = Proof::try_from_bytes(&proof_data)?;
364
365    if proof.miner.ne(&miner) {
366        return Err(ProgramError::InvalidAccountData);
367    }
368
369    if is_writable && !info.is_writable {
370        return Err(ProgramError::InvalidAccountData);
371    }
372
373    Ok(())
374}
375
376/// Errors if:
377/// - Owner is not Coal program.
378/// - Data is empty.
379/// - Data cannot deserialize into a proof account.
380/// - Proof miner does not match the expected address.
381/// - Expected to be writable, but is not.
382pub fn load_proof_v2_with_miner<'a, 'info>(
383    info: &'a AccountInfo<'info>,
384    miner: &Pubkey,
385    resource: &Pubkey,
386    is_writable: bool,
387) -> Result<(), ProgramError> {
388    if info.owner.ne(&crate::id()) {
389        return Err(ProgramError::InvalidAccountOwner);
390    }
391
392    if info.data_is_empty() {
393        return Err(ProgramError::UninitializedAccount);
394    }
395
396    let proof_data = info.data.borrow();
397    let proof = ProofV2::try_from_bytes(&proof_data)?;
398
399    if proof.resource.ne(&resource) {
400        return Err(ProgramError::InvalidAccountData);
401    }
402
403    if proof.miner.ne(&miner) {
404        return Err(ProgramError::InvalidAccountData);
405    }
406
407    if is_writable && !info.is_writable {
408        return Err(ProgramError::InvalidAccountData);
409    }
410
411    Ok(())
412}
413
414/// Errors if:
415/// - Owner is not Coal program.
416/// - Data is empty.
417/// - Data cannot deserialize into a proof account.
418/// - Expected to be writable, but is not.
419pub fn load_any_coal_proof<'a, 'info>(
420    info: &'a AccountInfo<'info>,
421    is_writable: bool,
422) -> Result<(), ProgramError> {
423    if info.owner.ne(&crate::id()) {
424        return Err(ProgramError::InvalidAccountOwner);
425    }
426
427    if info.data_is_empty() {
428        return Err(ProgramError::UninitializedAccount);
429    }
430
431    if info.data.borrow()[0].ne(&(Proof::discriminator() as u8)) {
432        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
433    }
434
435    if is_writable && !info.is_writable {
436        return Err(ProgramError::InvalidAccountData);
437    }
438
439    Ok(())
440}
441
442/// Errors if:
443/// - Owner is not Coal program.
444/// - Data is empty.
445/// - Data cannot deserialize into a proof account.
446/// - Expected to be writable, but is not.
447pub fn load_any_proof_v2<'a, 'info>(
448    info: &'a AccountInfo<'info>,
449    is_writable: bool,
450) -> Result<(), ProgramError> {
451    if info.owner.ne(&crate::id()) {
452        return Err(ProgramError::InvalidAccountOwner);
453    }
454
455    if info.data_is_empty() {
456        return Err(ProgramError::UninitializedAccount);
457    }
458
459    if info.data.borrow()[0].ne(&(ProofV2::discriminator() as u8)) {
460        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
461    }
462
463    if is_writable && !info.is_writable {
464        return Err(ProgramError::InvalidAccountData);
465    }
466
467    Ok(())
468}
469
470/// Errors if:
471/// - Owner is not Coal program.
472/// - Address does not match the expected address.
473/// - Data is empty.
474/// - Data cannot deserialize into a treasury account.
475/// - Expected to be writable, but is not.
476pub fn load_treasury<'a, 'info>(
477    info: &'a AccountInfo<'info>,
478    is_writable: bool,
479) -> Result<(), ProgramError> {
480    if info.owner.ne(&crate::id()) {
481        return Err(ProgramError::InvalidAccountOwner);
482    }
483
484    if info.key.ne(&TREASURY_ADDRESS) {
485        return Err(ProgramError::InvalidSeeds);
486    }
487
488    if info.data_is_empty() {
489        return Err(ProgramError::UninitializedAccount);
490    }
491
492    if info.data.borrow()[0].ne(&(Treasury::discriminator() as u8)) {
493        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
494    }
495
496    if is_writable && !info.is_writable {
497        return Err(ProgramError::InvalidAccountData);
498    }
499
500    Ok(())
501}
502
503/// Errors if:
504/// - Address does not match the expected treasury tokens address.
505/// - Cannot load as a token account
506pub fn load_coal_treasury_tokens<'a, 'info>(
507    info: &'a AccountInfo<'info>,
508    is_writable: bool,
509) -> Result<(), ProgramError> {
510    if info.key.ne(&COAL_TREASURY_TOKENS_ADDRESS) {
511        return Err(ProgramError::InvalidSeeds);
512    }
513
514    load_token_account(info, Some(&TREASURY_ADDRESS), &COAL_MINT_ADDRESS, is_writable)
515}
516
517/// Errors if:
518/// - Address does not match the expected treasury tokens address.
519/// - Cannot load as a token account
520pub fn load_wood_treasury_tokens<'a, 'info>(
521    info: &'a AccountInfo<'info>,
522    is_writable: bool,
523) -> Result<(), ProgramError> {
524    if info.key.ne(&WOOD_TREASURY_TOKENS_ADDRESS) {
525        return Err(ProgramError::InvalidSeeds);
526    }
527
528    load_token_account(info, Some(&TREASURY_ADDRESS), &WOOD_MINT_ADDRESS, is_writable)
529}
530
531/// Errors if:
532/// - Owner is not SPL token program.
533/// - Address does not match the expected mint address.
534/// - Data is empty.
535/// - Data cannot deserialize into a mint account.
536/// - Expected to be writable, but is not.
537pub fn load_mint<'a, 'info>(
538    info: &'a AccountInfo<'info>,
539    address: Pubkey,
540    is_writable: bool,
541) -> Result<(), ProgramError> {
542    if info.owner.ne(&spl_token::id()) {
543        return Err(ProgramError::InvalidAccountOwner);
544    }
545
546    if info.key.ne(&address) {
547        return Err(ProgramError::InvalidSeeds);
548    }
549
550    if info.data_is_empty() {
551        return Err(ProgramError::UninitializedAccount);
552    }
553
554    Mint::unpack(&info.data.borrow())?;
555
556    if is_writable && !info.is_writable {
557        return Err(ProgramError::InvalidAccountData);
558    }
559
560    Ok(())
561}
562
563/// Errors if:
564/// - Owner is not SPL token program.
565/// - Data is empty.
566/// - Data cannot deserialize into a token account.
567/// - Token account owner does not match the expected owner address.
568/// - Token account mint does not match the expected mint address.
569/// - Expected to be writable, but is not.
570pub fn load_token_account<'a, 'info>(
571    info: &'a AccountInfo<'info>,
572    owner: Option<&Pubkey>,
573    mint: &Pubkey,
574    is_writable: bool,
575) -> Result<(), ProgramError> {
576    if info.owner.ne(&spl_token::id()) {
577        return Err(ProgramError::InvalidAccountOwner);
578    }
579
580    if info.data_is_empty() {
581        return Err(ProgramError::UninitializedAccount);
582    }
583
584    let account_data = info.data.borrow();
585    let account = spl_token::state::Account::unpack(&account_data)?;
586
587    if account.mint.ne(&mint) {
588        msg!("Invalid mint: {:?} == {:?}", account.mint, mint);
589        return Err(ProgramError::InvalidAccountData);
590    }
591
592    if let Some(owner) = owner {
593        if account.owner.ne(owner) {
594            msg!("Invalid owner: {:?} == {:?}", account.owner, owner);
595            return Err(ProgramError::InvalidAccountData);
596        }
597    }
598
599    if is_writable && !info.is_writable {
600        msg!("Invalid writable: {:?} == {:?}", info.is_writable, is_writable);
601        return Err(ProgramError::InvalidAccountData);
602    }
603
604    Ok(())
605}
606
607/// Errors if:
608/// - Address does not match PDA derived from provided seeds.
609/// - Cannot load as an uninitialized account.
610pub fn load_uninitialized_pda<'a, 'info>(
611    info: &'a AccountInfo<'info>,
612    seeds: &[&[u8]],
613    bump: u8,
614    program_id: &Pubkey,
615) -> Result<(), ProgramError> {
616    let pda = Pubkey::find_program_address(seeds, program_id);
617
618    if info.key.ne(&pda.0) {
619        return Err(ProgramError::InvalidSeeds);
620    }
621
622    if bump.ne(&pda.1) {
623        return Err(ProgramError::InvalidSeeds);
624    }
625
626    load_system_account(info, true)
627}
628
629/// Errors if:
630/// - Owner is not the system program.
631/// - Data is not empty.
632/// - Account is not writable.
633pub fn load_system_account<'a, 'info>(
634    info: &'a AccountInfo<'info>,
635    is_writable: bool,
636) -> Result<(), ProgramError> {
637    if info.owner.ne(&system_program::id()) {
638        return Err(ProgramError::InvalidAccountOwner);
639    }
640
641    if !info.data_is_empty() {
642        return Err(ProgramError::AccountAlreadyInitialized);
643    }
644
645    if is_writable && !info.is_writable {
646        return Err(ProgramError::InvalidAccountData);
647    }
648
649    Ok(())
650}
651
652/// Errors if:
653/// - Owner is not the sysvar address.
654/// - Account cannot load with the expected address.
655pub fn load_sysvar<'a, 'info>(
656    info: &'a AccountInfo<'info>,
657    key: Pubkey,
658) -> Result<(), ProgramError> {
659    if info.owner.ne(&sysvar::id()) {
660        return Err(ProgramError::InvalidAccountOwner);
661    }
662
663    load_account(info, key, false)
664}
665
666/// Errors if:
667/// - Address does not match the expected value.
668/// - Expected to be writable, but is not.
669pub fn load_account<'a, 'info>(
670    info: &'a AccountInfo<'info>,
671    key: Pubkey,
672    is_writable: bool,
673) -> Result<(), ProgramError> {
674    if info.key.ne(&key) {
675        return Err(ProgramError::InvalidAccountData);
676    }
677
678    if is_writable && !info.is_writable {
679        return Err(ProgramError::InvalidAccountData);
680    }
681
682    Ok(())
683}
684
685/// Errors if:
686/// - Address does not match the expected value.
687/// - Account is not executable.
688pub fn load_program<'a, 'info>(
689    info: &'a AccountInfo<'info>,
690    key: Pubkey,
691) -> Result<(), ProgramError> {
692    if info.key.ne(&key) {
693        return Err(ProgramError::IncorrectProgramId);
694    }
695
696    if !info.executable {
697        return Err(ProgramError::InvalidAccountData);
698    }
699
700    Ok(())
701}
702
703/// Errors if:
704/// - Account is not writable.
705pub fn load_any<'a, 'info>(
706    info: &'a AccountInfo<'info>,
707    is_writable: bool,
708) -> Result<(), ProgramError> {
709    if is_writable && !info.is_writable {
710        return Err(ProgramError::InvalidAccountData);
711    }
712
713    Ok(())
714}
715
716/// Errors if:
717/// - Data is empty.
718/// - Update authority is not the forge pickaxe collection.
719/// - Attributes plugin is not present.
720/// - Durability attribute is not present.
721/// - Multiplier attribute is not present.
722pub fn load_asset<'a, 'info>(
723    info: &'a AccountInfo<'info>,
724) -> Result<(f64, u64, String), ProgramError> {
725    if info.owner.ne(&mpl_core::ID) {
726        return Err(ProgramError::InvalidAccountOwner);
727    }
728
729    if info.data_is_empty() {
730        return Err(ProgramError::UninitializedAccount);
731    }
732
733    let asset = Asset::from_bytes(&info.data.borrow()).unwrap();
734
735    match asset.base.update_authority {
736        UpdateAuthority::Collection(address) => {
737            if address.ne(&FORGE_PICKAXE_COLLECTION) {
738                msg!("Invalid collection: {:?} == {:?}", address, FORGE_PICKAXE_COLLECTION);
739                return Err(ProgramError::InvalidAccountData);
740            }
741        }
742        _ => return Err(ProgramError::InvalidAccountData),
743    }
744
745    if asset.plugin_list.attributes.is_none() {
746        return Err(ProgramError::InvalidAccountData);
747    }
748
749	let attributes_plugin = asset.plugin_list.attributes.unwrap();
750	let durability_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "durability");
751	let multiplier_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "multiplier");
752    let resource_attr = attributes_plugin.attributes.attribute_list.iter().find(|attr| attr.key == "resource");
753    let durability = durability_attr.unwrap().value.parse::<f64>().unwrap();
754    let multiplier = multiplier_attr.unwrap().value.parse::<u64>().unwrap();
755    let resource = resource_attr.unwrap().value.clone();
756    
757    Ok((durability, multiplier, resource))
758}
759
760pub fn load_tool<'a, 'info>(
761    info: &'a AccountInfo<'info>,
762    miner: &Pubkey,
763    is_writable: bool,
764) -> Result<(u64, u64), ProgramError> {    
765    if info.owner.ne(&crate::id()) {
766        return Err(ProgramError::InvalidAccountOwner);
767    }
768
769    if info.data_is_empty() {
770        return Err(ProgramError::UninitializedAccount);
771    }
772    
773    let tool_data = info.data.borrow();
774    let tool = Tool::try_from_bytes(&tool_data).unwrap();
775
776    if tool.miner.ne(&miner) {
777        return Err(ProgramError::InvalidAccountData);
778    }
779
780    if is_writable && !info.is_writable {
781        return Err(ProgramError::InvalidAccountData);
782    }
783
784    Ok((tool.durability, tool.multiplier))
785}
786
787pub fn is_tool<'a, 'info>(info: &'a AccountInfo<'info>,) -> bool {
788    info.data.borrow()[0].eq(&(Tool::discriminator() as u8))
789}
790
791pub fn load_wood_tool<'a, 'info>(
792    info: &'a AccountInfo<'info>,
793    miner: &Pubkey,
794    is_writable: bool,
795) -> Result<(u64, u64), ProgramError> {    
796    if info.owner.ne(&crate::id()) {
797        return Err(ProgramError::InvalidAccountOwner);
798    }
799
800    if info.data_is_empty() {
801        return Err(ProgramError::UninitializedAccount);
802    }
803    
804    let tool_data = info.data.borrow();
805    let tool = WoodTool::try_from_bytes(&tool_data).unwrap();
806
807    if tool.miner.ne(&miner) {
808        return Err(ProgramError::InvalidAccountData);
809    }
810
811    if is_writable && !info.is_writable {
812        return Err(ProgramError::InvalidAccountData);
813    }
814
815    Ok((tool.durability, tool.multiplier))
816}
817
818pub fn load_any_tool_with_asset<'a, 'info>(
819    info: &'a AccountInfo<'info>,
820    miner: &Pubkey,
821    asset: &Pubkey,
822    is_writable: bool,
823) -> Result<u64, ProgramError> {
824    if info.owner.ne(&crate::id()) {
825        return Err(ProgramError::InvalidAccountOwner);
826    }
827
828    if info.data_is_empty() {
829        return Err(ProgramError::UninitializedAccount);
830    }
831
832    if is_writable && !info.is_writable {
833        return Err(ProgramError::InvalidAccountData);
834    }
835    
836    let tool_data = info.data.borrow();
837
838    match tool_data[0] {
839        d if d == Tool::discriminator() as u8 => {
840            let tool = Tool::try_from_bytes(&tool_data).unwrap();
841
842            if tool.miner.ne(&miner) {
843                return Err(ProgramError::InvalidAccountData);
844            }
845
846            if tool.asset.ne(&asset) {
847                return Err(ProgramError::InvalidAccountData);
848            }
849
850            return Ok(tool.durability);
851
852        },
853        d if d == WoodTool::discriminator() as u8 => {
854            let tool = WoodTool::try_from_bytes(&tool_data).unwrap();
855
856            if tool.miner.ne(&miner) {
857                return Err(ProgramError::InvalidAccountData);
858            }
859
860            if tool.asset.ne(&asset) {
861                return Err(ProgramError::InvalidAccountData);
862            }
863
864            return Ok(tool.durability);
865
866        },
867        _ => Err(ProgramError::InvalidAccountData),
868    }
869}
870
871pub fn amount_u64_to_f64(amount: u64) -> f64 {
872    (amount as f64) / 10f64.powf(TOKEN_DECIMALS as f64)
873}
874
875pub fn amount_f64_to_u64(amount: f64) -> u64 {
876    (amount * 10f64.powf(TOKEN_DECIMALS as f64)) as u64
877}