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
use borsh::BorshSerialize;
use mpl_utils::{assert_signer, create_or_allocate_account_raw};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
program::invoke,
pubkey::Pubkey,
};
use spl_token::instruction::approve;
use crate::{
assertions::{
metadata::assert_currently_holding,
uses::{assert_burner, assert_use_authority_derivation, process_use_authority_validation},
},
error::MetadataError,
state::{
Key, Metadata, TokenMetadataAccount, UseAuthorityRecord, UseMethod, PREFIX, USER,
USE_AUTHORITY_RECORD_SIZE,
},
};
pub fn process_approve_use_authority(
program_id: &Pubkey,
accounts: &[AccountInfo],
number_of_uses: u64,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let use_authority_record_info = next_account_info(account_info_iter)?;
let owner_info = next_account_info(account_info_iter)?;
let payer = next_account_info(account_info_iter)?;
let user_info = next_account_info(account_info_iter)?;
let token_account_info = next_account_info(account_info_iter)?;
let metadata_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let program_as_burner = next_account_info(account_info_iter)?;
let token_program_account_info = next_account_info(account_info_iter)?;
let system_account_info = next_account_info(account_info_iter)?;
let metadata: Metadata = Metadata::from_account_info(metadata_info)?;
if metadata.uses.is_none() {
return Err(MetadataError::Unusable.into());
}
if *token_program_account_info.key != spl_token::id() {
return Err(MetadataError::InvalidTokenProgram.into());
}
assert_signer(owner_info)?;
assert_signer(payer)?;
assert_currently_holding(
program_id,
owner_info,
metadata_info,
&metadata,
mint_info,
token_account_info,
)?;
let metadata_uses = metadata.uses.unwrap();
let bump_seed = assert_use_authority_derivation(
program_id,
use_authority_record_info,
user_info,
mint_info,
)?;
let use_authority_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
mint_info.key.as_ref(),
USER.as_bytes(),
user_info.key.as_ref(),
&[bump_seed],
];
process_use_authority_validation(use_authority_record_info.data_len(), true)?;
create_or_allocate_account_raw(
*program_id,
use_authority_record_info,
system_account_info,
payer,
USE_AUTHORITY_RECORD_SIZE,
use_authority_seeds,
)?;
if number_of_uses > metadata_uses.remaining {
return Err(MetadataError::NotEnoughUses.into());
}
if metadata_uses.use_method == UseMethod::Burn {
assert_burner(program_as_burner.key)?;
invoke(
&approve(
token_program_account_info.key,
token_account_info.key,
program_as_burner.key,
owner_info.key,
&[],
1,
)
.unwrap(),
&[
token_program_account_info.clone(),
token_account_info.clone(),
program_as_burner.clone(),
owner_info.clone(),
],
)?;
}
let mutable_data = &mut (*use_authority_record_info.try_borrow_mut_data()?);
let mut record = UseAuthorityRecord::from_bytes(mutable_data)?;
record.key = Key::UseAuthorityRecord;
record.allowed_uses = number_of_uses;
record.bump = bump_seed;
record.serialize(mutable_data)?;
Ok(())
}