From 92d24b676e218a1ba5b39bd4d139680dad4f42f3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 22:03:18 +0000 Subject: [PATCH] Fix and re-enable token-extensions metadata Anchor example for Anchor 1.0 / Solana 3.x The example failed to build after the repo moved to Anchor 1.0 / Solana 3.x because it pinned ancient spl-token-metadata-interface 0.3.3 / spl-type-length-value 0.4.3, which dragged in an old solana-zk-token-sdk that no longer compiles. - Bump spl-token-metadata-interface to 0.8.0 and spl-type-length-value to 0.9.1 (aligned with the version anchor-spl 1.0 pulls, resolving a VariableLenPack trait version clash). - initialize.rs: replace removed DEFAULT_EXEMPTION_THRESHOLD / DEFAULT_LAMPORTS_PER_BYTE_YEAR rent calc with Rent::get()?.minimum_balance(); drop the now-unused rent imports. - Add the renamed program_id field (formerly token_program_id) to the TokenMetadataInitialize / TokenMetadataUpdateField / TokenMetadataUpdateAuthority CPI account structs. - Rename handle_process_* instruction fns to process_* to match lib.rs. - Make the instructions module public so the LiteSVM test can reference its arg/enum types. - Add #![allow(clippy::diverging_sub_expression)] for the Anchor #[program] macro false positive. - Remove the example from .github/.ghaignore. Verified: cargo build-sbf, cargo test (LiteSVM full flow passes), cargo fmt --check, and cargo clippy --tests -D warnings all pass. https://claude.ai/code/session_013dpnF6uSGWXjkJJZseqzcP --- .github/.ghaignore | 3 -- .../anchor/programs/metadata/Cargo.toml | 4 +- .../metadata/src/instructions/emit.rs | 2 +- .../metadata/src/instructions/initialize.rs | 10 ++--- .../metadata/src/instructions/remove_key.rs | 4 +- .../src/instructions/update_authority.rs | 4 +- .../metadata/src/instructions/update_field.rs | 4 +- .../anchor/programs/metadata/src/lib.rs | 4 +- .../programs/metadata/tests/test_metadata.rs | 40 ++++++++++++++----- 9 files changed, 44 insertions(+), 31 deletions(-) diff --git a/.github/.ghaignore b/.github/.ghaignore index 0ee23466..f282d982 100644 --- a/.github/.ghaignore +++ b/.github/.ghaignore @@ -1,5 +1,2 @@ -# build failed - program outdated -tokens/token-extensions/metadata/anchor - # dependency issues tokens/token-extensions/nft-meta-data-pointer/anchor-example/anchor diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/Cargo.toml b/tokens/token-extensions/metadata/anchor/programs/metadata/Cargo.toml index ca6dfca4..2295ed2e 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/Cargo.toml +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/Cargo.toml @@ -22,8 +22,8 @@ custom-panic = [] [dependencies] anchor-lang = "1.0.0" anchor-spl = "1.0.0" -spl-token-metadata-interface = "0.3.3" -spl-type-length-value = "0.4.3" +spl-token-metadata-interface = "0.8.0" +spl-type-length-value = "0.9.1" [dev-dependencies] litesvm = "0.11.0" diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/emit.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/emit.rs index e52527f8..72749e2f 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/emit.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/emit.rs @@ -11,7 +11,7 @@ pub struct Emit<'info> { // Invoke the emit instruction from spl_token_metadata_interface directly // There is not an anchor CpiContext for this instruction -pub fn handle_process_emit(context: Context) -> Result<()> { +pub fn process_emit(context: Context) -> Result<()> { invoke( &emit( &context.accounts.token_program.key(), // token program id diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/initialize.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/initialize.rs index 4fb894bf..9f57330f 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/initialize.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/initialize.rs @@ -1,7 +1,4 @@ use anchor_lang::prelude::*; -use anchor_lang::solana_program::rent::{ - DEFAULT_EXEMPTION_THRESHOLD, DEFAULT_LAMPORTS_PER_BYTE_YEAR, -}; use anchor_lang::system_program::{transfer, Transfer}; use anchor_spl::token_interface::{ token_metadata_initialize, Mint, Token2022, TokenMetadataInitialize, @@ -27,7 +24,7 @@ pub struct Initialize<'info> { pub system_program: Program<'info, System>, } -pub fn handle_process_initialize(context: Context, args: TokenMetadataArgs) -> Result<()> { +pub fn process_initialize(context: Context, args: TokenMetadataArgs) -> Result<()> { let TokenMetadataArgs { name, symbol, uri } = args; // Define token metadata @@ -42,8 +39,7 @@ pub fn handle_process_initialize(context: Context, args: TokenMetada let data_len = 4 + token_metadata.get_packed_len()?; // Calculate lamports required for the additional metadata - let lamports = - data_len as u64 * DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64; + let lamports = Rent::get()?.minimum_balance(data_len); // Transfer additional lamports to mint account transfer( @@ -62,7 +58,7 @@ pub fn handle_process_initialize(context: Context, args: TokenMetada CpiContext::new( context.accounts.token_program.key(), TokenMetadataInitialize { - token_program_id: context.accounts.token_program.to_account_info(), + program_id: context.accounts.token_program.to_account_info(), mint: context.accounts.mint_account.to_account_info(), metadata: context.accounts.mint_account.to_account_info(), mint_authority: context.accounts.payer.to_account_info(), diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/remove_key.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/remove_key.rs index e2a838b3..d171e18d 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/remove_key.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/remove_key.rs @@ -19,13 +19,13 @@ pub struct RemoveKey<'info> { // Invoke the remove_key instruction from spl_token_metadata_interface directly // There is not an anchor CpiContext for this instruction -pub fn handle_process_remove_key(context: Context, key: String) -> Result<()> { +pub fn process_remove_key(context: Context, key: String) -> Result<()> { invoke( &remove_key( &context.accounts.token_program.key(), // token program id &context.accounts.mint_account.key(), // "metadata" account &context.accounts.update_authority.key(), // update authority - key, // key to remove + key, // key to remove true, // idempotent flag, if true transaction will not fail if key does not exist ), &[ diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_authority.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_authority.rs index 7134e86b..92a8ac1e 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_authority.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_authority.rs @@ -18,7 +18,7 @@ pub struct UpdateAuthority<'info> { pub system_program: Program<'info, System>, } -pub fn handle_process_update_authority(context: Context) -> Result<()> { +pub fn process_update_authority(context: Context) -> Result<()> { let new_authority_key = match &context.accounts.new_authority { Some(account) => OptionalNonZeroPubkey::try_from(Some(account.key()))?, None => OptionalNonZeroPubkey::try_from(None)?, @@ -29,7 +29,7 @@ pub fn handle_process_update_authority(context: Context) -> Res CpiContext::new( context.accounts.token_program.key(), TokenMetadataUpdateAuthority { - token_program_id: context.accounts.token_program.to_account_info(), + program_id: context.accounts.token_program.to_account_info(), metadata: context.accounts.mint_account.to_account_info(), current_authority: context.accounts.current_authority.to_account_info(), diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_field.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_field.rs index 2fc3e591..cdd5504a 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_field.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/instructions/update_field.rs @@ -23,7 +23,7 @@ pub struct UpdateField<'info> { pub system_program: Program<'info, System>, } -pub fn handle_process_update_field(context: Context, args: UpdateFieldArgs) -> Result<()> { +pub fn process_update_field(context: Context, args: UpdateFieldArgs) -> Result<()> { let UpdateFieldArgs { field, value } = args; // Convert to Field type from spl_token_metadata_interface @@ -80,7 +80,7 @@ pub fn handle_process_update_field(context: Context, args: UpdateFi CpiContext::new( context.accounts.token_program.key(), TokenMetadataUpdateField { - token_program_id: context.accounts.token_program.to_account_info(), + program_id: context.accounts.token_program.to_account_info(), metadata: context.accounts.mint_account.to_account_info(), update_authority: context.accounts.authority.to_account_info(), }, diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/src/lib.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/src/lib.rs index 3a5770aa..1496bd5e 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/src/lib.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/src/lib.rs @@ -1,7 +1,9 @@ +#![allow(clippy::diverging_sub_expression)] + use anchor_lang::prelude::*; use instructions::*; -mod instructions; +pub mod instructions; declare_id!("BJHEDXSQfD9kBFvhw8ZCGmPFRihzvbMoxoHUKpXdpn4D"); diff --git a/tokens/token-extensions/metadata/anchor/programs/metadata/tests/test_metadata.rs b/tokens/token-extensions/metadata/anchor/programs/metadata/tests/test_metadata.rs index 5ed4c5ce..3b34a126 100644 --- a/tokens/token-extensions/metadata/anchor/programs/metadata/tests/test_metadata.rs +++ b/tokens/token-extensions/metadata/anchor/programs/metadata/tests/test_metadata.rs @@ -4,11 +4,11 @@ use { InstructionData, ToAccountMetas, }, litesvm::LiteSVM, + solana_keypair::Keypair, solana_kite::{ create_wallet, send_transaction_from_instructions, token_extensions::TOKEN_EXTENSIONS_PROGRAM_ID, }, - solana_keypair::Keypair, solana_signer::Signer, }; @@ -47,7 +47,13 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![initialize_ix], &[&payer, &mint_keypair], &payer.pubkey()).unwrap(); + send_transaction_from_instructions( + &mut svm, + vec![initialize_ix], + &[&payer, &mint_keypair], + &payer.pubkey(), + ) + .unwrap(); // Verify mint exists let mint_account = svm @@ -75,7 +81,8 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![update_name_ix], &[&payer], &payer.pubkey()).unwrap(); + send_transaction_from_instructions(&mut svm, vec![update_name_ix], &[&payer], &payer.pubkey()) + .unwrap(); svm.expire_blockhash(); // Step 3: Add custom field @@ -96,7 +103,13 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![add_custom_field_ix], &[&payer], &payer.pubkey()).unwrap(); + send_transaction_from_instructions( + &mut svm, + vec![add_custom_field_ix], + &[&payer], + &payer.pubkey(), + ) + .unwrap(); svm.expire_blockhash(); // Step 4: Remove custom field @@ -114,7 +127,8 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![remove_key_ix], &[&payer], &payer.pubkey()).unwrap(); + send_transaction_from_instructions(&mut svm, vec![remove_key_ix], &[&payer], &payer.pubkey()) + .unwrap(); svm.expire_blockhash(); // Step 5: Update authority to None @@ -130,7 +144,13 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![update_authority_ix], &[&payer], &payer.pubkey()).unwrap(); + send_transaction_from_instructions( + &mut svm, + vec![update_authority_ix], + &[&payer], + &payer.pubkey(), + ) + .unwrap(); svm.expire_blockhash(); // Step 6: Emit metadata (verify it doesn't fail) @@ -143,14 +163,12 @@ fn test_metadata_full_flow() { } .to_account_metas(None), ); - send_transaction_from_instructions(&mut svm, vec![emit_ix], &[&payer], &payer.pubkey()).unwrap(); + send_transaction_from_instructions(&mut svm, vec![emit_ix], &[&payer], &payer.pubkey()) + .unwrap(); // Verify mint still exists after all operations let mint_account = svm .get_account(&mint_keypair.pubkey()) .expect("Mint account should still exist after all metadata operations"); - assert!( - !mint_account.data.is_empty(), - "Mint should still have data" - ); + assert!(!mint_account.data.is_empty(), "Mint should still have data"); }