1965 lines
98 KiB
Rust
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);
|
|
}
|
|
}
|