mpl_testing_utils/utils/
edition_marker.rs

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}