#![cfg(feature = "test-bpf")] mod utils; use sol_nft_metadata::{error::MetadataError, id, instruction, state::Key}; use mpl_token_vault::state::PREFIX; use num_traits::FromPrimitive; use solana_program_test::*; use solana_sdk::{ instruction::InstructionError, pubkey::Pubkey, signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, transport::TransportError, }; use utils::*; // NOTE: these tests depend on the token-vault program having been compiled // via (cd ../../token-vault/program/ && cargo build-bpf) mod mint_new_edition_from_master_edition_via_vault_proxy { use super::*; #[tokio::test] async fn success() { let mut program_test = program_test(); program_test.add_program("mpl_token_vault", mpl_token_vault::id(), None); let mut context = program_test.start_with_context().await; let test_metadata = Metadata::new(); let test_master_edition = MasterEditionV2::new(&test_metadata); let test_external_price = ExternalPrice::new(); let test_vault = Vault::new(); let test_edition_marker = EditionMarker::new(&test_metadata, &test_master_edition, 10); test_metadata .create( &mut context, "Test".to_string(), "TST".to_string(), "uri".to_string(), None, 10, false, ) .await .unwrap(); test_master_edition .create(&mut context, Some(10)) .await .unwrap(); test_external_price.create(&mut context).await.unwrap(); test_external_price .update( &mut context, 1, &test_external_price.price_mint.pubkey(), true, ) .await .unwrap(); test_vault .create(&mut context, &test_external_price) .await .unwrap(); let (safety_deposit_box, store) = test_vault .add_token_to_inactive_vault(&mut context, 1, &test_metadata) .await .unwrap(); test_vault.activate(&mut context, 1).await.unwrap(); test_vault .combine(&mut context, &test_external_price) .await .unwrap(); test_edition_marker .create_via_vault(&mut context, &test_vault, &safety_deposit_box, &store) .await .unwrap(); let edition_marker = test_edition_marker.get_data(&mut context).await; assert_eq!(edition_marker.ledger[1], 32); assert_eq!(edition_marker.key, Key::EditionMarker); } #[tokio::test] async fn fail_invalid_store_owner_pda() { let mut program_test = program_test(); program_test.add_program("mpl_token_vault", mpl_token_vault::id(), None); let mut context = program_test.start_with_context().await; let test_metadata = Metadata::new(); let test_master_edition = MasterEditionV2::new(&test_metadata); let test_external_price = ExternalPrice::new(); let test_vault = Vault::new(); let test_edition_marker = EditionMarker::new(&test_metadata, &test_master_edition, 10); let fake_store = Keypair::new(); let payer_pubkey = context.payer.pubkey(); test_metadata .create( &mut context, "Test".to_string(), "TST".to_string(), "uri".to_string(), None, 10, false, ) .await .unwrap(); test_master_edition .create(&mut context, Some(10)) .await .unwrap(); test_external_price.create(&mut context).await.unwrap(); test_external_price .update( &mut context, 1, &test_external_price.price_mint.pubkey(), true, ) .await .unwrap(); test_vault .create(&mut context, &test_external_price) .await .unwrap(); let (safety_deposit_box, _) = test_vault .add_token_to_inactive_vault(&mut context, 1, &test_metadata) .await .unwrap(); test_vault.activate(&mut context, 1).await.unwrap(); test_vault .combine(&mut context, &test_external_price) .await .unwrap(); create_token_account( &mut context, &fake_store, &test_vault.mint.pubkey(), &payer_pubkey, ) .await .unwrap(); let tx = Transaction::new_signed_with_payer( &[ instruction::mint_edition_from_master_edition_via_vault_proxy( id(), test_edition_marker.new_metadata_pubkey, test_edition_marker.new_edition_pubkey, test_edition_marker.master_edition_pubkey, test_edition_marker.mint.pubkey(), test_edition_marker.pubkey, context.payer.pubkey(), context.payer.pubkey(), context.payer.pubkey(), fake_store.pubkey(), safety_deposit_box, test_vault.keypair.pubkey(), context.payer.pubkey(), test_edition_marker.metadata_pubkey, spl_token::id(), mpl_token_vault::id(), test_edition_marker.edition, ), ], Some(&context.payer.pubkey()), &[&context.payer, &context.payer], context.last_blockhash, ); let result = context .banks_client .process_transaction(tx) .await .unwrap_err(); assert_custom_error!(result, MetadataError::InvalidOwner); } #[tokio::test] async fn fail_invalid_vault_authority() { let mut program_test = program_test(); program_test.add_program("mpl_token_vault", mpl_token_vault::id(), None); let mut context = program_test.start_with_context().await; let test_metadata = Metadata::new(); let test_master_edition = MasterEditionV2::new(&test_metadata); let test_external_price = ExternalPrice::new(); let test_vault = Vault::new(); let test_edition_marker = EditionMarker::new(&test_metadata, &test_master_edition, 10); let fake_vault_authority = Keypair::new(); test_metadata .create( &mut context, "Test".to_string(), "TST".to_string(), "uri".to_string(), None, 10, false, ) .await .unwrap(); test_master_edition .create(&mut context, Some(10)) .await .unwrap(); test_external_price.create(&mut context).await.unwrap(); test_external_price .update( &mut context, 1, &test_external_price.price_mint.pubkey(), true, ) .await .unwrap(); test_vault .create(&mut context, &test_external_price) .await .unwrap(); let (safety_deposit_box, store) = test_vault .add_token_to_inactive_vault(&mut context, 1, &test_metadata) .await .unwrap(); test_vault.activate(&mut context, 1).await.unwrap(); test_vault .combine(&mut context, &test_external_price) .await .unwrap(); let tx = Transaction::new_signed_with_payer( &[ instruction::mint_edition_from_master_edition_via_vault_proxy( id(), test_edition_marker.new_metadata_pubkey, test_edition_marker.new_edition_pubkey, test_edition_marker.master_edition_pubkey, test_edition_marker.mint.pubkey(), test_edition_marker.pubkey, context.payer.pubkey(), context.payer.pubkey(), fake_vault_authority.pubkey(), store, safety_deposit_box, test_vault.keypair.pubkey(), context.payer.pubkey(), test_edition_marker.metadata_pubkey, spl_token::id(), mpl_token_vault::id(), test_edition_marker.edition, ), ], Some(&context.payer.pubkey()), &[&context.payer, &context.payer, &fake_vault_authority], context.last_blockhash, ); let result = context .banks_client .process_transaction(tx) .await .unwrap_err(); assert_custom_error!( result, mpl_token_vault::error::VaultError::AuthorityDoesNotMatch ); } #[tokio::test] async fn fail_store_account_mismatch() { let mut program_test = program_test(); program_test.add_program("mpl_token_vault", mpl_token_vault::id(), None); let mut context = program_test.start_with_context().await; let test_metadata = Metadata::new(); let test_master_edition = MasterEditionV2::new(&test_metadata); let test_external_price = ExternalPrice::new(); let test_vault = Vault::new(); let test_edition_marker = EditionMarker::new(&test_metadata, &test_master_edition, 10); // TEST VAULT test_metadata .create( &mut context, "Test".to_string(), "TST".to_string(), "uri".to_string(), None, 10, false, ) .await .unwrap(); test_master_edition .create(&mut context, Some(10)) .await .unwrap(); test_external_price.create(&mut context).await.unwrap(); test_external_price .update( &mut context, 1, &test_external_price.price_mint.pubkey(), true, ) .await .unwrap(); test_vault .create(&mut context, &test_external_price) .await .unwrap(); let (safety_deposit_box, _) = test_vault .add_token_to_inactive_vault(&mut context, 1, &test_metadata) .await .unwrap(); test_vault.activate(&mut context, 1).await.unwrap(); test_vault .combine(&mut context, &test_external_price) .await .unwrap(); // Generate fake store let store = Keypair::new(); let token_mint_pubkey = test_metadata.mint.pubkey(); let metaplex_token_vault_id = mpl_token_vault::id(); let vault_pubkey = test_vault.keypair.pubkey(); let seeds = &[ PREFIX.as_bytes(), &vault_pubkey.as_ref(), &token_mint_pubkey.as_ref(), ]; let (_, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); let seeds = &[ PREFIX.as_bytes(), &metaplex_token_vault_id.as_ref(), &vault_pubkey.as_ref(), ]; let (authority, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); create_token_account(&mut context, &store, &token_mint_pubkey, &authority) .await .unwrap(); let tx = Transaction::new_signed_with_payer( &[ instruction::mint_edition_from_master_edition_via_vault_proxy( id(), test_edition_marker.new_metadata_pubkey, test_edition_marker.new_edition_pubkey, test_edition_marker.master_edition_pubkey, test_edition_marker.mint.pubkey(), test_edition_marker.pubkey, context.payer.pubkey(), context.payer.pubkey(), context.payer.pubkey(), store.pubkey(), safety_deposit_box, test_vault.keypair.pubkey(), context.payer.pubkey(), test_edition_marker.metadata_pubkey, spl_token::id(), mpl_token_vault::id(), test_edition_marker.edition, ), ], Some(&context.payer.pubkey()), &[&context.payer, &context.payer], context.last_blockhash, ); let result = context .banks_client .process_transaction(tx) .await .unwrap_err(); assert_custom_error!( result, mpl_token_vault::error::VaultError::StoreDoesNotMatchSafetyDepositBox ); } }