From d3b63e3a3c2a00016373dca6f6c0dc43b687c92d Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 2 Aug 2026 09:57:55 +0200 Subject: [PATCH] v0.4.7-pre.004 --- Cargo.toml | 4 +- ...METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md | 4 +- ...TAPLEX_COLLECTIONS_CREATORS_AUTHORITIES.md | 23 ++ kb-lib/CHANGELOG.md | 10 +- kb-lib/README.md | 2 +- kb-lib/TODO.md | 5 +- kb-lib/src/executor.rs | 8 +- kb-lib/src/executor/metadata.rs | 5 +- .../metadata/metaplex_token_metadata.rs | 8 +- .../metaplex_token_metadata/builder.rs | 322 +++++++++++++++++- .../metaplex_token_metadata/executor.rs | 5 +- .../metaplex_token_metadata/intent.rs | 92 ++++- kb-lib/src/lib.rs | 8 +- .../METAPLEX_TOKEN_METADATA_MATRIX.json | 6 +- 14 files changed, 478 insertions(+), 24 deletions(-) create mode 100644 docs/validation/V0_4_7_PRE_004_METAPLEX_COLLECTIONS_CREATORS_AUTHORITIES.md diff --git a/Cargo.toml b/Cargo.toml index cd41637..e0845d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ # file: Cargo.toml -# version: 10 +# version: 11 [workspace] resolver = "3" @@ -18,7 +18,7 @@ members = [ ] [workspace.package] -version = "0.4.7-pre.3" +version = "0.4.7-pre.4" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" diff --git a/docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md b/docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md index 7f8e204..4e1abd5 100644 --- a/docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md +++ b/docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.4.7` — achèvement de Metaplex Token Metadata @@ -362,7 +362,7 @@ La numérotation ci-dessous est un plan fermé initial. Elle peut être regroup - erreurs fail-closed avant signature ; - variantes historiques rejetées explicitement. -### `0.4.7-pre.004` — collections, créateurs et autorités +### `0.4.7-pre.004` — collections, créateurs et autorités — implémentée, validation locale requise **Travaux** diff --git a/docs/validation/V0_4_7_PRE_004_METAPLEX_COLLECTIONS_CREATORS_AUTHORITIES.md b/docs/validation/V0_4_7_PRE_004_METAPLEX_COLLECTIONS_CREATORS_AUTHORITIES.md new file mode 100644 index 0000000..3f379ac --- /dev/null +++ b/docs/validation/V0_4_7_PRE_004_METAPLEX_COLLECTIONS_CREATORS_AUTHORITIES.md @@ -0,0 +1,23 @@ + + + +# Validation `0.4.7-pre.004` — collections, créateurs et autorités + +## Surface livrée + +- wrappers courants `Verify` et `Unverify` ; +- variantes `CreatorV1` et `CollectionV1` ; +- opération dépréciée `SetCollectionSize` avec approbation explicite ; +- validation des combinaisons de comptes et des PDA collection metadata/master edition ; +- anciennes instructions remplacées conservées en decode-only. + +## Validation de préparation + +- audit Rust général : à exécuter ; +- audit des exports : à exécuter ; +- `cargo fmt --all` : à exécuter localement ; +- `cargo check --workspace` : à exécuter localement ; +- `cargo clippy --all-targets` : à exécuter localement ; +- `cargo test -p kb-lib` : à exécuter localement. + +Aucune validation réseau n'est déclarée dans cette prerelease. diff --git a/kb-lib/CHANGELOG.md b/kb-lib/CHANGELOG.md index 94c92e7..8b591d1 100644 --- a/kb-lib/CHANGELOG.md +++ b/kb-lib/CHANGELOG.md @@ -1,8 +1,16 @@ - + # CHANGELOG — kb-lib +## 0.4.7-pre.004 — collections, créateurs et autorités + +- ajout des wrappers courants `Verify` et `Unverify` pour les créateurs et collections ; +- ajout de `SetCollectionSize` comme opération officiellement constructible mais dépréciée, soumise à approbation opérateur explicite ; +- validation des combinaisons de comptes creator/collection et des PDA collection metadata/master edition ; +- ajout des codes d’opération, exports publics, bindings et tests unitaires correspondants ; +- maintien des anciennes instructions de vérification remplacées en decode-only. + ## 0.4.7-pre.003 — fondations de l’exécuteur Metaplex ### Correctif `0.4.7-pre.003-delta-fix-003` diff --git a/kb-lib/README.md b/kb-lib/README.md index 985208f..6d1827e 100644 --- a/kb-lib/README.md +++ b/kb-lib/README.md @@ -58,7 +58,7 @@ Les matrices actives sont maintenues sous [`../test-fixtures/contract-matrices/` ## Statut et limites -La migration des surfaces historiques jusqu’à un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur Metaplex Token Metadata couvre les 58 discriminants et les matérialisations possèdent désormais un contrat fermé de propriété des faits. L’exécuteur Metaplex possède désormais ses fondations typées et les wrappers courants `Create`/`Update`; les autres opérations courantes restent à achever jusqu’à `pre.006`. Le registre ElGamal n’est pas déclaré validé sur Devnet ou Mainnet. +La migration des surfaces historiques jusqu’à un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur Metaplex Token Metadata couvre les 58 discriminants et les matérialisations possèdent désormais un contrat fermé de propriété des faits. L’exécuteur Metaplex possède désormais ses fondations typées et les wrappers courants `Create`/`Update` et `Verify`/`Unverify`, ainsi que les opérations dépréciées `PuffMetadata`, `SetTokenStandard` et `SetCollectionSize`; les autres opérations courantes restent à achever jusqu’à `pre.006`. Le registre ElGamal n’est pas déclaré validé sur Devnet ou Mainnet. ## Documents diff --git a/kb-lib/TODO.md b/kb-lib/TODO.md index e1a621e..ed884e3 100644 --- a/kb-lib/TODO.md +++ b/kb-lib/TODO.md @@ -1,5 +1,5 @@ - + # TODO — kb-lib @@ -7,7 +7,8 @@ - [x] établir le contrat fermé de couverture et de propriété des faits ; - [x] introduire les intents et builders courants `Create`/`Update` ; -- [ ] achever Verify/Unverify, délégations, pNFT, éditions, use, escrow et opérations courantes restantes avant `pre.006` ; +- [x] achever les wrappers courants Verify/Unverify et l’opération dépréciée SetCollectionSize ; +- [ ] achever délégations, pNFT, éditions, use, escrow et opérations courantes restantes avant `pre.006` ; ## Entre `0.4.6` et `0.4.7` — Metaplex Token Metadata diff --git a/kb-lib/src/executor.rs b/kb-lib/src/executor.rs index 50111fa..a10f625 100644 --- a/kb-lib/src/executor.rs +++ b/kb-lib/src/executor.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor.rs -// version: 11 +// version: 12 //! Consolidated executor modules. @@ -214,12 +214,18 @@ pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION; pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES; /// Stable deprecated Metaplex PuffMetadata operation code. pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION; +/// Stable deprecated Metaplex SetCollectionSize operation code. +pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION; /// Stable deprecated Metaplex SetTokenStandard operation code. pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION; /// Current and deprecated Metaplex Token Metadata operations. pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES; +/// Stable operation code for the current Metaplex Unverify wrapper. +pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION; /// Stable operation code for the current Metaplex Update wrapper. pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION; +/// Stable operation code for the current Metaplex Verify wrapper. +pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION; /// Exposes the Metaplex Token Metadata executor. pub use self::metadata::ExMetadataMetaplexTokenMetadataExecutor; /// Reserved `kb-lib.executor.metadata.solana_program_metadata` executor. diff --git a/kb-lib/src/executor/metadata.rs b/kb-lib/src/executor/metadata.rs index 24308e6..849de0a 100644 --- a/kb-lib/src/executor/metadata.rs +++ b/kb-lib/src/executor/metadata.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor/metadata.rs -// version: 5 +// version: 6 //! `metadata` executor family. @@ -10,9 +10,12 @@ mod spl_name_service; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION; +pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES; +pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION; pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION; +pub use self::metaplex_token_metadata::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION; pub use self::metaplex_token_metadata::ExMetadataMetaplexTokenMetadataExecutor; pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataExecutionIntent; pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataOperation; diff --git a/kb-lib/src/executor/metadata/metaplex_token_metadata.rs b/kb-lib/src/executor/metadata/metaplex_token_metadata.rs index df3e1ac..8206a27 100644 --- a/kb-lib/src/executor/metadata/metaplex_token_metadata.rs +++ b/kb-lib/src/executor/metadata/metaplex_token_metadata.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor/metadata/metaplex_token_metadata.rs -// version: 7 +// version: 8 //! Metaplex Token Metadata typed execution surface. @@ -15,12 +15,18 @@ pub use self::intent::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION; pub use self::intent::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES; /// Stable deprecated operation code for PuffMetadata. pub use self::intent::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION; +/// Stable deprecated operation code for SetCollectionSize. +pub use self::intent::EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION; /// Stable deprecated operation code for SetTokenStandard. pub use self::intent::EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION; /// Current and deprecated operations implemented by this prerelease. pub use self::intent::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES; +/// Stable operation code for the current Unverify wrapper. +pub use self::intent::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION; /// Stable operation code for the current Update wrapper. pub use self::intent::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION; +/// Stable operation code for the current Verify wrapper. +pub use self::intent::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION; /// Exposes the complete Metaplex Token Metadata execution intent. pub use self::intent::ExMetaplexTokenMetadataExecutionIntent; /// Exposes the current and deprecated Metaplex Token Metadata operation intent. diff --git a/kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs b/kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs index 88b6577..4aa3901 100644 --- a/kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs +++ b/kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs -// version: 4 +// version: 5 //! Official Metaplex builders and conservative plan validation. @@ -106,6 +106,51 @@ pub(crate) fn executor_metaplex_token_metadata_build_prepared_plan( 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, + ), }; let instruction = match instruction_result { std::result::Result::Ok(value) => value, @@ -152,7 +197,7 @@ fn validate_intent(intent: &crate::ExMetaplexTokenMetadataExecutionIntent) -> kb if !intent.policy.dry_run { return std::result::Result::Err(kb_core::Error::new( "execution_metaplex_dry_run_required", - "Metaplex Token Metadata pre.003 intents are simulation-only", + "Metaplex Token Metadata pre.004 intents are simulation-only", )); } match intent.policy.cost_limit.max_fee_lamports { @@ -391,6 +436,223 @@ fn build_set_token_standard( 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, +) -> kb_core::Result { + if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CollectionV1) + && (collection_mint.is_none() + || collection_metadata.is_none() + || collection_master_edition.is_none()) + { + return std::result::Result::Err(kb_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(kb_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!(kb_program_ids::SYSTEM_PROGRAM_ID, "system_program"), + sysvar_instructions: parse_official_pubkey!( + kb_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, +) -> kb_core::Result { + if matches!(verification_args, mpl_token_metadata::types::VerificationArgs::CollectionV1) + && (collection_mint.is_none() || collection_metadata.is_none()) + { + return std::result::Result::Err(kb_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(kb_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!(kb_program_ids::SYSTEM_PROGRAM_ID, "system_program"), + sysvar_instructions: parse_official_pubkey!( + kb_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, +) -> kb_core::Result { + let mint = match parse_pubkey(collection_mint, "collection_mint") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let metadata = match parse_pubkey(collection_metadata, "collection_metadata") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = validate_metadata_pda(&metadata, &mint) { + return std::result::Result::Err(error); + } + let official = mpl_token_metadata::instructions::SetCollectionSize { + collection_metadata: parse_official_pubkey!(&metadata.to_string(), "collection_metadata"), + collection_authority: parse_official_pubkey!( + &collection_authority.0, + "collection_authority" + ), + collection_mint: parse_official_pubkey!(&mint.to_string(), "collection_mint"), + collection_authority_record: match collection_authority_record { + std::option::Option::Some(value) => std::option::Option::Some(parse_official_pubkey!( + &value.0, + "collection_authority_record" + )), + std::option::Option::None => std::option::Option::None, + }, + } + .instruction(mpl_token_metadata::instructions::SetCollectionSizeInstructionArgs { + set_collection_size_args: mpl_token_metadata::types::SetCollectionSizeArgs { size }, + }); + return std::result::Result::Ok(convert_official_instruction!(official)); +} + fn validate_metadata_pda( metadata: &solana_pubkey::Pubkey, mint: &solana_pubkey::Pubkey, @@ -527,6 +789,7 @@ fn required_signers( #[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(); @@ -634,4 +897,59 @@ mod tests { 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, + } + ); + } + } } diff --git a/kb-lib/src/executor/metadata/metaplex_token_metadata/executor.rs b/kb-lib/src/executor/metadata/metaplex_token_metadata/executor.rs index b784634..fa8b86e 100644 --- a/kb-lib/src/executor/metadata/metaplex_token_metadata/executor.rs +++ b/kb-lib/src/executor/metadata/metaplex_token_metadata/executor.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor/metadata/metaplex_token_metadata/executor.rs -// version: 3 +// version: 4 //! Exact Metaplex Token Metadata capability dispatch. @@ -129,6 +129,7 @@ impl crate::ExApiInstructionExecutor for crate::ExMetadataMetaplexTokenMetadataE #[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(); @@ -158,7 +159,7 @@ mod tests { let supported = ::capability( &executor, &program_id, - crate::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION, + crate::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION, ); assert!(matches!(supported, crate::ExApiExecutionCapability::Supported { .. })); let unknown = ::capability( diff --git a/kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs b/kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs index a53961d..2648631 100644 --- a/kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs +++ b/kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs -// version: 3 +// version: 4 //! Typed current and deprecated Metaplex Token Metadata execution intents. @@ -17,17 +17,30 @@ pub const EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION: &str = /// Stable deprecated operation code for SetTokenStandard, discriminator 35. pub const EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION: &str = "metadata.metaplex_token_metadata.set_token_standard"; +/// Stable operation code for the current Verify wrapper, discriminator 52. +pub const EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION: &str = + "metadata.metaplex_token_metadata.verify"; +/// Stable operation code for the current Unverify wrapper, discriminator 53. +pub const EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION: &str = + "metadata.metaplex_token_metadata.unverify"; +/// Stable deprecated operation code for SetCollectionSize, discriminator 34. +pub const EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION: &str = + "metadata.metaplex_token_metadata.set_collection_size"; /// Deprecated operations that require explicit operator approval at runtime. pub const EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES: &[&str] = &[ EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION, EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION, + EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION, ]; -/// Current and deprecated operations implemented by `0.4.7-pre.003`. +/// Current and deprecated operations implemented through `0.4.7-pre.004`. pub const EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES: &[&str] = &[ EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION, EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION, EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION, EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION, + EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION, + EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION, + EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION, ]; /// One current or explicitly deprecated Metaplex Token Metadata operation. @@ -104,6 +117,57 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional canonical edition PDA. edition: std::option::Option, }, + /// Build the current unified Verify wrapper for a creator or collection. + Verify { + /// Creator, collection update authority or delegated authority signer. + authority: crate::MdPubkey, + /// Optional delegate record PDA. + delegate_record: std::option::Option, + /// Metadata account whose creator or collection relation is verified. + metadata: crate::MdPubkey, + /// Optional collection mint; required for collection verification. + collection_mint: std::option::Option, + /// Optional canonical collection metadata PDA. + collection_metadata: std::option::Option, + /// Optional canonical collection master edition PDA. + collection_master_edition: std::option::Option, + /// Official unified verification variant. + #[ts(type = "unknown")] + verification_args: mpl_token_metadata::types::VerificationArgs, + }, + /// Build the current unified Unverify wrapper for a creator or collection. + Unverify { + /// Creator, collection update authority or delegated authority signer. + authority: crate::MdPubkey, + /// Optional delegate record PDA. + delegate_record: std::option::Option, + /// Metadata account whose creator or collection relation is unverified. + metadata: crate::MdPubkey, + /// Optional collection mint; required for collection unverification. + collection_mint: std::option::Option, + /// Optional canonical collection metadata PDA. + collection_metadata: std::option::Option, + /// Official unified verification variant. + #[ts(type = "unknown")] + verification_args: mpl_token_metadata::types::VerificationArgs, + }, + /// Build obsolete SetCollectionSize retained for compatibility workflows. + #[deprecated( + since = "0.4.7", + note = "SetCollectionSize is obsolete; prefer canonical collection creation and update flows" + )] + SetCollectionSize { + /// Canonical collection metadata PDA. + collection_metadata: crate::MdPubkey, + /// Collection authority signer. + collection_authority: crate::MdPubkey, + /// Collection mint. + collection_mint: crate::MdPubkey, + /// Optional collection authority record PDA. + collection_authority_record: std::option::Option, + /// New collection size. + size: u64, + }, } #[allow(deprecated)] @@ -119,12 +183,22 @@ impl crate::ExMetaplexTokenMetadataOperation { Self::SetTokenStandard { .. } => { crate::EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION }, + Self::Verify { .. } => crate::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION, + Self::Unverify { .. } => crate::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION, + Self::SetCollectionSize { .. } => { + crate::EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION + }, }; } /// Returns whether the operation is obsolete and requires explicit operator approval. pub fn is_deprecated(&self) -> bool { - return matches!(self, Self::PuffMetadata { .. } | Self::SetTokenStandard { .. }); + return matches!( + self, + Self::PuffMetadata { .. } + | Self::SetTokenStandard { .. } + | Self::SetCollectionSize { .. } + ); } } @@ -154,12 +228,12 @@ mod tests { #[test] #[allow(deprecated)] fn operation_codes_are_exact_unique_and_partition_deprecated_operations() { - assert_eq!(crate::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES.len(), 4); + assert_eq!(crate::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES.len(), 7); let mut unique = std::collections::BTreeSet::new(); for operation_code in crate::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES { assert!(unique.insert(*operation_code)); } - assert_eq!(crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES.len(), 2); + assert_eq!(crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES.len(), 3); assert!( crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES .contains(&crate::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION,) @@ -176,6 +250,14 @@ mod tests { !crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES .contains(&crate::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION,) ); + assert!( + !crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES + .contains(&crate::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION,) + ); + assert!( + !crate::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES + .contains(&crate::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION,) + ); } #[test] diff --git a/kb-lib/src/lib.rs b/kb-lib/src/lib.rs index a6fc913..e4b3037 100644 --- a/kb-lib/src/lib.rs +++ b/kb-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: kb-lib/src/lib.rs -// version: 28 +// version: 29 //! Consolidated decoder, executor, materializer and shared model library. #![warn(missing_docs)] @@ -364,12 +364,18 @@ pub use self::executor::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION; pub use self::executor::EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES; /// Stable deprecated Metaplex PuffMetadata operation code. pub use self::executor::EX_METAPLEX_TOKEN_METADATA_PUFF_METADATA_OPERATION; +/// Stable deprecated Metaplex SetCollectionSize operation code. +pub use self::executor::EX_METAPLEX_TOKEN_METADATA_SET_COLLECTION_SIZE_OPERATION; /// Stable deprecated Metaplex SetTokenStandard operation code. pub use self::executor::EX_METAPLEX_TOKEN_METADATA_SET_TOKEN_STANDARD_OPERATION; /// Current and deprecated Metaplex Token Metadata operations. pub use self::executor::EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES; +/// Stable operation code for the current Metaplex Unverify wrapper. +pub use self::executor::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION; /// Stable operation code for the current Metaplex Update wrapper. pub use self::executor::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION; +/// Stable operation code for the current Metaplex Verify wrapper. +pub use self::executor::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION; /// Exposes the Address Lookup Table close operation code. pub use self::executor::EX_SOLANA_CORE_ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION; /// Exposes the Address Lookup Table create operation code. diff --git a/test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json b/test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json index b6e77b9..9c06a16 100644 --- a/test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json +++ b/test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json @@ -4301,7 +4301,7 @@ "authoritative_owned_account_reread", "operation_specific_state_transition" ], - "implementation_status": "planned", + "implementation_status": "implemented_pre_004", "classification_reason": "obsolete_but_officially_constructible_retained_with_rust_deprecation_and_runtime_operator_approval" }, { @@ -6133,7 +6133,7 @@ "authoritative_owned_account_reread", "operation_specific_state_transition" ], - "implementation_status": "scheduled" + "implementation_status": "implemented_pre_004" }, { "discriminator": 53, @@ -6207,7 +6207,7 @@ "authoritative_owned_account_reread", "operation_specific_state_transition" ], - "implementation_status": "scheduled" + "implementation_status": "implemented_pre_004" }, { "discriminator": 54,