// file: ks-lib/src/executor/metadata/metaplex_token_metadata/builder.rs // version: 13 //! Official Metaplex builders and conservative plan validation. use std::str::FromStr; // rust-rules: trait-import macro_rules! parse_official_pubkey { ($value:expr, $field:expr) => {{ match std::str::FromStr::from_str($value) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_pubkey_invalid", format!("invalid {} public key: {}", $field, error), )); }, } }}; } macro_rules! parse_optional_official_pubkey { ($value:expr, $field:expr) => {{ match $value { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, $field)) }, std::option::Option::None => std::option::Option::None, } }}; } macro_rules! convert_official_instruction { ($instruction:expr) => {{ let instruction = $instruction; let program_id = match parse_str_pubkey(&instruction.program_id.to_string(), "program_id") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mut accounts = std::vec::Vec::with_capacity(instruction.accounts.len()); for account in instruction.accounts { let pubkey = match parse_str_pubkey(&account.pubkey.to_string(), "instruction_account") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; accounts.push(solana_instruction::AccountMeta { pubkey, is_signer: account.is_signer, is_writable: account.is_writable, }); } solana_instruction::Instruction { program_id, accounts, data: instruction.data, } }}; } #[allow(deprecated)] pub(crate) fn executor_metaplex_token_metadata_build_prepared_plan( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_intent(intent) { return std::result::Result::Err(error); } let instruction_result = match &intent.operation { crate::ExMetaplexTokenMetadataOperation::Create { metadata, master_edition, mint, mint_as_signer, authority, update_authority, update_authority_as_signer, spl_token_program, create_args, } => build_create( intent, metadata, master_edition.as_ref(), mint, *mint_as_signer, authority, update_authority, *update_authority_as_signer, spl_token_program.as_ref(), create_args, ), crate::ExMetaplexTokenMetadataOperation::UpdateAsUpdateAuthorityV2 { authority, mint, metadata, edition, token, authorization_rules_program, authorization_rules, update_args, } => build_update( intent, authority, mint, metadata, edition.as_ref(), token.as_ref(), authorization_rules_program.as_ref(), authorization_rules.as_ref(), update_args, ), crate::ExMetaplexTokenMetadataOperation::PuffMetadata { metadata } => { build_puff_metadata(metadata) }, crate::ExMetaplexTokenMetadataOperation::SetTokenStandard { metadata, update_authority, mint, edition, } => build_set_token_standard(metadata, update_authority, mint, edition.as_ref()), crate::ExMetaplexTokenMetadataOperation::Verify { authority, delegate_record, metadata, collection_mint, collection_metadata, collection_master_edition, verification_args, } => build_verify( authority, delegate_record.as_ref(), metadata, collection_mint.as_ref(), collection_metadata.as_ref(), collection_master_edition.as_ref(), verification_args, ), crate::ExMetaplexTokenMetadataOperation::Unverify { authority, delegate_record, metadata, collection_mint, collection_metadata, verification_args, } => build_unverify( authority, delegate_record.as_ref(), metadata, collection_mint.as_ref(), collection_metadata.as_ref(), verification_args, ), crate::ExMetaplexTokenMetadataOperation::SetCollectionSize { collection_metadata, collection_authority, collection_mint, collection_authority_record, size, } => build_set_collection_size( collection_metadata, collection_authority, collection_mint, collection_authority_record.as_ref(), *size, ), crate::ExMetaplexTokenMetadataOperation::Burn { authority, collection_metadata, metadata, edition, mint, token, master_edition, master_edition_mint, master_edition_token, edition_marker, token_record, burn_args } => build_burn(authority, collection_metadata.as_ref(), metadata, edition.as_ref(), mint, token, master_edition.as_ref(), master_edition_mint.as_ref(), master_edition_token.as_ref(), edition_marker.as_ref(), token_record.as_ref(), burn_args), crate::ExMetaplexTokenMetadataOperation::Delegate { delegate_record, delegate, metadata, master_edition, token_record, mint, token, authority, spl_token_program, authorization_rules_program, authorization_rules, delegate_args } => build_delegate(intent, delegate_record.as_ref(), delegate, metadata, master_edition.as_ref(), token_record.as_ref(), mint, token.as_ref(), authority, spl_token_program.as_ref(), authorization_rules_program.as_ref(), authorization_rules.as_ref(), delegate_args), crate::ExMetaplexTokenMetadataOperation::Revoke { delegate_record, delegate, metadata, master_edition, token_record, mint, token, authority, spl_token_program, authorization_rules_program, authorization_rules, revoke_args } => build_revoke(intent, delegate_record.as_ref(), delegate, metadata, master_edition.as_ref(), token_record.as_ref(), mint, token.as_ref(), authority, spl_token_program.as_ref(), authorization_rules_program.as_ref(), authorization_rules.as_ref(), revoke_args), crate::ExMetaplexTokenMetadataOperation::Lock { authority, token_owner, token, mint, metadata, edition, token_record, spl_token_program, authorization_rules_program, authorization_rules, lock_args } => build_lock(intent, authority, token_owner.as_ref(), token, mint, metadata, edition.as_ref(), token_record.as_ref(), spl_token_program.as_ref(), authorization_rules_program.as_ref(), authorization_rules.as_ref(), lock_args), crate::ExMetaplexTokenMetadataOperation::Unlock { authority, token_owner, token, mint, metadata, edition, token_record, spl_token_program, authorization_rules_program, authorization_rules, unlock_args } => build_unlock(intent, authority, token_owner.as_ref(), token, mint, metadata, edition.as_ref(), token_record.as_ref(), spl_token_program.as_ref(), authorization_rules_program.as_ref(), authorization_rules.as_ref(), unlock_args), crate::ExMetaplexTokenMetadataOperation::Transfer { token, token_owner, destination_token, destination_owner, mint, metadata, edition, token_record, destination_token_record, authority, authorization_rules_program, authorization_rules, transfer_args } => build_transfer(intent, token, token_owner, destination_token, destination_owner, mint, metadata, edition.as_ref(), token_record.as_ref(), destination_token_record.as_ref(), authority, authorization_rules_program.as_ref(), authorization_rules.as_ref(), transfer_args), crate::ExMetaplexTokenMetadataOperation::CloseAccounts { metadata, edition, mint, authority, destination } => build_close_accounts(metadata, edition, mint, authority, destination), crate::ExMetaplexTokenMetadataOperation::DeprecatedCreateMasterEdition { edition, mint, printing_mint, one_time_printing_authorization_mint, update_authority, printing_mint_authority, mint_authority, metadata, payer, token_program, system_program, rent, one_time_printing_authorization_mint_authority } => build_idl_instruction_no_args(2, &[(edition, false, true), (mint, false, true), (printing_mint, false, true), (one_time_printing_authorization_mint, false, true), (update_authority, true, false), (printing_mint_authority, true, false), (mint_authority, true, false), (metadata, false, false), (payer, true, false), (token_program, false, false), (system_program, false, false), (rent, false, false), (one_time_printing_authorization_mint_authority, true, false)]), crate::ExMetaplexTokenMetadataOperation::DeprecatedMintNewEditionFromMasterEditionViaPrintingToken { metadata, edition, master_edition, mint, mint_authority, printing_mint, master_token_account, edition_marker, burn_authority, payer, master_update_authority, master_metadata, token_program, system_program, rent, reservation_list } => build_idl_instruction_no_args(3, &[(metadata, false, true), (edition, false, true), (master_edition, false, true), (mint, false, true), (mint_authority, true, false), (printing_mint, false, true), (master_token_account, false, true), (edition_marker, false, true), (burn_authority, true, false), (payer, true, false), (master_update_authority, false, false), (master_metadata, false, false), (token_program, false, false), (system_program, false, false), (rent, false, false), (reservation_list, false, true)]), crate::ExMetaplexTokenMetadataOperation::UpdatePrimarySaleHappenedViaToken { metadata, owner, token } => build_idl_instruction_no_args(4, &[(metadata, false, true), (owner, true, false), (token, false, false)]), crate::ExMetaplexTokenMetadataOperation::DeprecatedSetReservationList { master_edition, reservation_list, resource } => build_idl_instruction_no_args(5, &[(master_edition, false, true), (reservation_list, false, true), (resource, true, false)]), crate::ExMetaplexTokenMetadataOperation::DeprecatedCreateReservationList { reservation_list, payer, update_authority, master_edition, resource, metadata, system_program, rent } => build_idl_instruction_no_args(6, &[(reservation_list, false, true), (payer, true, false), (update_authority, true, false), (master_edition, false, false), (resource, false, false), (metadata, false, false), (system_program, false, false), (rent, false, false)]), crate::ExMetaplexTokenMetadataOperation::DeprecatedMintPrintingTokensViaToken { destination, token, one_time_printing_authorization_mint, printing_mint, burn_authority, metadata, master_edition, token_program, rent } => build_idl_instruction_no_args(8, &[(destination, false, true), (token, false, true), (one_time_printing_authorization_mint, false, true), (printing_mint, false, true), (burn_authority, true, false), (metadata, false, false), (master_edition, false, false), (token_program, false, false), (rent, false, false)]), crate::ExMetaplexTokenMetadataOperation::DeprecatedMintPrintingTokens { destination, printing_mint, update_authority, metadata, master_edition, token_program, rent } => build_idl_instruction_no_args(9, &[(destination, false, true), (printing_mint, false, true), (update_authority, true, false), (metadata, false, false), (master_edition, false, false), (token_program, false, false), (rent, false, false)]), crate::ExMetaplexTokenMetadataOperation::ConvertMasterEditionV1ToV2 { master_edition, one_time_auth, printing_mint } => build_idl_instruction_no_args(12, &[(master_edition, false, true), (one_time_auth, false, true), (printing_mint, false, true)]), crate::ExMetaplexTokenMetadataOperation::MintNewEditionFromMasterEditionViaVaultProxy { new_metadata, new_edition, master_edition, new_mint, edition_mark_pda, new_mint_authority, payer, vault_authority, safety_deposit_store, safety_deposit_box, vault, new_metadata_update_authority, metadata, token_program, token_vault_program, system_program, rent, args } => build_idl_instruction_with_args(13, &[(new_metadata, false, true), (new_edition, false, true), (master_edition, false, true), (new_mint, false, true), (edition_mark_pda, false, true), (new_mint_authority, true, false), (payer, true, true), (vault_authority, true, false), (safety_deposit_store, false, false), (safety_deposit_box, false, false), (vault, false, false), (new_metadata_update_authority, false, false), (metadata, false, false), (token_program, false, false), (token_vault_program, false, false), (system_program, false, false), (rent, false, false)], args), crate::ExMetaplexTokenMetadataOperation::Utilize { metadata, token_account, mint, use_authority, owner, token_program, ata_program, system_program, rent, use_authority_record, burner, args } => build_idl_instruction_with_args(19, &[(metadata, false, true), (token_account, false, true), (mint, false, true), (use_authority, true, true), (owner, false, false), (token_program, false, false), (ata_program, false, false), (system_program, false, false), (rent, false, false), (use_authority_record, false, true), (burner, false, false)], args), crate::ExMetaplexTokenMetadataOperation::ApproveUseAuthority { use_authority_record, owner, payer, user, owner_token_account, metadata, mint, burner, token_program, system_program, rent, args } => build_idl_instruction_with_args(20, &[(use_authority_record, false, true), (owner, true, true), (payer, true, true), (user, false, false), (owner_token_account, false, true), (metadata, false, false), (mint, false, false), (burner, false, false), (token_program, false, false), (system_program, false, false), (rent, false, false)], args), crate::ExMetaplexTokenMetadataOperation::RevokeUseAuthority { use_authority_record, owner, user, owner_token_account, mint, metadata, token_program, system_program, rent } => build_idl_instruction_no_args(21, &[(use_authority_record, false, true), (owner, true, true), (user, false, false), (owner_token_account, false, true), (mint, false, false), (metadata, false, false), (token_program, false, false), (system_program, false, false), (rent, false, false)]), crate::ExMetaplexTokenMetadataOperation::CreateEscrowAccount { escrow, metadata, mint, token_account, edition, payer, system_program, sysvar_instructions, authority } => build_idl_instruction_trailing_optional_no_args(38, &[(escrow, false, true), (metadata, false, true), (mint, false, false), (token_account, false, false), (edition, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false)], authority.as_ref().map(|value| return (value, true, false))), crate::ExMetaplexTokenMetadataOperation::CloseEscrowAccount { escrow, metadata, mint, token_account, edition, payer, system_program, sysvar_instructions } => build_idl_instruction_no_args(39, &[(escrow, false, true), (metadata, false, true), (mint, false, false), (token_account, false, false), (edition, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false)]), crate::ExMetaplexTokenMetadataOperation::TransferOutOfEscrow { escrow, metadata, payer, attribute_mint, attribute_src, attribute_dst, escrow_mint, escrow_account, system_program, ata_program, token_program, sysvar_instructions, authority, args } => build_idl_instruction_trailing_optional_with_args(40, &[(escrow, false, false), (metadata, false, true), (payer, true, true), (attribute_mint, false, false), (attribute_src, false, true), (attribute_dst, false, true), (escrow_mint, false, false), (escrow_account, false, false), (system_program, false, false), (ata_program, false, false), (token_program, false, false), (sysvar_instructions, false, false)], authority.as_ref().map(|value| return (value, true, false)), args), crate::ExMetaplexTokenMetadataOperation::Mint { token, token_owner, metadata, master_edition, token_record, mint, authority, delegate_record, payer, system_program, sysvar_instructions, spl_token_program, spl_ata_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_optional_with_args(43, &[(std::option::Option::Some(token), false, true), (token_owner.as_ref(), false, false), (std::option::Option::Some(metadata), false, false), (master_edition.as_ref(), false, true), (token_record.as_ref(), false, true), (std::option::Option::Some(mint), false, true), (std::option::Option::Some(authority), true, false), (delegate_record.as_ref(), false, false), (std::option::Option::Some(payer), true, true), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (std::option::Option::Some(spl_token_program), false, false), (std::option::Option::Some(spl_ata_program), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)], args), crate::ExMetaplexTokenMetadataOperation::Migrate { metadata, edition, token, token_owner, mint, payer, authority, collection_metadata, delegate_record, token_record, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules } => build_idl_instruction_optional_no_args(48, &[(std::option::Option::Some(metadata), false, true), (std::option::Option::Some(edition), false, true), (std::option::Option::Some(token), false, true), (std::option::Option::Some(token_owner), false, false), (std::option::Option::Some(mint), false, false), (std::option::Option::Some(payer), true, true), (std::option::Option::Some(authority), true, false), (std::option::Option::Some(collection_metadata), false, false), (std::option::Option::Some(delegate_record), false, false), (std::option::Option::Some(token_record), false, true), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (std::option::Option::Some(spl_token_program), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)]), crate::ExMetaplexTokenMetadataOperation::Use { authority, delegate_record, token, mint, metadata, edition, payer, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_optional_with_args(51, &[(std::option::Option::Some(authority), true, false), (delegate_record.as_ref(), false, true), (token.as_ref(), false, true), (std::option::Option::Some(mint), false, false), (std::option::Option::Some(metadata), false, true), (edition.as_ref(), false, true), (std::option::Option::Some(payer), true, false), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (spl_token_program.as_ref(), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)], args), crate::ExMetaplexTokenMetadataOperation::Collect { authority, recipient } => build_idl_instruction_no_args(54, &[(authority, true, false), (recipient, false, false)]), crate::ExMetaplexTokenMetadataOperation::Print { edition_metadata, edition, edition_mint, edition_token_account_owner, edition_token_account, edition_mint_authority, edition_token_record, master_edition, edition_marker_pda, payer, master_token_account_owner, master_token_account, master_metadata, update_authority, spl_token_program, spl_ata_program, sysvar_instructions, system_program, args } => build_print(edition_metadata, edition, edition_mint, edition_token_account_owner, edition_token_account, edition_mint_authority, edition_token_record.as_ref(), master_edition, edition_marker_pda, payer, master_token_account_owner, master_token_account, master_metadata, update_authority, spl_token_program, spl_ata_program, sysvar_instructions, system_program, args), crate::ExMetaplexTokenMetadataOperation::Resize { metadata, edition, mint, payer, authority, token, system_program } => build_resize(metadata, edition, mint, payer, authority.as_ref(), token.as_ref(), system_program), }; let instruction = match instruction_result { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let required_signers = required_signers(&intent.fee_payer, &instruction.accounts); if let std::result::Result::Err(error) = validate_authorized_signers(intent, &required_signers) { return std::result::Result::Err(error); } return std::result::Result::Ok(crate::ExApiPreparedExecutionPlan { executor_name: "ks-lib-executor.metadata.metaplex_token_metadata".to_string(), executor_version: env!("CARGO_PKG_VERSION").to_string(), intent_id: intent.intent_id.clone(), operation_code: intent.operation.operation_code().to_string(), fee_payer: intent.fee_payer.clone(), instructions: vec![planned_instruction(intent.operation.operation_code(), &instruction)], required_signers, policy: intent.policy.clone(), requested_spend_lamports: intent.max_rent_lamports, requested_compute_unit_price_micro_lamports: std::option::Option::None, }); } fn validate_intent(intent: &crate::ExMetaplexTokenMetadataExecutionIntent) -> ks_core::Result<()> { if intent.intent_id.trim().is_empty() { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_intent_id_empty", "Metaplex Token Metadata execution intent id must not be empty", )); } if intent.operation.is_deprecated() && !intent.allow_deprecated_operation { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_deprecated_operation_not_approved", "deprecated Metaplex Token Metadata operation requires explicit operator approval", )); } if intent.policy.simulation != crate::ExApiExecutionSimulationPolicy::Required { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_simulation_required", "Metaplex Token Metadata execution requires simulation before signing or sending", )); } match intent.policy.cost_limit.max_fee_lamports { std::option::Option::Some(value) if value > 0 => {}, std::option::Option::Some(_) | std::option::Option::None => { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_fee_limit_missing", "Metaplex Token Metadata execution requires a positive transaction fee ceiling", )); }, } match intent.policy.cost_limit.max_spend_lamports { std::option::Option::Some(value) if value >= intent.max_rent_lamports => {}, std::option::Option::Some(_) | std::option::Option::None => { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_spend_limit_insufficient", "Metaplex Token Metadata execution spend ceiling must cover max_rent_lamports", )); }, } return std::result::Result::Ok(()); } #[allow(clippy::too_many_arguments)] fn build_create( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, metadata: &crate::MdPubkey, master_edition: std::option::Option<&crate::MdPubkey>, mint: &crate::MdPubkey, mint_as_signer: bool, authority: &crate::MdPubkey, update_authority: &crate::MdPubkey, update_authority_as_signer: bool, spl_token_program: std::option::Option<&crate::MdProgramId>, create_args: &mpl_token_metadata::types::CreateArgs, ) -> ks_core::Result { let metadata = match parse_pubkey(metadata, "metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mint = match parse_pubkey(mint, "mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::result::Result::Err(error) = validate_metadata_pda(&metadata, &mint) { return std::result::Result::Err(error); } let master_edition = match parse_optional_pubkey(master_edition, "master_edition") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::option::Option::Some(value) = master_edition { if let std::result::Result::Err(error) = validate_edition_pda(&value, &mint) { return std::result::Result::Err(error); } } if !matches!(create_args, mpl_token_metadata::types::CreateArgs::V1 { .. }) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_create_args_unsupported", "only CreateArgs::V1 is current and supported", )); } let official = mpl_token_metadata::instructions::Create { metadata: parse_official_pubkey!(&metadata.to_string(), "metadata"), master_edition: match master_edition.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "master_edition" )), std::option::Option::None => std::option::Option::None, }, mint: (parse_official_pubkey!(&mint.to_string(), "mint"), mint_as_signer), authority: parse_official_pubkey!(&authority.0, "authority"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), update_authority: ( parse_official_pubkey!(&update_authority.0, "update_authority"), update_authority_as_signer, ), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: match spl_token_program { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, "spl_token_program")) }, std::option::Option::None => std::option::Option::None, }, } .instruction(mpl_token_metadata::instructions::CreateInstructionArgs { create_args: create_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_update( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, authority: &crate::MdPubkey, mint: &crate::MdPubkey, metadata: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, token: std::option::Option<&crate::MdPubkey>, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, update_args: &mpl_token_metadata::types::UpdateArgs, ) -> ks_core::Result { if !matches!(update_args, mpl_token_metadata::types::UpdateArgs::AsUpdateAuthorityV2 { .. }) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_update_variant_not_yet_enabled", "pre.003 enables only UpdateArgs::AsUpdateAuthorityV2; delegate variants are scheduled later", )); } let mint = match parse_pubkey(mint, "mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let metadata = match parse_pubkey(metadata, "metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::result::Result::Err(error) = validate_metadata_pda(&metadata, &mint) { return std::result::Result::Err(error); } let edition = match parse_optional_pubkey(edition, "edition") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::option::Option::Some(value) = edition { if let std::result::Result::Err(error) = validate_edition_pda(&value, &mint) { return std::result::Result::Err(error); } } if authorization_rules_program.is_some() != authorization_rules.is_some() { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_rule_set_accounts_incomplete", "authorization rules program and account must be supplied together", )); } let official = mpl_token_metadata::instructions::Update { authority: parse_official_pubkey!(&authority.0, "authority"), delegate_record: std::option::Option::None, token: match token { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, "token")) }, std::option::Option::None => std::option::Option::None, }, mint: parse_official_pubkey!(&mint.to_string(), "mint"), metadata: parse_official_pubkey!(&metadata.to_string(), "metadata"), edition: match edition.as_ref() { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.to_string(), "edition")) }, std::option::Option::None => std::option::Option::None, }, payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), authorization_rules_program: match authorization_rules_program { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: match authorization_rules { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, "authorization_rules")) }, std::option::Option::None => std::option::Option::None, }, } .instruction(mpl_token_metadata::instructions::UpdateInstructionArgs { update_args: update_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_puff_metadata( metadata: &crate::MdPubkey, ) -> ks_core::Result { let metadata = match parse_pubkey(metadata, "metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let official = mpl_token_metadata::instructions::PuffMetadata { metadata: parse_official_pubkey!(&metadata.to_string(), "metadata"), } .instruction(); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_set_token_standard( metadata: &crate::MdPubkey, update_authority: &crate::MdPubkey, mint: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, ) -> ks_core::Result { let metadata = match parse_pubkey(metadata, "metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mint = match parse_pubkey(mint, "mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::result::Result::Err(error) = validate_metadata_pda(&metadata, &mint) { return std::result::Result::Err(error); } let edition = match parse_optional_pubkey(edition, "edition") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::option::Option::Some(value) = edition { if let std::result::Result::Err(error) = validate_edition_pda(&value, &mint) { return std::result::Result::Err(error); } } let official = mpl_token_metadata::instructions::SetTokenStandard { metadata: parse_official_pubkey!(&metadata.to_string(), "metadata"), update_authority: parse_official_pubkey!(&update_authority.0, "update_authority"), mint: parse_official_pubkey!(&mint.to_string(), "mint"), edition: match edition.as_ref() { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.to_string(), "edition")) }, std::option::Option::None => std::option::Option::None, }, } .instruction(); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_verify( authority: &crate::MdPubkey, delegate_record: std::option::Option<&crate::MdPubkey>, metadata: &crate::MdPubkey, collection_mint: std::option::Option<&crate::MdPubkey>, collection_metadata: std::option::Option<&crate::MdPubkey>, collection_master_edition: std::option::Option<&crate::MdPubkey>, verification_args: &mpl_token_metadata::types::VerificationArgs, ) -> ks_core::Result { if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CollectionV1) && (collection_mint.is_none() || collection_metadata.is_none() || collection_master_edition.is_none()) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_collection_accounts_incomplete", "collection verification requires mint, metadata and master edition accounts", )); } if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CreatorV1) && (collection_mint.is_some() || collection_metadata.is_some() || collection_master_edition.is_some()) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_creator_accounts_unexpected", "creator verification must not provide collection accounts", )); } let parsed_collection_mint = match parse_optional_pubkey(collection_mint, "collection_mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let parsed_collection_metadata = match parse_optional_pubkey(collection_metadata, "collection_metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let parsed_collection_master_edition = match parse_optional_pubkey(collection_master_edition, "collection_master_edition") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let ( std::option::Option::Some(mint), std::option::Option::Some(metadata), std::option::Option::Some(edition), ) = ( parsed_collection_mint.as_ref(), parsed_collection_metadata.as_ref(), parsed_collection_master_edition.as_ref(), ) { if let std::result::Result::Err(error) = validate_metadata_pda(metadata, mint) { return std::result::Result::Err(error); } if let std::result::Result::Err(error) = validate_edition_pda(edition, mint) { return std::result::Result::Err(error); } } let official = mpl_token_metadata::instructions::Verify { authority: parse_official_pubkey!(&authority.0, "authority"), delegate_record: match delegate_record { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, "delegate_record")) }, std::option::Option::None => std::option::Option::None, }, metadata: parse_official_pubkey!(&metadata.0, "metadata"), collection_mint: match parsed_collection_mint.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "collection_mint" )), std::option::Option::None => std::option::Option::None, }, collection_metadata: match parsed_collection_metadata.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "collection_metadata" )), std::option::Option::None => std::option::Option::None, }, collection_master_edition: match parsed_collection_master_edition.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "collection_master_edition" )), std::option::Option::None => std::option::Option::None, }, system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), } .instruction(mpl_token_metadata::instructions::VerifyInstructionArgs { verification_args: verification_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_unverify( authority: &crate::MdPubkey, delegate_record: std::option::Option<&crate::MdPubkey>, metadata: &crate::MdPubkey, collection_mint: std::option::Option<&crate::MdPubkey>, collection_metadata: std::option::Option<&crate::MdPubkey>, verification_args: &mpl_token_metadata::types::VerificationArgs, ) -> ks_core::Result { if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CollectionV1) && (collection_mint.is_none() || collection_metadata.is_none()) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_collection_accounts_incomplete", "collection unverification requires mint and metadata accounts", )); } if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CreatorV1) && (collection_mint.is_some() || collection_metadata.is_some()) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_creator_accounts_unexpected", "creator unverification must not provide collection accounts", )); } let parsed_collection_mint = match parse_optional_pubkey(collection_mint, "collection_mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let parsed_collection_metadata = match parse_optional_pubkey(collection_metadata, "collection_metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let (std::option::Option::Some(mint), std::option::Option::Some(metadata)) = (parsed_collection_mint.as_ref(), parsed_collection_metadata.as_ref()) { if let std::result::Result::Err(error) = validate_metadata_pda(metadata, mint) { return std::result::Result::Err(error); } } let official = mpl_token_metadata::instructions::Unverify { authority: parse_official_pubkey!(&authority.0, "authority"), delegate_record: match delegate_record { std::option::Option::Some(value) => { std::option::Option::Some(parse_official_pubkey!(&value.0, "delegate_record")) }, std::option::Option::None => std::option::Option::None, }, metadata: parse_official_pubkey!(&metadata.0, "metadata"), collection_mint: match parsed_collection_mint.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "collection_mint" )), std::option::Option::None => std::option::Option::None, }, collection_metadata: match parsed_collection_metadata.as_ref() { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.to_string(), "collection_metadata" )), std::option::Option::None => std::option::Option::None, }, system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), } .instruction(mpl_token_metadata::instructions::UnverifyInstructionArgs { verification_args: verification_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_set_collection_size( collection_metadata: &crate::MdPubkey, collection_authority: &crate::MdPubkey, collection_mint: &crate::MdPubkey, collection_authority_record: std::option::Option<&crate::MdPubkey>, size: u64, ) -> ks_core::Result { let mint = match parse_pubkey(collection_mint, "collection_mint") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let metadata = match parse_pubkey(collection_metadata, "collection_metadata") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::result::Result::Err(error) = validate_metadata_pda(&metadata, &mint) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::SetCollectionSize { collection_metadata: parse_official_pubkey!(&metadata.to_string(), "collection_metadata"), collection_authority: parse_official_pubkey!( &collection_authority.0, "collection_authority" ), collection_mint: parse_official_pubkey!(&mint.to_string(), "collection_mint"), collection_authority_record: match collection_authority_record { std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( &value.0, "collection_authority_record" )), std::option::Option::None => std::option::Option::None, }, } .instruction(mpl_token_metadata::instructions::SetCollectionSizeInstructionArgs { set_collection_size_args: mpl_token_metadata::types::SetCollectionSizeArgs { size }, }); return std::result::Result::Ok(convert_official_instruction!(official)); } fn validate_metadata_pda( metadata: &solana_pubkey::Pubkey, mint: &solana_pubkey::Pubkey, ) -> ks_core::Result<()> { let program_id = match parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let (expected, _) = solana_pubkey::Pubkey::find_program_address( &[b"metadata", program_id.as_ref(), mint.as_ref()], &program_id, ); if *metadata != expected { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_metadata_pda_mismatch", format!("metadata PDA {} does not match expected {}", metadata, expected), )); } return std::result::Result::Ok(()); } fn validate_edition_pda( edition: &solana_pubkey::Pubkey, mint: &solana_pubkey::Pubkey, ) -> ks_core::Result<()> { let program_id = match parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let (expected, _) = solana_pubkey::Pubkey::find_program_address( &[b"metadata", program_id.as_ref(), mint.as_ref(), b"edition"], &program_id, ); if *edition != expected { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_edition_pda_mismatch", format!("edition PDA {} does not match expected {}", edition, expected), )); } return std::result::Result::Ok(()); } fn validate_authorized_signers( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, required: &[crate::ExApiRequiredSigner], ) -> ks_core::Result<()> { for signer in required { if !intent.policy.authorized_signers.contains(&signer.pubkey) { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_signer_not_authorized", format!("required signer {} is not authorized", signer.pubkey.0), )); } } return std::result::Result::Ok(()); } fn parse_pubkey(value: &crate::MdPubkey, field: &str) -> ks_core::Result { return parse_str_pubkey(&value.0, field); } fn parse_str_pubkey(value: &str, field: &str) -> ks_core::Result { return solana_pubkey::Pubkey::from_str(value).map_err(|error| { return ks_core::Error::new( "execution_metaplex_pubkey_invalid", format!("invalid {field} public key: {error}"), ); }); } fn parse_optional_pubkey( value: std::option::Option<&crate::MdPubkey>, field: &str, ) -> ks_core::Result> { return match value { std::option::Option::Some(value) => { parse_pubkey(value, field).map(std::option::Option::Some) }, std::option::Option::None => std::result::Result::Ok(std::option::Option::None), }; } fn planned_instruction( operation_code: &str, instruction: &solana_instruction::Instruction, ) -> crate::ExApiPlannedInstruction { return crate::ExApiPlannedInstruction { program_id: crate::MdProgramId(instruction.program_id.to_string()), operation_code: operation_code.to_string(), accounts: instruction .accounts .iter() .map(|account| { return crate::ExApiPlannedAccount { pubkey: crate::MdPubkey(account.pubkey.to_string()), is_signer: account.is_signer, is_writable: account.is_writable, }; }) .collect(), data: instruction.data.clone(), }; } fn required_signers( fee_payer: &crate::MdPubkey, accounts: &[solana_instruction::AccountMeta], ) -> std::vec::Vec { let mut required = vec![crate::ExApiRequiredSigner { pubkey: fee_payer.clone(), role: "fee_payer".to_string(), }]; for account in accounts { if !account.is_signer { continue; } let pubkey = crate::MdPubkey(account.pubkey.to_string()); if required.iter().any(|value| return value.pubkey == pubkey) { continue; } required.push(crate::ExApiRequiredSigner { pubkey, role: "metaplex_authority".to_string(), }); } return required; } fn validate_rule_accounts( program: std::option::Option<&crate::MdProgramId>, rules: std::option::Option<&crate::MdPubkey>, ) -> ks_core::Result<()> { if program.is_some() != rules.is_some() { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_rule_set_accounts_incomplete", "authorization rules program and account must be supplied together", )); } return std::result::Result::Ok(()); } #[allow(clippy::too_many_arguments)] fn build_delegate( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, delegate_record: std::option::Option<&crate::MdPubkey>, delegate: &crate::MdPubkey, metadata: &crate::MdPubkey, master_edition: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, mint: &crate::MdPubkey, token: std::option::Option<&crate::MdPubkey>, authority: &crate::MdPubkey, spl_token_program: std::option::Option<&crate::MdProgramId>, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, delegate_args: &mpl_token_metadata::types::DelegateArgs, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_rule_accounts(authorization_rules_program, authorization_rules) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::Delegate { delegate_record: parse_optional_official_pubkey!(delegate_record, "delegate_record"), delegate: parse_official_pubkey!(&delegate.0, "delegate"), metadata: parse_official_pubkey!(&metadata.0, "metadata"), master_edition: parse_optional_official_pubkey!(master_edition, "master_edition"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), mint: parse_official_pubkey!(&mint.0, "mint"), token: parse_optional_official_pubkey!(token, "token"), authority: parse_official_pubkey!(&authority.0, "authority"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: match spl_token_program { std::option::Option::Some(v) => { std::option::Option::Some(parse_official_pubkey!(&v.0, "spl_token_program")) }, std::option::Option::None => std::option::Option::None, }, authorization_rules_program: match authorization_rules_program { std::option::Option::Some(v) => std::option::Option::Some(parse_official_pubkey!( &v.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: parse_optional_official_pubkey!( authorization_rules, "authorization_rules" ), } .instruction(mpl_token_metadata::instructions::DelegateInstructionArgs { delegate_args: delegate_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_revoke( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, delegate_record: std::option::Option<&crate::MdPubkey>, delegate: &crate::MdPubkey, metadata: &crate::MdPubkey, master_edition: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, mint: &crate::MdPubkey, token: std::option::Option<&crate::MdPubkey>, authority: &crate::MdPubkey, spl_token_program: std::option::Option<&crate::MdProgramId>, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, revoke_args: &mpl_token_metadata::types::RevokeArgs, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_rule_accounts(authorization_rules_program, authorization_rules) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::Revoke { delegate_record: parse_optional_official_pubkey!(delegate_record, "delegate_record"), delegate: parse_official_pubkey!(&delegate.0, "delegate"), metadata: parse_official_pubkey!(&metadata.0, "metadata"), master_edition: parse_optional_official_pubkey!(master_edition, "master_edition"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), mint: parse_official_pubkey!(&mint.0, "mint"), token: parse_optional_official_pubkey!(token, "token"), authority: parse_official_pubkey!(&authority.0, "authority"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: match spl_token_program { std::option::Option::Some(v) => { std::option::Option::Some(parse_official_pubkey!(&v.0, "spl_token_program")) }, std::option::Option::None => std::option::Option::None, }, authorization_rules_program: match authorization_rules_program { std::option::Option::Some(v) => std::option::Option::Some(parse_official_pubkey!( &v.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: parse_optional_official_pubkey!( authorization_rules, "authorization_rules" ), } .instruction(mpl_token_metadata::instructions::RevokeInstructionArgs { revoke_args: revoke_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_lock( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, authority: &crate::MdPubkey, token_owner: std::option::Option<&crate::MdPubkey>, token: &crate::MdPubkey, mint: &crate::MdPubkey, metadata: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, spl_token_program: std::option::Option<&crate::MdProgramId>, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, lock_args: &mpl_token_metadata::types::LockArgs, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_rule_accounts(authorization_rules_program, authorization_rules) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::Lock { authority: parse_official_pubkey!(&authority.0, "authority"), token_owner: parse_optional_official_pubkey!(token_owner, "token_owner"), token: parse_official_pubkey!(&token.0, "token"), mint: parse_official_pubkey!(&mint.0, "mint"), metadata: parse_official_pubkey!(&metadata.0, "metadata"), edition: parse_optional_official_pubkey!(edition, "edition"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: match spl_token_program { std::option::Option::Some(v) => { std::option::Option::Some(parse_official_pubkey!(&v.0, "spl_token_program")) }, std::option::Option::None => std::option::Option::None, }, authorization_rules_program: match authorization_rules_program { std::option::Option::Some(v) => std::option::Option::Some(parse_official_pubkey!( &v.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: parse_optional_official_pubkey!( authorization_rules, "authorization_rules" ), } .instruction(mpl_token_metadata::instructions::LockInstructionArgs { lock_args: lock_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_unlock( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, authority: &crate::MdPubkey, token_owner: std::option::Option<&crate::MdPubkey>, token: &crate::MdPubkey, mint: &crate::MdPubkey, metadata: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, spl_token_program: std::option::Option<&crate::MdProgramId>, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, unlock_args: &mpl_token_metadata::types::UnlockArgs, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_rule_accounts(authorization_rules_program, authorization_rules) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::Unlock { authority: parse_official_pubkey!(&authority.0, "authority"), token_owner: parse_optional_official_pubkey!(token_owner, "token_owner"), token: parse_official_pubkey!(&token.0, "token"), mint: parse_official_pubkey!(&mint.0, "mint"), metadata: parse_official_pubkey!(&metadata.0, "metadata"), edition: parse_optional_official_pubkey!(edition, "edition"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: match spl_token_program { std::option::Option::Some(v) => { std::option::Option::Some(parse_official_pubkey!(&v.0, "spl_token_program")) }, std::option::Option::None => std::option::Option::None, }, authorization_rules_program: match authorization_rules_program { std::option::Option::Some(v) => std::option::Option::Some(parse_official_pubkey!( &v.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: parse_optional_official_pubkey!( authorization_rules, "authorization_rules" ), } .instruction(mpl_token_metadata::instructions::UnlockInstructionArgs { unlock_args: unlock_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_transfer( intent: &crate::ExMetaplexTokenMetadataExecutionIntent, token: &crate::MdPubkey, token_owner: &crate::MdPubkey, destination_token: &crate::MdPubkey, destination_owner: &crate::MdPubkey, mint: &crate::MdPubkey, metadata: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, destination_token_record: std::option::Option<&crate::MdPubkey>, authority: &crate::MdPubkey, authorization_rules_program: std::option::Option<&crate::MdProgramId>, authorization_rules: std::option::Option<&crate::MdPubkey>, transfer_args: &mpl_token_metadata::types::TransferArgs, ) -> ks_core::Result { if let std::result::Result::Err(error) = validate_rule_accounts(authorization_rules_program, authorization_rules) { return std::result::Result::Err(error); } let official = mpl_token_metadata::instructions::Transfer { token: parse_official_pubkey!(&token.0, "token"), token_owner: parse_official_pubkey!(&token_owner.0, "token_owner"), destination_token: parse_official_pubkey!(&destination_token.0, "destination_token"), destination_owner: parse_official_pubkey!(&destination_owner.0, "destination_owner"), mint: parse_official_pubkey!(&mint.0, "mint"), metadata: parse_official_pubkey!(&metadata.0, "metadata"), edition: parse_optional_official_pubkey!(edition, "edition"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), destination_token_record: parse_optional_official_pubkey!( destination_token_record, "destination_token_record" ), authority: parse_official_pubkey!(&authority.0, "authority"), payer: parse_official_pubkey!(&intent.fee_payer.0, "fee_payer"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: parse_official_pubkey!( ks_program_ids::SPL_TOKEN_PROGRAM_ID, "spl_token_program" ), spl_ata_program: parse_official_pubkey!( ks_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, "spl_ata_program" ), authorization_rules_program: match authorization_rules_program { std::option::Option::Some(v) => std::option::Option::Some(parse_official_pubkey!( &v.0, "authorization_rules_program" )), std::option::Option::None => std::option::Option::None, }, authorization_rules: parse_optional_official_pubkey!( authorization_rules, "authorization_rules" ), } .instruction(mpl_token_metadata::instructions::TransferInstructionArgs { transfer_args: transfer_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_burn( authority: &crate::MdPubkey, collection_metadata: std::option::Option<&crate::MdPubkey>, metadata: &crate::MdPubkey, edition: std::option::Option<&crate::MdPubkey>, mint: &crate::MdPubkey, token: &crate::MdPubkey, master_edition: std::option::Option<&crate::MdPubkey>, master_edition_mint: std::option::Option<&crate::MdPubkey>, master_edition_token: std::option::Option<&crate::MdPubkey>, edition_marker: std::option::Option<&crate::MdPubkey>, token_record: std::option::Option<&crate::MdPubkey>, burn_args: &mpl_token_metadata::types::BurnArgs, ) -> ks_core::Result { let official = mpl_token_metadata::instructions::Burn { authority: parse_official_pubkey!(&authority.0, "authority"), collection_metadata: parse_optional_official_pubkey!( collection_metadata, "collection_metadata" ), metadata: parse_official_pubkey!(&metadata.0, "metadata"), edition: parse_optional_official_pubkey!(edition, "edition"), mint: parse_official_pubkey!(&mint.0, "mint"), token: parse_official_pubkey!(&token.0, "token"), master_edition: parse_optional_official_pubkey!(master_edition, "master_edition"), master_edition_mint: parse_optional_official_pubkey!( master_edition_mint, "master_edition_mint" ), master_edition_token: parse_optional_official_pubkey!( master_edition_token, "master_edition_token" ), edition_marker: parse_optional_official_pubkey!(edition_marker, "edition_marker"), token_record: parse_optional_official_pubkey!(token_record, "token_record"), system_program: parse_official_pubkey!(ks_program_ids::SYSTEM_PROGRAM_ID, "system_program"), sysvar_instructions: parse_official_pubkey!( ks_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, "sysvar_instructions" ), spl_token_program: parse_official_pubkey!( ks_program_ids::SPL_TOKEN_PROGRAM_ID, "spl_token_program" ), } .instruction(mpl_token_metadata::instructions::BurnInstructionArgs { burn_args: burn_args.clone(), }); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_close_accounts( metadata: &crate::MdPubkey, edition: &crate::MdPubkey, mint: &crate::MdPubkey, authority: &crate::MdPubkey, destination: &crate::MdPubkey, ) -> ks_core::Result { let official = mpl_token_metadata::instructions::CloseAccounts { metadata: parse_official_pubkey!(&metadata.0, "metadata"), edition: parse_official_pubkey!(&edition.0, "edition"), mint: parse_official_pubkey!(&mint.0, "mint"), authority: parse_official_pubkey!(&authority.0, "authority"), destination: parse_official_pubkey!(&destination.0, "destination"), } .instruction(); return std::result::Result::Ok(convert_official_instruction!(official)); } #[allow(clippy::too_many_arguments)] fn build_print( edition_metadata: &crate::MdPubkey, edition: &crate::MdPubkey, edition_mint: &crate::MdPubkey, edition_token_account_owner: &crate::MdPubkey, edition_token_account: &crate::MdPubkey, edition_mint_authority: &crate::MdPubkey, edition_token_record: std::option::Option<&crate::MdPubkey>, master_edition: &crate::MdPubkey, edition_marker_pda: &crate::MdPubkey, payer: &crate::MdPubkey, master_token_account_owner: &crate::MdPubkey, master_token_account: &crate::MdPubkey, master_metadata: &crate::MdPubkey, update_authority: &crate::MdPubkey, spl_token_program: &crate::MdPubkey, spl_ata_program: &crate::MdPubkey, sysvar_instructions: &crate::MdPubkey, system_program: &crate::MdPubkey, args: &mpl_token_metadata::types::PrintArgs, ) -> ks_core::Result { let mut builder = mpl_token_metadata::instructions::PrintBuilder::new(); builder .edition_metadata(parse_official_pubkey!(&edition_metadata.0, "edition_metadata")) .edition(parse_official_pubkey!(&edition.0, "edition")) .edition_mint(parse_official_pubkey!(&edition_mint.0, "edition_mint"), true) .edition_token_account_owner(parse_official_pubkey!( &edition_token_account_owner.0, "edition_token_account_owner" )) .edition_token_account(parse_official_pubkey!( &edition_token_account.0, "edition_token_account" )) .edition_mint_authority(parse_official_pubkey!( &edition_mint_authority.0, "edition_mint_authority" )) .edition_token_record(parse_optional_official_pubkey!( edition_token_record, "edition_token_record" )) .master_edition(parse_official_pubkey!(&master_edition.0, "master_edition")) .edition_marker_pda(parse_official_pubkey!(&edition_marker_pda.0, "edition_marker_pda")) .payer(parse_official_pubkey!(&payer.0, "payer")) .master_token_account_owner( parse_official_pubkey!(&master_token_account_owner.0, "master_token_account_owner"), true, ) .master_token_account(parse_official_pubkey!( &master_token_account.0, "master_token_account" )) .master_metadata(parse_official_pubkey!(&master_metadata.0, "master_metadata")) .update_authority(parse_official_pubkey!(&update_authority.0, "update_authority")) .spl_token_program(parse_official_pubkey!(&spl_token_program.0, "spl_token_program")) .spl_ata_program(parse_official_pubkey!(&spl_ata_program.0, "spl_ata_program")) .sysvar_instructions(parse_official_pubkey!(&sysvar_instructions.0, "sysvar_instructions")) .system_program(parse_official_pubkey!(&system_program.0, "system_program")) .print_args(args.clone()); let official = builder.instruction(); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_resize( metadata: &crate::MdPubkey, edition: &crate::MdPubkey, mint: &crate::MdPubkey, payer: &crate::MdPubkey, authority: std::option::Option<&crate::MdPubkey>, token: std::option::Option<&crate::MdPubkey>, system_program: &crate::MdPubkey, ) -> ks_core::Result { let mut builder = mpl_token_metadata::instructions::ResizeBuilder::new(); builder .metadata(parse_official_pubkey!(&metadata.0, "metadata")) .edition(parse_official_pubkey!(&edition.0, "edition")) .mint(parse_official_pubkey!(&mint.0, "mint")) .payer(parse_official_pubkey!(&payer.0, "payer"), true) .authority(parse_optional_official_pubkey!(authority, "authority")) .token(parse_optional_official_pubkey!(token, "token")) .system_program(parse_official_pubkey!(&system_program.0, "system_program")); let official = builder.instruction(); return std::result::Result::Ok(convert_official_instruction!(official)); } fn build_idl_instruction_trailing_optional_no_args( discriminator: u8, accounts: &[(&crate::MdPubkey, bool, bool)], trailing_account: std::option::Option<(&crate::MdPubkey, bool, bool)>, ) -> ks_core::Result { let mut exact_accounts = accounts.to_vec(); if let std::option::Option::Some(value) = trailing_account { exact_accounts.push(value); } return build_idl_instruction_no_args(discriminator, exact_accounts.as_slice()); } fn build_idl_instruction_trailing_optional_with_args( discriminator: u8, accounts: &[(&crate::MdPubkey, bool, bool)], trailing_account: std::option::Option<(&crate::MdPubkey, bool, bool)>, args: &T, ) -> ks_core::Result { let mut exact_accounts = accounts.to_vec(); if let std::option::Option::Some(value) = trailing_account { exact_accounts.push(value); } return build_idl_instruction_with_args(discriminator, exact_accounts.as_slice(), args); } fn build_idl_instruction_optional_no_args( discriminator: u8, accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)], ) -> ks_core::Result { return build_idl_instruction_optional(discriminator, accounts, std::option::Option::None); } fn build_idl_instruction_optional_with_args( discriminator: u8, accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)], args: &T, ) -> ks_core::Result { let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_arguments_serialize_failed", error.to_string(), )); }, }; return build_idl_instruction_optional( discriminator, accounts, std::option::Option::Some(encoded), ); } fn build_idl_instruction_optional( discriminator: u8, accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)], encoded_args: std::option::Option>, ) -> ks_core::Result { let program_id = match parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mut metas = std::vec::Vec::with_capacity(accounts.len()); for (value, is_signer, is_writable) in accounts { match value { std::option::Option::Some(value) => { let pubkey = match parse_pubkey(value, "instruction_account") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; metas.push(solana_instruction::AccountMeta { pubkey, is_signer: *is_signer, is_writable: *is_writable, }); }, std::option::Option::None => { metas.push(solana_instruction::AccountMeta { pubkey: program_id, is_signer: false, is_writable: false, }); }, } } let mut data = vec![discriminator]; if let std::option::Option::Some(mut value) = encoded_args { data.append(&mut value); } return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts: metas, data, }); } fn build_idl_instruction_no_args( discriminator: u8, accounts: &[(&crate::MdPubkey, bool, bool)], ) -> ks_core::Result { return build_idl_instruction(discriminator, accounts, std::option::Option::None); } fn build_idl_instruction_with_args( discriminator: u8, accounts: &[(&crate::MdPubkey, bool, bool)], args: &T, ) -> ks_core::Result { let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "execution_metaplex_arguments_serialize_failed", error.to_string(), )); }, }; return build_idl_instruction(discriminator, accounts, std::option::Option::Some(encoded)); } fn build_idl_instruction( discriminator: u8, accounts: &[(&crate::MdPubkey, bool, bool)], encoded_args: std::option::Option>, ) -> ks_core::Result { let program_id = match parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mut metas = std::vec::Vec::with_capacity(accounts.len()); for (value, is_signer, is_writable) in accounts { let pubkey = match parse_pubkey(value, "instruction_account") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; metas.push(solana_instruction::AccountMeta { pubkey, is_signer: *is_signer, is_writable: *is_writable, }); } let mut data = vec![discriminator]; if let std::option::Option::Some(mut value) = encoded_args { data.append(&mut value); } return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts: metas, data, }); } #[cfg(test)] mod tests { #[allow(deprecated)] fn approved_puff_intent() -> crate::ExMetaplexTokenMetadataExecutionIntent { let fee_payer = crate::MdPubkey("11111111111111111111111111111111".to_string()); let mut policy = crate::ExApiExecutionPolicy::default(); policy.cost_limit.max_fee_lamports = std::option::Option::Some(5_000); policy.cost_limit.max_spend_lamports = std::option::Option::Some(10_000); policy.authorized_signers = vec![fee_payer.clone()]; return crate::ExMetaplexTokenMetadataExecutionIntent { intent_id: "metaplex-puff-test".to_string(), fee_payer, max_rent_lamports: 10_000, policy, allow_deprecated_operation: true, operation: crate::ExMetaplexTokenMetadataOperation::PuffMetadata { metadata: crate::MdPubkey( "SysvarRent111111111111111111111111111111111".to_string(), ), }, }; } #[test] #[allow(deprecated)] fn deprecated_operation_requires_explicit_approval() { let mut intent = approved_puff_intent(); intent.allow_deprecated_operation = false; let result = super::validate_intent(&intent); assert_eq!( result.as_ref().err().map(ks_core::Error::code), std::option::Option::Some("execution_metaplex_deprecated_operation_not_approved"), ); } #[test] #[allow(deprecated)] fn policy_requires_simulation_and_sufficient_limits() { let mut intent = approved_puff_intent(); intent.policy.cost_limit.max_spend_lamports = std::option::Option::Some(9_999); let spend_result = super::validate_intent(&intent); assert_eq!( spend_result.as_ref().err().map(ks_core::Error::code), std::option::Option::Some("execution_metaplex_spend_limit_insufficient"), ); intent.policy.cost_limit.max_spend_lamports = std::option::Option::Some(10_000); intent.policy.dry_run = false; assert!(super::validate_intent(&intent).is_ok()); } #[test] fn metadata_and_edition_pda_validation_is_exact() { let program_id_result = super::parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ); assert!(program_id_result.is_ok()); let program_id = match program_id_result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let mint = solana_pubkey::Pubkey::new_unique(); let (metadata, _) = solana_pubkey::Pubkey::find_program_address( &[b"metadata", program_id.as_ref(), mint.as_ref()], &program_id, ); let (edition, _) = solana_pubkey::Pubkey::find_program_address( &[b"metadata", program_id.as_ref(), mint.as_ref(), b"edition"], &program_id, ); assert!(super::validate_metadata_pda(&metadata, &mint).is_ok()); assert!(super::validate_edition_pda(&edition, &mint).is_ok()); let wrong = solana_pubkey::Pubkey::new_unique(); let metadata_result = super::validate_metadata_pda(&wrong, &mint); assert_eq!( metadata_result.as_ref().err().map(ks_core::Error::code), std::option::Option::Some("execution_metaplex_metadata_pda_mismatch"), ); let edition_result = super::validate_edition_pda(&wrong, &mint); assert_eq!( edition_result.as_ref().err().map(ks_core::Error::code), std::option::Option::Some("execution_metaplex_edition_pda_mismatch"), ); } #[test] #[allow(deprecated)] fn puff_metadata_builds_one_exact_instruction_and_deduplicates_fee_payer() { let intent = approved_puff_intent(); let result = super::executor_metaplex_token_metadata_build_prepared_plan(&intent); assert!(result.is_ok()); let plan = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(plan.operation_code, crate::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION,); assert_eq!(plan.instructions.len(), 1); assert_eq!( plan.instructions[0].program_id.0, ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, ); assert_eq!(plan.instructions[0].data.first().copied(), std::option::Option::Some(14),); assert_eq!(plan.required_signers.len(), 1); assert_eq!(plan.required_signers[0].pubkey, intent.fee_payer); assert_eq!(plan.required_signers[0].role, "fee_payer"); } #[test] fn creator_verify_and_unverify_build_current_wrappers() { let fee_payer = crate::MdPubkey("11111111111111111111111111111111".to_string()); let authority = crate::MdPubkey("SysvarRent111111111111111111111111111111111".to_string()); let metadata = crate::MdPubkey("SysvarC1ock11111111111111111111111111111111".to_string()); let mut policy = crate::ExApiExecutionPolicy::default(); policy.cost_limit.max_fee_lamports = std::option::Option::Some(5_000); policy.cost_limit.max_spend_lamports = std::option::Option::Some(10_000); policy.authorized_signers = vec![fee_payer.clone(), authority.clone()]; for operation in [ crate::ExMetaplexTokenMetadataOperation::Verify { authority: authority.clone(), delegate_record: std::option::Option::None, metadata: metadata.clone(), collection_mint: std::option::Option::None, collection_metadata: std::option::Option::None, collection_master_edition: std::option::Option::None, verification_args: mpl_token_metadata::types::VerificationArgs::CreatorV1, }, crate::ExMetaplexTokenMetadataOperation::Unverify { authority: authority.clone(), delegate_record: std::option::Option::None, metadata: metadata.clone(), collection_mint: std::option::Option::None, collection_metadata: std::option::Option::None, verification_args: mpl_token_metadata::types::VerificationArgs::CreatorV1, }, ] { let intent = crate::ExMetaplexTokenMetadataExecutionIntent { intent_id: "metaplex-verification-test".to_string(), fee_payer: fee_payer.clone(), max_rent_lamports: 10_000, policy: policy.clone(), allow_deprecated_operation: false, operation, }; let result = super::executor_metaplex_token_metadata_build_prepared_plan(&intent); assert!(result.is_ok()); let plan = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(plan.instructions.len(), 1); assert_eq!( plan.instructions[0].data.first().copied(), match intent.operation { crate::ExMetaplexTokenMetadataOperation::Verify { .. } => std::option::Option::Some(52), crate::ExMetaplexTokenMetadataOperation::Unverify { .. } => std::option::Option::Some(53), _ => std::option::Option::None, } ); } } #[test] fn close_accounts_preserves_discriminator_and_exact_signer() { let result = super::build_close_accounts( &crate::MdPubkey("11111111111111111111111111111111".to_string()), &crate::MdPubkey("SysvarRent111111111111111111111111111111111".to_string()), &crate::MdPubkey("So11111111111111111111111111111111111111112".to_string()), &crate::MdPubkey("Vote111111111111111111111111111111111111111".to_string()), &crate::MdPubkey("Stake11111111111111111111111111111111111111".to_string()), ); assert!(result.is_ok()); let instruction = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(instruction.data.first().copied(), std::option::Option::Some(57)); assert_eq!(instruction.accounts.len(), 5); assert!(instruction.accounts[3].is_signer); } #[test] fn print_uses_current_official_signer_contract() { let accounts: std::vec::Vec = (0..18) .map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string())) .collect(); let result = super::build_print( &accounts[0], &accounts[1], &accounts[2], &accounts[3], &accounts[4], &accounts[5], std::option::Option::Some(&accounts[6]), &accounts[7], &accounts[8], &accounts[9], &accounts[10], &accounts[11], &accounts[12], &accounts[13], &accounts[14], &accounts[15], &accounts[16], &accounts[17], &mpl_token_metadata::types::PrintArgs::V1 { edition: 1 }, ); assert!(result.is_ok()); let instruction = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(instruction.data.first().copied(), std::option::Option::Some(55)); assert_eq!(instruction.accounts.len(), 18); assert!(instruction.accounts[2].is_signer); assert!(instruction.accounts[2].is_writable); assert!(instruction.accounts[5].is_signer); assert!(!instruction.accounts[5].is_writable); assert!(instruction.accounts[9].is_signer); assert!(instruction.accounts[9].is_writable); assert!(instruction.accounts[10].is_signer); assert!(!instruction.accounts[10].is_writable); } #[test] fn resize_uses_current_official_payer_signer_contract() { let accounts: std::vec::Vec = (0..7) .map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string())) .collect(); let result = super::build_resize( &accounts[0], &accounts[1], &accounts[2], &accounts[3], std::option::Option::Some(&accounts[4]), std::option::Option::Some(&accounts[5]), &accounts[6], ); assert!(result.is_ok()); let instruction = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(instruction.data, vec![56]); assert_eq!(instruction.accounts.len(), 7); assert!(instruction.accounts[3].is_signer); assert!(instruction.accounts[3].is_writable); assert!(instruction.accounts[4].is_signer); assert!(!instruction.accounts[4].is_writable); } #[test] fn positional_optional_accounts_use_placeholders_but_escrow_tail_is_omitted() { let accounts: std::vec::Vec = (0..15) .map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string())) .collect(); let program_id = match super::parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let create = super::build_idl_instruction_trailing_optional_no_args( 38, &[ (&accounts[0], false, true), (&accounts[1], false, true), (&accounts[2], false, false), (&accounts[3], false, false), (&accounts[4], false, false), (&accounts[5], true, true), (&accounts[6], false, false), (&accounts[7], false, false), ], std::option::Option::None, ); assert!(create.is_ok()); let create = match create { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(create.accounts.len(), 8); let create_with_authority = super::build_idl_instruction_trailing_optional_no_args( 38, &[ (&accounts[0], false, true), (&accounts[1], false, true), (&accounts[2], false, false), (&accounts[3], false, false), (&accounts[4], false, false), (&accounts[5], true, true), (&accounts[6], false, false), (&accounts[7], false, false), ], std::option::Option::Some((&accounts[8], true, false)), ); assert!(create_with_authority.is_ok()); let create_with_authority = match create_with_authority { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(create_with_authority.accounts.len(), 9); assert_eq!(create_with_authority.accounts[8].pubkey.to_string(), accounts[8].0); assert!(create_with_authority.accounts[8].is_signer); assert!(!create_with_authority.accounts[8].is_writable); let transfer_out = super::build_idl_instruction_trailing_optional_with_args( 40, &[ (&accounts[0], false, false), (&accounts[1], false, true), (&accounts[2], true, true), (&accounts[3], false, false), (&accounts[4], false, true), (&accounts[5], false, true), (&accounts[6], false, false), (&accounts[7], false, false), (&accounts[8], false, false), (&accounts[9], false, false), (&accounts[10], false, false), (&accounts[11], false, false), ], std::option::Option::None, &mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs { amount: 1 }, ); assert!(transfer_out.is_ok()); let transfer_out = match transfer_out { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(transfer_out.accounts.len(), 12); let mint = super::build_idl_instruction_optional_with_args( 43, &[ (std::option::Option::Some(&accounts[0]), false, true), (std::option::Option::None, false, false), (std::option::Option::Some(&accounts[2]), false, false), (std::option::Option::None, false, true), (std::option::Option::None, false, true), (std::option::Option::Some(&accounts[5]), false, true), (std::option::Option::Some(&accounts[6]), true, false), (std::option::Option::None, false, false), (std::option::Option::Some(&accounts[8]), true, true), (std::option::Option::Some(&accounts[9]), false, false), (std::option::Option::Some(&accounts[10]), false, false), (std::option::Option::Some(&accounts[11]), false, false), (std::option::Option::Some(&accounts[12]), false, false), (std::option::Option::None, false, false), (std::option::Option::None, false, false), ], &mpl_token_metadata::types::MintArgs::V1 { amount: 1, authorization_data: std::option::Option::None, }, ); assert!(mint.is_ok()); let mint = match mint { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(mint.accounts.len(), 15); for index in [1_usize, 3, 4, 7, 13, 14] { assert_eq!(mint.accounts[index].pubkey, program_id); assert!(!mint.accounts[index].is_signer); assert!(!mint.accounts[index].is_writable); } let migrate = super::build_idl_instruction_optional_no_args( 48, &[ (std::option::Option::Some(&accounts[0]), false, true), (std::option::Option::Some(&accounts[1]), false, true), (std::option::Option::Some(&accounts[2]), false, true), (std::option::Option::Some(&accounts[3]), false, false), (std::option::Option::Some(&accounts[4]), false, false), (std::option::Option::Some(&accounts[5]), true, true), (std::option::Option::Some(&accounts[6]), true, false), (std::option::Option::Some(&accounts[7]), false, false), (std::option::Option::Some(&accounts[8]), false, false), (std::option::Option::Some(&accounts[9]), false, true), (std::option::Option::Some(&accounts[10]), false, false), (std::option::Option::Some(&accounts[11]), false, false), (std::option::Option::Some(&accounts[12]), false, false), (std::option::Option::None, false, false), (std::option::Option::None, false, false), ], ); assert!(migrate.is_ok()); let migrate = match migrate { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(migrate.accounts[13].pubkey, program_id); assert_eq!(migrate.accounts[14].pubkey, program_id); let use_instruction = super::build_idl_instruction_optional_with_args( 51, &[ (std::option::Option::Some(&accounts[0]), true, false), (std::option::Option::None, false, true), (std::option::Option::None, false, true), (std::option::Option::Some(&accounts[3]), false, false), (std::option::Option::Some(&accounts[4]), false, true), (std::option::Option::None, false, true), (std::option::Option::Some(&accounts[6]), true, false), (std::option::Option::Some(&accounts[7]), false, false), (std::option::Option::Some(&accounts[8]), false, false), (std::option::Option::None, false, false), (std::option::Option::None, false, false), (std::option::Option::None, false, false), ], &mpl_token_metadata::types::UseArgs::V1 { authorization_data: std::option::Option::None, }, ); assert!(use_instruction.is_ok()); let use_instruction = match use_instruction { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(use_instruction.accounts.len(), 12); for index in [1_usize, 2, 5, 9, 10, 11] { assert_eq!(use_instruction.accounts[index].pubkey, program_id); assert!(!use_instruction.accounts[index].is_signer); assert!(!use_instruction.accounts[index].is_writable); } } #[test] fn print_and_resize_accept_absent_current_optional_accounts() { let accounts: std::vec::Vec = (0..18) .map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string())) .collect(); let program_id = match super::parse_str_pubkey( ks_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, "program_id", ) { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let print = super::build_print( &accounts[0], &accounts[1], &accounts[2], &accounts[3], &accounts[4], &accounts[5], std::option::Option::None, &accounts[7], &accounts[8], &accounts[9], &accounts[10], &accounts[11], &accounts[12], &accounts[13], &accounts[14], &accounts[15], &accounts[16], &accounts[17], &mpl_token_metadata::types::PrintArgs::V1 { edition: 1 }, ); assert!(print.is_ok()); let print = match print { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(print.accounts.len(), 18); assert_eq!(print.accounts[6].pubkey, program_id); assert!(!print.accounts[6].is_signer); assert!(!print.accounts[6].is_writable); let resize = super::build_resize( &accounts[0], &accounts[1], &accounts[2], &accounts[3], std::option::Option::None, std::option::Option::None, &accounts[6], ); assert!(resize.is_ok()); let resize = match resize { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(resize.accounts.len(), 7); assert_eq!(resize.accounts[4].pubkey, program_id); assert_eq!(resize.accounts[5].pubkey, program_id); assert!(!resize.accounts[4].is_signer); assert!(!resize.accounts[4].is_writable); assert!(!resize.accounts[5].is_signer); assert!(!resize.accounts[5].is_writable); } #[test] fn collect_builds_exact_discriminator_and_signer_contract() { let authority = crate::MdPubkey("11111111111111111111111111111111".to_string()); let recipient = crate::MdPubkey("SysvarRent111111111111111111111111111111111".to_string()); let result = super::build_idl_instruction_no_args( 54, &[(&authority, true, false), (&recipient, false, false)], ); assert!(result.is_ok()); let instruction = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(instruction.data, vec![54]); assert_eq!(instruction.accounts.len(), 2); assert!(instruction.accounts[0].is_signer); assert!(!instruction.accounts[1].is_signer); } }