Files
khadhroony-bot3/ks-lib/src/executor/metadata/metaplex_token_metadata/builder.rs
2026-08-09 22:48:31 +02:00

1965 lines
98 KiB
Rust

// 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<crate::ExApiPreparedExecutionPlan> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_pubkey::Pubkey> {
return parse_str_pubkey(&value.0, field);
}
fn parse_str_pubkey(value: &str, field: &str) -> ks_core::Result<solana_pubkey::Pubkey> {
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<std::option::Option<solana_pubkey::Pubkey>> {
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<crate::ExApiRequiredSigner> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
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<T: borsh_0_10::BorshSerialize>(
discriminator: u8,
accounts: &[(&crate::MdPubkey, bool, bool)],
trailing_account: std::option::Option<(&crate::MdPubkey, bool, bool)>,
args: &T,
) -> ks_core::Result<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
return build_idl_instruction_optional(discriminator, accounts, std::option::Option::None);
}
fn build_idl_instruction_optional_with_args<T: borsh_0_10::BorshSerialize>(
discriminator: u8,
accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)],
args: &T,
) -> ks_core::Result<solana_instruction::Instruction> {
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<std::vec::Vec<u8>>,
) -> ks_core::Result<solana_instruction::Instruction> {
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<solana_instruction::Instruction> {
return build_idl_instruction(discriminator, accounts, std::option::Option::None);
}
fn build_idl_instruction_with_args<T: borsh_0_10::BorshSerialize>(
discriminator: u8,
accounts: &[(&crate::MdPubkey, bool, bool)],
args: &T,
) -> ks_core::Result<solana_instruction::Instruction> {
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<std::vec::Vec<u8>>,
) -> ks_core::Result<solana_instruction::Instruction> {
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<crate::MdPubkey> = (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<crate::MdPubkey> = (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<crate::MdPubkey> = (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<crate::MdPubkey> = (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);
}
}