1use crate::utils::*;
2use borsh::BorshSerialize;
3use mpl_token_metadata::{
4 id,
5 instruction::{self, MetadataInstruction, MintNewEditionFromMasterEditionViaTokenArgs},
6 state::{EDITION, EDITION_MARKER_BIT_SIZE, PREFIX},
7};
8use solana_program::{
9 borsh::try_from_slice_unchecked,
10 instruction::{AccountMeta, Instruction},
11 sysvar,
12};
13use solana_program_test::*;
14use solana_sdk::{
15 pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, transaction::Transaction,
16};
17
18#[derive(Debug)]
19pub struct EditionMarker {
20 pub new_metadata_pubkey: Pubkey,
21 pub new_edition_pubkey: Pubkey,
22 pub master_edition_pubkey: Pubkey,
23 pub metadata_mint_pubkey: Pubkey,
24 pub mint: Keypair,
25 pub metadata_pubkey: Pubkey,
26 pub pubkey: Pubkey,
27 pub edition: u64,
28 pub token: Keypair,
29 pub metadata_token_pubkey: Pubkey,
30}
31
32impl EditionMarker {
33 pub fn new(metadata: &Metadata, master_edition: &MasterEditionV2, edition: u64) -> Self {
34 let mint = Keypair::new();
35 let mint_pubkey = mint.pubkey();
36 let metadata_mint_pubkey = metadata.mint.pubkey();
37 let program_id = id();
38
39 let edition_number = edition.checked_div(EDITION_MARKER_BIT_SIZE).unwrap();
40 let as_string = edition_number.to_string();
41 let (pubkey, _) = Pubkey::find_program_address(
42 &[
43 PREFIX.as_bytes(),
44 program_id.as_ref(),
45 metadata_mint_pubkey.as_ref(),
46 EDITION.as_bytes(),
47 as_string.as_bytes(),
48 ],
49 &program_id,
50 );
51
52 let metadata_seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()];
53 let (new_metadata_pubkey, _) = Pubkey::find_program_address(metadata_seeds, &id());
54
55 let master_edition_seeds = &[
56 PREFIX.as_bytes(),
57 program_id.as_ref(),
58 mint_pubkey.as_ref(),
59 EDITION.as_bytes(),
60 ];
61 let (new_edition_pubkey, _) = Pubkey::find_program_address(master_edition_seeds, &id());
62
63 EditionMarker {
64 pubkey,
65 edition,
66 mint,
67 metadata_mint_pubkey,
68 metadata_pubkey: metadata.pubkey,
69 master_edition_pubkey: master_edition.pubkey,
70 new_metadata_pubkey,
71 new_edition_pubkey,
72 metadata_token_pubkey: metadata.token.pubkey(),
73 token: Keypair::new(),
74 }
75 }
76
77 pub async fn get_data(
78 &self,
79 context: &mut ProgramTestContext,
80 ) -> mpl_token_metadata::state::EditionMarker {
81 let account = get_account(context, &self.pubkey).await;
82 try_from_slice_unchecked(&account.data).unwrap()
83 }
84
85 pub async fn create_via_vault(
86 &self,
87 context: &mut ProgramTestContext,
88 vault: &Vault,
89 safety_deposit_box: &Pubkey,
90 store: &Pubkey,
91 ) -> Result<(), BanksClientError> {
92 let metaplex_token_vault_id = mpl_token_vault::id();
93 let vault_pubkey = vault.keypair.pubkey();
94
95 let vault_mint_seeds = &[
96 PREFIX.as_bytes(),
97 metaplex_token_vault_id.as_ref(),
98 vault_pubkey.as_ref(),
99 ];
100 let (_authority, _) =
101 Pubkey::find_program_address(vault_mint_seeds, &metaplex_token_vault_id);
102
103 create_mint(context, &self.mint, &context.payer.pubkey(), None).await?;
104 create_token_account(
105 context,
106 &self.token,
107 &self.mint.pubkey(),
108 &context.payer.pubkey(),
109 )
110 .await?;
111 mint_tokens(
112 context,
113 &self.mint.pubkey(),
114 &self.token.pubkey(),
115 1,
116 &context.payer.pubkey(),
117 None,
118 )
119 .await?;
120
121 #[allow(deprecated)]
122 let tx = Transaction::new_signed_with_payer(
123 &[
124 instruction::mint_edition_from_master_edition_via_vault_proxy(
125 id(),
126 self.new_metadata_pubkey,
127 self.new_edition_pubkey,
128 self.master_edition_pubkey,
129 self.mint.pubkey(),
130 self.pubkey,
131 context.payer.pubkey(),
132 context.payer.pubkey(),
133 context.payer.pubkey(),
134 *store,
135 *safety_deposit_box,
136 vault.keypair.pubkey(),
137 context.payer.pubkey(),
138 self.metadata_pubkey,
139 spl_token::id(),
140 mpl_token_vault::id(),
141 self.edition,
142 ),
143 ],
144 Some(&context.payer.pubkey()),
145 &[&context.payer, &context.payer],
146 context.last_blockhash,
147 );
148
149 context.banks_client.process_transaction(tx).await
150 }
151
152 pub async fn create(&self, context: &mut ProgramTestContext) -> Result<(), BanksClientError> {
153 create_mint(context, &self.mint, &context.payer.pubkey(), None).await?;
154 create_token_account(
155 context,
156 &self.token,
157 &self.mint.pubkey(),
158 &context.payer.pubkey(),
159 )
160 .await?;
161 mint_tokens(
162 context,
163 &self.mint.pubkey(),
164 &self.token.pubkey(),
165 1,
166 &context.payer.pubkey(),
167 None,
168 )
169 .await?;
170
171 let tx = Transaction::new_signed_with_payer(
172 &[instruction::mint_new_edition_from_master_edition_via_token(
173 id(),
174 self.new_metadata_pubkey,
175 self.new_edition_pubkey,
176 self.master_edition_pubkey,
177 self.mint.pubkey(),
178 context.payer.pubkey(),
179 context.payer.pubkey(),
180 context.payer.pubkey(),
181 self.metadata_token_pubkey,
182 context.payer.pubkey(),
183 self.metadata_pubkey,
184 self.metadata_mint_pubkey,
185 self.edition,
186 )],
187 Some(&context.payer.pubkey()),
188 &[&context.payer, &context.payer],
189 context.last_blockhash,
190 );
191
192 context.banks_client.process_transaction(tx).await
193 }
194
195 pub async fn create_with_invalid_token_program(
196 &self,
197 context: &mut ProgramTestContext,
198 ) -> Result<(), BanksClientError> {
199 let fake_token_program = Keypair::new();
200 let program_id = mpl_token_metadata::id();
201
202 let edition_number = self.edition.checked_div(EDITION_MARKER_BIT_SIZE).unwrap();
203 let as_string = edition_number.to_string();
204 let (edition_mark_pda, _) = Pubkey::find_program_address(
205 &[
206 PREFIX.as_bytes(),
207 program_id.as_ref(),
208 self.metadata_mint_pubkey.as_ref(),
209 EDITION.as_bytes(),
210 as_string.as_bytes(),
211 ],
212 &program_id,
213 );
214
215 let accounts = vec![
216 AccountMeta::new(self.new_metadata_pubkey, false),
217 AccountMeta::new(self.new_edition_pubkey, false),
218 AccountMeta::new(self.master_edition_pubkey, false),
219 AccountMeta::new(self.mint.pubkey(), false),
220 AccountMeta::new(edition_mark_pda, false),
221 AccountMeta::new_readonly(context.payer.pubkey(), true),
222 AccountMeta::new(context.payer.pubkey(), true),
223 AccountMeta::new_readonly(context.payer.pubkey(), true),
224 AccountMeta::new_readonly(self.token.pubkey(), false),
225 AccountMeta::new_readonly(context.payer.pubkey(), false),
226 AccountMeta::new_readonly(self.metadata_pubkey, false),
227 AccountMeta::new_readonly(fake_token_program.pubkey(), false),
228 AccountMeta::new_readonly(solana_program::system_program::id(), false),
229 AccountMeta::new_readonly(sysvar::rent::id(), false),
230 ];
231
232 let fake_instruction = Instruction {
233 program_id,
234 accounts,
235 data: MetadataInstruction::MintNewEditionFromMasterEditionViaToken(
236 MintNewEditionFromMasterEditionViaTokenArgs {
237 edition: self.edition,
238 },
239 )
240 .try_to_vec()
241 .unwrap(),
242 };
243
244 let tx = Transaction::new_signed_with_payer(
245 &[fake_instruction],
246 Some(&context.payer.pubkey()),
247 &[&context.payer],
248 context.last_blockhash,
249 );
250
251 context.banks_client.process_transaction(tx).await
252 }
253}