From 6b2e869d92c8aedcbe9c83b06396b7b32e73a679 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Wed, 28 Aug 2024 14:29:36 -0400 Subject: [PATCH 1/6] Resizing TM accounts going forward. --- .../src/generated/errors/mplTokenMetadata.ts | 16 +++ .../generated/errors/mpl_token_metadata.rs | 3 + configs/program-scripts/test.sh | 6 +- idls/token_metadata.json | 5 + programs/token-metadata/program/Cargo.toml | 21 +-- .../program/src/assertions/edition.rs | 13 +- programs/token-metadata/program/src/error.rs | 4 + .../program/src/processor/fee/mod.rs | 8 +- .../program/src/processor/metadata/create.rs | 8 +- .../program/src/processor/metadata/print.rs | 8 +- .../processor/metadata/set_token_standard.rs | 2 +- .../metadata/update_metadata_account_v2.rs | 2 +- .../src/processor/verification/collection.rs | 2 +- .../src/processor/verification/creator.rs | 2 +- .../program/src/state/edition.rs | 6 +- .../program/src/state/master_edition.rs | 33 +++-- .../program/src/state/metadata.rs | 32 ++--- .../token-metadata/program/src/state/mod.rs | 31 +++-- .../program/src/state/programmable.rs | 3 + .../program/src/utils/collection.rs | 1 + .../token-metadata/program/src/utils/fee.rs | 26 +++- .../program/src/utils/master_edition.rs | 11 +- .../program/src/utils/metadata.rs | 11 +- .../token-metadata/program/src/utils/mod.rs | 26 ++-- programs/token-metadata/program/tests/fees.rs | 5 +- .../token-metadata/program/tests/print.rs | 5 +- .../program/tests/seralization.rs | 10 +- .../token-metadata/program/tests/update.rs | 7 +- .../program/tests/utils/digital_asset.rs | 8 +- .../program/tests/utils/edition_marker.rs | 28 +++- .../program/tests/utils/master_edition_v2.rs | 14 +- .../program/tests/utils/metadata.rs | 31 ++++- .../token-metadata/program/tests/utils/mod.rs | 122 +++++++++++++++++- 33 files changed, 374 insertions(+), 136 deletions(-) diff --git a/clients/js/src/generated/errors/mplTokenMetadata.ts b/clients/js/src/generated/errors/mplTokenMetadata.ts index 11bd15ae..d9d45a2f 100644 --- a/clients/js/src/generated/errors/mplTokenMetadata.ts +++ b/clients/js/src/generated/errors/mplTokenMetadata.ts @@ -2923,6 +2923,22 @@ nameToErrorMap.set( MissingImmutableOwnerExtensionError ); +/** ExpectedUninitializedAccount: Expected account to be uninitialized */ +export class ExpectedUninitializedAccountError extends ProgramError { + override readonly name: string = 'ExpectedUninitializedAccount'; + + readonly code: number = 0xc7; // 199 + + constructor(program: Program, cause?: Error) { + super('Expected account to be uninitialized', program, cause); + } +} +codeToErrorMap.set(0xc7, ExpectedUninitializedAccountError); +nameToErrorMap.set( + 'ExpectedUninitializedAccount', + ExpectedUninitializedAccountError +); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/clients/rust/src/generated/errors/mpl_token_metadata.rs b/clients/rust/src/generated/errors/mpl_token_metadata.rs index 223106cb..21268700 100644 --- a/clients/rust/src/generated/errors/mpl_token_metadata.rs +++ b/clients/rust/src/generated/errors/mpl_token_metadata.rs @@ -607,6 +607,9 @@ pub enum MplTokenMetadataError { /// 198 (0xC6) - Missing immutable owner extension #[error("Missing immutable owner extension")] MissingImmutableOwnerExtension, + /// 199 (0xC7) - Expected account to be uninitialized + #[error("Expected account to be uninitialized")] + ExpectedUninitializedAccount, } impl solana_program::program_error::PrintProgramError for MplTokenMetadataError { diff --git a/configs/program-scripts/test.sh b/configs/program-scripts/test.sh index e76d13f4..d83bf5b8 100755 --- a/configs/program-scripts/test.sh +++ b/configs/program-scripts/test.sh @@ -35,8 +35,10 @@ for p in ${PROGRAMS[@]}; do cd ${WORKING_DIR}/programs/${p} if [ ! "$(command -v $SOLFMT)" = "" ]; then - CARGO_TERM_COLOR=always cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} 2>&1 | ${SOLFMT} + CARGO_TERM_COLOR=always cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} -- --nocapture 2>&1 | ${SOLFMT} && \ + CARGO_TERM_COLOR=always cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} --features padded -- --nocapture 2>&1 | ${SOLFMT} else - cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} + cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} -- --nocapture && \ + cargo test-sbf --sbf-out-dir ${WORKING_DIR}/${OUTPUT} ${ARGS} --features padded -- --nocapture fi done diff --git a/idls/token_metadata.json b/idls/token_metadata.json index 01c89595..6c6f2e6f 100644 --- a/idls/token_metadata.json +++ b/idls/token_metadata.json @@ -7828,6 +7828,11 @@ "code": 198, "name": "MissingImmutableOwnerExtension", "msg": "Missing immutable owner extension" + }, + { + "code": 199, + "name": "ExpectedUninitializedAccount", + "msg": "Expected account to be uninitialized" } ], "metadata": { diff --git a/programs/token-metadata/program/Cargo.toml b/programs/token-metadata/program/Cargo.toml index 555c68e2..b901c932 100644 --- a/programs/token-metadata/program/Cargo.toml +++ b/programs/token-metadata/program/Cargo.toml @@ -1,23 +1,24 @@ [package] -name = "token_metadata" -version = "1.14.0" -description = "Metaplex Metadata" authors = ["Metaplex Developers "] -repository = "https://github.com/metaplex-foundation/metaplex-program-library" -license-file = "../../../LICENSE" +description = "Metaplex Metadata" edition = "2021" +license-file = "../../../LICENSE" +name = "token_metadata" readme = "README.md" +repository = "https://github.com/metaplex-foundation/metaplex-program-library" +version = "1.14.0" [features] no-entrypoint = [] -test-bpf = [] +padded = [] serde-feature = ["serde", "serde_with"] +test-bpf = [] [dependencies] arrayref = "0.3.6" borsh = "0.9.3" mpl-token-auth-rules = { version = "=1.4.3-beta.1", features = [ - "no-entrypoint", + "no-entrypoint", ] } mpl-token-metadata-context-derive = { version = "0.3.0", path = "../macro" } mpl-utils = { version = "0.3.4", features = ["spl-token"] } @@ -27,17 +28,17 @@ serde = { version = "1.0.149", optional = true } serde_with = { version = "1.14.0", optional = true } shank = { version = "0.3.0" } solana-program = ">= 1.14.13, < 1.17" -spl-token-2022 = "0.8.0" spl-associated-token-account = { version = ">= 1.1.3, < 3.0", features = [ - "no-entrypoint", + "no-entrypoint", ] } +spl-token-2022 = "0.8.0" thiserror = "1.0" [dev-dependencies] async-trait = "0.1.64" rmp-serde = "1.1.1" rooster = { git = "https://github.com/metaplex-foundation/rooster", features = [ - "no-entrypoint", + "no-entrypoint", ] } serde = { version = "1.0.147", features = ["derive"] } solana-program-test = ">= 1.14.13, < 1.17" diff --git a/programs/token-metadata/program/src/assertions/edition.rs b/programs/token-metadata/program/src/assertions/edition.rs index be4c7b48..750e35fa 100644 --- a/programs/token-metadata/program/src/assertions/edition.rs +++ b/programs/token-metadata/program/src/assertions/edition.rs @@ -7,7 +7,8 @@ use crate::{ error::MetadataError, pda::find_master_edition_account, state::{ - Key, TokenStandard, EDITION, PREFIX, TOKEN_STANDARD_INDEX, TOKEN_STANDARD_INDEX_EDITION, + Key, TokenStandard, EDITION, EDITION_TOKEN_STANDARD_OFFSET, + MASTER_EDITION_TOKEN_STANDARD_OFFSET, PREFIX, }, utils::unpack, }; @@ -29,13 +30,11 @@ pub fn assert_edition_is_not_programmable(edition_info: &AccountInfo) -> Program let edition_data = edition_info.data.borrow(); // Check if it's a master edition of a pNFT - if (edition_data.len() > TOKEN_STANDARD_INDEX - && edition_data[0] == Key::MasterEditionV2 as u8 + if (edition_data[0] == Key::MasterEditionV2 as u8 // Check if it's an edition of a pNFT - && (edition_data[TOKEN_STANDARD_INDEX] == TokenStandard::ProgrammableNonFungible as u8)) - || (edition_data.len() > TOKEN_STANDARD_INDEX_EDITION - && edition_data[0] == Key::EditionV1 as u8 - && edition_data[TOKEN_STANDARD_INDEX_EDITION] + && (edition_data[edition_data.len() - MASTER_EDITION_TOKEN_STANDARD_OFFSET] == TokenStandard::ProgrammableNonFungible as u8)) + || (edition_data[0] == Key::EditionV1 as u8 + && edition_data[edition_data.len() - EDITION_TOKEN_STANDARD_OFFSET] == TokenStandard::ProgrammableNonFungible as u8) { return Err(MetadataError::InvalidTokenStandard.into()); diff --git a/programs/token-metadata/program/src/error.rs b/programs/token-metadata/program/src/error.rs index 040b17b6..64fbc8d7 100644 --- a/programs/token-metadata/program/src/error.rs +++ b/programs/token-metadata/program/src/error.rs @@ -783,6 +783,10 @@ pub enum MetadataError { /// 198 #[error("Missing immutable owner extension")] MissingImmutableOwnerExtension, + + /// 199 + #[error("Expected account to be uninitialized")] + ExpectedUninitializedAccount, } impl PrintProgramError for MetadataError { diff --git a/programs/token-metadata/program/src/processor/fee/mod.rs b/programs/token-metadata/program/src/processor/fee/mod.rs index 6ccabbad..f91b2488 100644 --- a/programs/token-metadata/program/src/processor/fee/mod.rs +++ b/programs/token-metadata/program/src/processor/fee/mod.rs @@ -3,10 +3,7 @@ use num_traits::FromPrimitive; use solana_program::{account_info::next_account_info, rent::Rent, system_program, sysvar::Sysvar}; use super::*; -use crate::{ - state::{fee::FEE_AUTHORITY, MAX_METADATA_LEN}, - utils::fee::clear_fee_flag, -}; +use crate::{state::fee::FEE_AUTHORITY, utils::fee::clear_fee_flag}; pub(crate) fn process_collect_fees(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -44,7 +41,8 @@ fn collect_fee_from_account(account_info: &AccountInfo, dest_info: &AccountInfo) }; let rent = Rent::get()?; - let metadata_rent = rent.minimum_balance(MAX_METADATA_LEN); + let data_len = account_info.data_len(); + let metadata_rent = rent.minimum_balance(data_len); let (fee_amount, rent_amount) = match account_key { Key::Uninitialized => { diff --git a/programs/token-metadata/program/src/processor/metadata/create.rs b/programs/token-metadata/program/src/processor/metadata/create.rs index f5f4c093..f17a7c7a 100644 --- a/programs/token-metadata/program/src/processor/metadata/create.rs +++ b/programs/token-metadata/program/src/processor/metadata/create.rs @@ -4,8 +4,8 @@ use crate::{ error::MetadataError, instruction::{Context, Create, CreateArgs}, state::{ - Metadata, ProgrammableConfig, TokenMetadataAccount, TokenStandard, MAX_MASTER_EDITION_LEN, - TOKEN_STANDARD_INDEX, + Metadata, ProgrammableConfig, TokenMetadataAccount, TokenStandard, + MASTER_EDITION_TOKEN_STANDARD_OFFSET, MAX_MASTER_EDITION_LEN, }, utils::{ create_master_edition, create_mint, @@ -162,7 +162,9 @@ fn create_v1(program_id: &Pubkey, ctx: Context, args: CreateArgs) -> Pro return Err(MetadataError::InvalidMasterEditionAccountLength.into()); } - data[TOKEN_STANDARD_INDEX] = TokenStandard::ProgrammableNonFungible as u8; + let data_len = data.len(); + data[data_len - MASTER_EDITION_TOKEN_STANDARD_OFFSET] = + TokenStandard::ProgrammableNonFungible as u8; } } else { return Err(MetadataError::MissingMasterEditionAccount.into()); diff --git a/programs/token-metadata/program/src/processor/metadata/print.rs b/programs/token-metadata/program/src/processor/metadata/print.rs index 29cd0e86..9c20f4f6 100644 --- a/programs/token-metadata/program/src/processor/metadata/print.rs +++ b/programs/token-metadata/program/src/processor/metadata/print.rs @@ -10,8 +10,8 @@ use crate::{ instruction::{Context, Print, PrintArgs}, pda::find_token_record_account, state::{ - Metadata, TokenMetadataAccount, TokenStandard, MAX_EDITION_LEN, - TOKEN_STANDARD_INDEX_EDITION, + Metadata, TokenMetadataAccount, TokenStandard, EDITION_TOKEN_STANDARD_OFFSET, + MAX_EDITION_LEN, }, utils::{ assert_owned_by, create_mint, create_token_record_account, @@ -298,6 +298,7 @@ fn print_logic<'a>( None, )?; + let data_len = edition_account_info.data_len(); // for pNFTs, we store the token standard value at the end of the // master edition account let mut data = edition_account_info.data.borrow_mut(); @@ -306,7 +307,8 @@ fn print_logic<'a>( return Err(MetadataError::InvalidMasterEditionAccountLength.into()); } - data[TOKEN_STANDARD_INDEX_EDITION] = TokenStandard::ProgrammableNonFungible as u8; + data[data_len - EDITION_TOKEN_STANDARD_OFFSET] = + TokenStandard::ProgrammableNonFungible as u8; } // Set fee flag after metadata account is created. diff --git a/programs/token-metadata/program/src/processor/metadata/set_token_standard.rs b/programs/token-metadata/program/src/processor/metadata/set_token_standard.rs index 19353c4f..21077975 100644 --- a/programs/token-metadata/program/src/processor/metadata/set_token_standard.rs +++ b/programs/token-metadata/program/src/processor/metadata/set_token_standard.rs @@ -10,7 +10,7 @@ use crate::{ }, error::MetadataError, state::{Metadata, TokenMetadataAccount, EDITION, PREFIX}, - utils::{check_token_standard, clean_write_metadata}, + utils::{check_token_standard, metadata::clean_write_metadata}, }; pub fn process_set_token_standard(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { diff --git a/programs/token-metadata/program/src/processor/metadata/update_metadata_account_v2.rs b/programs/token-metadata/program/src/processor/metadata/update_metadata_account_v2.rs index ce36c76b..e287a2f0 100644 --- a/programs/token-metadata/program/src/processor/metadata/update_metadata_account_v2.rs +++ b/programs/token-metadata/program/src/processor/metadata/update_metadata_account_v2.rs @@ -10,7 +10,7 @@ use crate::{ error::MetadataError, processor::all_account_infos, state::{DataV2, Metadata, TokenMetadataAccount}, - utils::{clean_write_metadata, puff_out_data_fields}, + utils::{metadata::clean_write_metadata, puff_out_data_fields}, }; // Update existing account instruction diff --git a/programs/token-metadata/program/src/processor/verification/collection.rs b/programs/token-metadata/program/src/processor/verification/collection.rs index d819c214..4ff66128 100644 --- a/programs/token-metadata/program/src/processor/verification/collection.rs +++ b/programs/token-metadata/program/src/processor/verification/collection.rs @@ -9,7 +9,7 @@ use crate::{ error::MetadataError, instruction::{Context, MetadataDelegateRole, Unverify, Verify}, state::{AuthorityRequest, AuthorityType, Metadata, TokenMetadataAccount}, - utils::{clean_write_metadata, decrement_collection_size, increment_collection_size}, + utils::{decrement_collection_size, increment_collection_size, metadata::clean_write_metadata}, }; pub(crate) fn verify_collection_v1(program_id: &Pubkey, ctx: Context) -> ProgramResult { diff --git a/programs/token-metadata/program/src/processor/verification/creator.rs b/programs/token-metadata/program/src/processor/verification/creator.rs index 2e11bd71..f97c1d73 100644 --- a/programs/token-metadata/program/src/processor/verification/creator.rs +++ b/programs/token-metadata/program/src/processor/verification/creator.rs @@ -6,7 +6,7 @@ use crate::{ error::MetadataError, instruction::{Context, Unverify, Verify}, state::{Creator, Metadata, TokenMetadataAccount}, - utils::clean_write_metadata, + utils::metadata::clean_write_metadata, }; pub(crate) fn verify_creator_v1(program_id: &Pubkey, ctx: Context) -> ProgramResult { diff --git a/programs/token-metadata/program/src/state/edition.rs b/programs/token-metadata/program/src/state/edition.rs index 12956acb..a0f94fee 100644 --- a/programs/token-metadata/program/src/state/edition.rs +++ b/programs/token-metadata/program/src/state/edition.rs @@ -1,11 +1,11 @@ use super::*; -pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200; +pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 1; // The last byte of the account contains the token standard value for // pNFT assets. This is used to restrict legacy operations on the master // edition account. -pub const TOKEN_STANDARD_INDEX_EDITION: usize = MAX_EDITION_LEN - 1; +pub const EDITION_TOKEN_STANDARD_OFFSET: usize = 1; #[repr(C)] #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] @@ -41,7 +41,7 @@ impl TokenMetadataAccount for Edition { } fn size() -> usize { - MAX_EDITION_LEN + 0 } } diff --git a/programs/token-metadata/program/src/state/master_edition.rs b/programs/token-metadata/program/src/state/master_edition.rs index 71c1afe2..f4098ce1 100644 --- a/programs/token-metadata/program/src/state/master_edition.rs +++ b/programs/token-metadata/program/src/state/master_edition.rs @@ -1,18 +1,21 @@ use super::*; -// Large buffer because the older master editions have two pubkeys in them, -// need to keep two versions same size because the conversion process actually -// changes the same account by rewriting it. -pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264; - // The last byte of the account containts the token standard value for // pNFT assets. This is used to restrict legacy operations on the master // edition account. -pub const TOKEN_STANDARD_INDEX: usize = MAX_MASTER_EDITION_LEN - 1; +pub const MASTER_EDITION_TOKEN_STANDARD_OFFSET: usize = 1; // The second to last byte of the account contains the fee flag, indicating // if the account has fees available for retrieval. -pub const MASTER_EDITION_FEE_FLAG_INDEX: usize = MAX_MASTER_EDITION_LEN - 2; +pub const MASTER_EDITION_FEE_FLAG_OFFSET: usize = 2; + +// Size of a master edition v2 account. +// +// key: 1 +// supply: 8 +// option max_supply: 1 + 8 +// flags: 3 +pub const MAX_MASTER_EDITION_LEN: usize = 1 + 8 + 9 + 2; pub trait MasterEdition { fn key(&self) -> Key; @@ -68,7 +71,7 @@ impl TokenMetadataAccount for MasterEditionV2 { } fn size() -> usize { - MAX_MASTER_EDITION_LEN + 0 } } @@ -90,7 +93,11 @@ impl MasterEdition for MasterEditionV2 { } fn save(&self, account: &AccountInfo) -> ProgramResult { - let mut storage = &mut account.data.borrow_mut()[..MASTER_EDITION_FEE_FLAG_INDEX]; + let end = account + .data_len() + .checked_sub(MASTER_EDITION_FEE_FLAG_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?; + let mut storage = &mut account.data.borrow_mut()[..end]; borsh::to_writer(&mut storage, self)?; Ok(()) } @@ -128,7 +135,7 @@ impl TokenMetadataAccount for MasterEditionV1 { } fn size() -> usize { - MAX_MASTER_EDITION_LEN + 0 } } @@ -150,7 +157,11 @@ impl MasterEdition for MasterEditionV1 { } fn save(&self, account: &AccountInfo) -> ProgramResult { - let mut storage = &mut account.data.borrow_mut()[..MASTER_EDITION_FEE_FLAG_INDEX]; + let end = account + .data_len() + .checked_sub(MASTER_EDITION_FEE_FLAG_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?; + let mut storage = &mut account.data.borrow_mut()[..end]; borsh::to_writer(&mut storage, self)?; Ok(()) } diff --git a/programs/token-metadata/program/src/state/metadata.rs b/programs/token-metadata/program/src/state/metadata.rs index 3aeee244..2c5b11bb 100644 --- a/programs/token-metadata/program/src/state/metadata.rs +++ b/programs/token-metadata/program/src/state/metadata.rs @@ -7,7 +7,10 @@ use crate::{ uses::assert_valid_use, }, instruction::{CollectionDetailsToggle, CollectionToggle, RuleSetToggle, UpdateArgs}, - utils::{clean_write_metadata, puff_out_data_fields}, + utils::{ + metadata::{clean_write_metadata, meta_deser_unchecked}, + puff_out_data_fields, + }, }; pub const MAX_NAME_LENGTH: usize = 32; @@ -27,8 +30,8 @@ pub const MAX_METADATA_LEN: usize = 1 // key + 34 // collection + 18 // uses + 10 // collection details -+ 33 // programmable config -+ 75; // Padding ++ 35 // programmable config ++ 1; // Fee flag pub const MAX_DATA_SIZE: usize = 4 + MAX_NAME_LENGTH @@ -43,7 +46,7 @@ pub const MAX_DATA_SIZE: usize = 4 // The last byte of the account contains the fee flag, indicating // if the account has fees available for retrieval. -pub const METADATA_FEE_FLAG_INDEX: usize = MAX_METADATA_LEN - 1; +pub const METADATA_FEE_FLAG_OFFSET: usize = 1; #[macro_export] macro_rules! metadata_seeds { @@ -212,6 +215,7 @@ impl Metadata { } // Update authority by Authority Item Delegate is deprecated. + #[allow(deprecated)] if let UpdateArgs::AsAuthorityItemDelegateV2 { new_update_authority: Some(_authority), .. @@ -339,7 +343,7 @@ impl TokenMetadataAccount for Metadata { } fn size() -> usize { - MAX_METADATA_LEN + 0 } } @@ -405,17 +409,12 @@ mod tests { error::MetadataError, state::{ CollectionAuthorityRecord, Edition, EditionMarker, Key, MasterEditionV2, Metadata, - TokenMetadataAccount, UseAuthorityRecord, MAX_METADATA_LEN, + TokenMetadataAccount, UseAuthorityRecord, }, utils::metadata::tests::{expected_pesky_metadata, pesky_data}, ID, }; - fn pad_metadata_length(metadata: &mut Vec) { - let padding_length = MAX_METADATA_LEN - metadata.len(); - metadata.extend(vec![0; padding_length]); - } - #[test] fn successfully_deserialize_corrupted_metadata() { // This should be able to deserialize the corrupted metadata account successfully due to the custom BorshDeserilization @@ -434,7 +433,6 @@ mod tests { let mut buf = Vec::new(); borsh::to_writer(&mut buf, &expected_metadata).unwrap(); - pad_metadata_length(&mut buf); let pubkey = Keypair::new().pubkey(); let owner = &ID; @@ -463,7 +461,6 @@ mod tests { let mut buf = Vec::new(); borsh::to_writer(&mut buf, &expected_metadata).unwrap(); - pad_metadata_length(&mut buf); let pubkey = Keypair::new().pubkey(); let invalid_owner = Keypair::new().pubkey(); @@ -488,7 +485,7 @@ mod tests { } #[test] - fn fail_to_deserialize_metadata_with_wrong_size() { + fn successfully_deserialize_metadata_with_different_size() { let expected_metadata = expected_pesky_metadata(); let mut buf = Vec::new(); @@ -511,10 +508,9 @@ mod tests { 1_000_000_000, ); - // `from_account_info` should not succeed because this account is not owned - // by `token-metadata` program. - let error = Metadata::from_account_info(&account_info).unwrap_err(); - assert_eq!(error, MetadataError::DataTypeMismatch.into()); + let md = Metadata::from_account_info(&account_info).unwrap(); + assert_eq!(md.key, Key::MetadataV1); + assert_eq!(md, expected_metadata); } #[test] diff --git a/programs/token-metadata/program/src/state/mod.rs b/programs/token-metadata/program/src/state/mod.rs index 14566da8..6b285b9b 100644 --- a/programs/token-metadata/program/src/state/mod.rs +++ b/programs/token-metadata/program/src/state/mod.rs @@ -52,12 +52,7 @@ use { // Re-export constants to maintain compatibility. pub use crate::pda::{BURN, COLLECTION_AUTHORITY, EDITION, PREFIX, USER}; -use crate::{ - assertions::assert_owned_by, - error::MetadataError, - utils::{meta_deser_unchecked, try_from_slice_checked}, - ID, -}; +use crate::{assertions::assert_owned_by, error::MetadataError, utils::try_from_slice_checked, ID}; /// Index of the discriminator on the account data. pub const DISCRIMINATOR_INDEX: usize = 0; @@ -74,6 +69,20 @@ pub enum TokenStandard { ProgrammableNonFungibleEdition, // NonFungible with programmable configuration } +impl From for TokenStandard { + fn from(value: u8) -> Self { + match value { + 0 => TokenStandard::NonFungible, + 1 => TokenStandard::FungibleAsset, + 2 => TokenStandard::Fungible, + 3 => TokenStandard::NonFungibleEdition, + 4 => TokenStandard::ProgrammableNonFungible, + 5 => TokenStandard::ProgrammableNonFungibleEdition, + _ => panic!("Invalid token standard"), + } + } +} + pub trait TokenMetadataAccount: BorshDeserialize { fn key() -> Key; @@ -95,10 +104,12 @@ pub trait TokenMetadataAccount: BorshDeserialize { } fn pad_length(buf: &mut Vec) -> Result<(), MetadataError> { - let padding_length = Self::size() - .checked_sub(buf.len()) - .ok_or(MetadataError::NumericalOverflowError)?; - buf.extend(vec![0; padding_length]); + if Self::size() != 0 { + let padding_length = Self::size() + .checked_sub(buf.len()) + .ok_or(MetadataError::NumericalOverflowError)?; + buf.extend(vec![0; padding_length]); + } Ok(()) } diff --git a/programs/token-metadata/program/src/state/programmable.rs b/programs/token-metadata/program/src/state/programmable.rs index 6508135c..b1fd57b5 100644 --- a/programs/token-metadata/program/src/state/programmable.rs +++ b/programs/token-metadata/program/src/state/programmable.rs @@ -88,6 +88,7 @@ pub struct TokenRecord { impl Default for TokenRecord { fn default() -> Self { + #[allow(deprecated)] Self { key: Key::TokenRecord, bump: 255, @@ -125,6 +126,7 @@ impl TokenRecord { } /// Resets the token state by clearing any state stored. + #[allow(deprecated)] pub fn reset(&mut self) { self.state = TokenState::Unlocked; self.rule_set_revision = None; @@ -167,6 +169,7 @@ impl Resizable for TokenRecord { None }; + #[allow(deprecated)] Ok(TokenRecord { key, bump, diff --git a/programs/token-metadata/program/src/utils/collection.rs b/programs/token-metadata/program/src/utils/collection.rs index be732777..4bac7b25 100644 --- a/programs/token-metadata/program/src/utils/collection.rs +++ b/programs/token-metadata/program/src/utils/collection.rs @@ -1,3 +1,4 @@ +use metadata::clean_write_metadata; use solana_program::msg; use super::*; diff --git a/programs/token-metadata/program/src/utils/fee.rs b/programs/token-metadata/program/src/utils/fee.rs index 2e823dfa..79cd662f 100644 --- a/programs/token-metadata/program/src/utils/fee.rs +++ b/programs/token-metadata/program/src/utils/fee.rs @@ -3,7 +3,10 @@ use solana_program::{ sysvar::Sysvar, }; -use crate::state::{fee::CREATE_FEE, Metadata, TokenMetadataAccount, METADATA_FEE_FLAG_INDEX}; +use crate::{ + error::MetadataError, + state::{fee::CREATE_FEE, MAX_METADATA_LEN, METADATA_FEE_FLAG_OFFSET}, +}; #[repr(C)] #[derive(Debug, Clone, Copy)] @@ -16,7 +19,14 @@ pub(crate) fn levy(args: LevyArgs) -> ProgramResult { // Fund metadata account with rent + Metaplex fee. let rent = Rent::get()?; - let fee = CREATE_FEE + rent.minimum_balance(Metadata::size()); + // Normally we would use the account data length to calculate the rent, but + // but levy is always called before the account is created, so it will be + // zero at this point. But we double check anyway. + let account_data_len = args.token_metadata_pda_info.data_len(); + if account_data_len > 0 { + return Err(MetadataError::ExpectedUninitializedAccount.into()); + } + let fee = CREATE_FEE + rent.minimum_balance(MAX_METADATA_LEN); invoke( &solana_program::system_instruction::transfer( @@ -34,17 +44,25 @@ pub(crate) fn levy(args: LevyArgs) -> ProgramResult { } pub(crate) fn set_fee_flag(pda_account_info: &AccountInfo) -> ProgramResult { + let last_byte = pda_account_info + .data_len() + .checked_sub(METADATA_FEE_FLAG_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?; let mut data = pda_account_info.try_borrow_mut_data()?; - data[METADATA_FEE_FLAG_INDEX] = 1; + data[last_byte] = 1; Ok(()) } pub(crate) fn clear_fee_flag(pda_account_info: &AccountInfo) -> ProgramResult { + let last_byte = pda_account_info + .data_len() + .checked_sub(METADATA_FEE_FLAG_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?; let mut data = pda_account_info.try_borrow_mut_data()?; // Clear the flag if the index exists. - if let Some(flag) = data.get_mut(METADATA_FEE_FLAG_INDEX) { + if let Some(flag) = data.get_mut(last_byte) { *flag = 0; } diff --git a/programs/token-metadata/program/src/utils/master_edition.rs b/programs/token-metadata/program/src/utils/master_edition.rs index da73b9d9..7563ed68 100644 --- a/programs/token-metadata/program/src/utils/master_edition.rs +++ b/programs/token-metadata/program/src/utils/master_edition.rs @@ -1,5 +1,6 @@ use arrayref::{array_mut_ref, array_ref, mut_array_refs}; use borsh::BorshSerialize; +use metadata::{process_create_metadata_accounts_logic, CreateMetadataAccountsLogicArgs}; use mpl_utils::{ assert_signer, create_or_allocate_account_raw, token::{get_mint_authority, get_mint_supply}, @@ -22,8 +23,10 @@ use crate::{ pda::MARKER, state::{ get_reservation_list, DataV2, EditionMarker, EditionMarkerV2, Key, MasterEdition, Metadata, - TokenMetadataAccount, Uses, EDITION, EDITION_MARKER_BIT_SIZE, MAX_EDITION_LEN, - MAX_EDITION_MARKER_SIZE, MAX_MASTER_EDITION_LEN, PREFIX, + TokenMetadataAccount, Uses, EDITION, EDITION_MARKER_BIT_SIZE, + EDITION_TOKEN_STANDARD_OFFSET, MASTER_EDITION_FEE_FLAG_OFFSET, + MASTER_EDITION_TOKEN_STANDARD_OFFSET, MAX_EDITION_LEN, MAX_EDITION_MARKER_SIZE, + MAX_MASTER_EDITION_LEN, PREFIX, }, }; @@ -417,7 +420,7 @@ pub fn calculate_supply_change<'a>( let edition_data = &mut master_edition_account_info.data.borrow_mut(); let output = array_mut_ref![edition_data, 0, MAX_MASTER_EDITION_LEN]; - let (_key, supply, _the_rest) = mut_array_refs![output, 1, 8, 273]; + let (_key, supply, _the_rest) = mut_array_refs![output, 1, 8, 11]; *supply = new_supply.to_le_bytes(); Ok(()) @@ -550,7 +553,7 @@ pub fn mint_limited_edition<'a>( let edition_data = &mut new_edition_account_info.data.borrow_mut(); let output = array_mut_ref![edition_data, 0, MAX_EDITION_LEN]; - let (key, parent, edition, _padding) = mut_array_refs![output, 1, 32, 8, 200]; + let (key, parent, edition, _padding) = mut_array_refs![output, 1, 32, 8, 1]; *key = [Key::EditionV1 as u8]; parent.copy_from_slice(master_edition_account_info.key.as_ref()); diff --git a/programs/token-metadata/program/src/utils/metadata.rs b/programs/token-metadata/program/src/utils/metadata.rs index 15de9f8e..a73daff9 100644 --- a/programs/token-metadata/program/src/utils/metadata.rs +++ b/programs/token-metadata/program/src/utils/metadata.rs @@ -1,4 +1,4 @@ -use borsh::{maybestd::io::Error as BorshError, BorshDeserialize}; +use borsh::{maybestd::io::Error as BorshError, BorshDeserialize, BorshSerialize}; use mpl_utils::{ create_or_allocate_account_raw, token::{get_mint_authority, SPL_TOKEN_PROGRAM_IDS}, @@ -16,7 +16,7 @@ use crate::{ }, state::{ Collection, CollectionDetails, Data, DataV2, Key, Metadata, ProgrammableConfig, - TokenStandard, Uses, EDITION, MAX_METADATA_LEN, METADATA_FEE_FLAG_INDEX, PREFIX, + TokenStandard, Uses, EDITION, MAX_METADATA_LEN, METADATA_FEE_FLAG_OFFSET, PREFIX, }, }; @@ -274,13 +274,16 @@ pub fn clean_write_metadata( metadata: &mut Metadata, metadata_account_info: &AccountInfo, ) -> ProgramResult { + let end = metadata_account_info + .data_len() + .checked_sub(METADATA_FEE_FLAG_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?; // Clear all data to ensure it is serialized cleanly with no trailing data due to creators array resizing. let mut metadata_account_info_data = metadata_account_info.try_borrow_mut_data()?; // Don't overwrite fee flag. - metadata_account_info_data[0..METADATA_FEE_FLAG_INDEX].fill(0); + metadata_account_info_data[0..end].fill(0); metadata.save(&mut metadata_account_info_data)?; - Ok(()) } diff --git a/programs/token-metadata/program/src/utils/mod.rs b/programs/token-metadata/program/src/utils/mod.rs index 755939fb..bc58c7de 100644 --- a/programs/token-metadata/program/src/utils/mod.rs +++ b/programs/token-metadata/program/src/utils/mod.rs @@ -9,7 +9,10 @@ pub(crate) mod token; pub use collection::*; pub use compression::*; pub use master_edition::*; -pub use metadata::*; +pub use metadata::{ + clean_write_metadata, meta_deser_unchecked, process_create_metadata_accounts_logic, + CreateMetadataAccountsLogicArgs, +}; pub use mpl_utils::{ assert_signer, close_account_raw, create_or_allocate_account_raw, resize_or_reallocate_account_raw, @@ -221,7 +224,7 @@ pub(crate) fn close_program_account<'a>( let rent_lamports = match key { // Metadata accounts could have fees stored, so we only want to withdraw // the actual rent lamport amount. - Key::MetadataV1 => rent.minimum_balance(Metadata::size()), + Key::MetadataV1 => rent.minimum_balance(account_info.data_len()), // Other accounts the rent is just the current lamport balance. _ => account_info.lamports(), }; @@ -239,7 +242,7 @@ pub(crate) fn close_program_account<'a>( **account_info.lamports.borrow_mut() = remaining_lamports; // If the account does not have fees on it, we realloc the data length to zero - // and assign ownerhsip to the system program. + // and assign ownership to the system program. if remaining_lamports == 0 { account_info.realloc(0, false)?; account_info.assign(&solana_program::system_program::ID); @@ -255,16 +258,13 @@ pub(crate) fn close_program_account<'a>( #[cfg(test)] mod tests { - pub use solana_program::pubkey::Pubkey; + use solana_program::pubkey::Pubkey; - use crate::{ - state::MAX_METADATA_LEN, - utils::{ - metadata::tests::{expected_pesky_metadata, pesky_data}, - try_from_slice_checked, - }, + use crate::utils::{ + metadata::tests::{expected_pesky_metadata, pesky_data}, + try_from_slice_checked, }; - pub use crate::{ + use crate::{ state::{Data, Key, Metadata}, utils::{puff_out_data_fields, puffed_out_string}, }; @@ -329,13 +329,13 @@ mod tests { #[test] fn deserialize_corrupted_metadata_ok() { - // This should be able to deserialize the corrupted metadata account successfully due to the custom BorshDeserilization + // This should be able to deserialize the corrupted metadata account successfully due to the custom BorshDeserialization // implementation for the Metadata struct. let expected_metadata = expected_pesky_metadata(); let corrupted_data = pesky_data(); let metadata: Metadata = - try_from_slice_checked(corrupted_data, Key::MetadataV1, MAX_METADATA_LEN).unwrap(); + try_from_slice_checked(corrupted_data, Key::MetadataV1, 0).unwrap(); assert_eq!(metadata, expected_metadata); } diff --git a/programs/token-metadata/program/tests/fees.rs b/programs/token-metadata/program/tests/fees.rs index f0753434..ac0a89a0 100644 --- a/programs/token-metadata/program/tests/fees.rs +++ b/programs/token-metadata/program/tests/fees.rs @@ -13,7 +13,7 @@ mod fees { }; use token_metadata::{ instruction::{collect_fees, BurnArgs, UpdateArgs}, - state::{CREATE_FEE, FEE_FLAG_CLEARED, METADATA_FEE_FLAG_INDEX}, + state::{CREATE_FEE, FEE_FLAG_CLEARED, METADATA_FEE_FLAG_OFFSET}, }; use super::*; @@ -144,7 +144,8 @@ mod fees { for account in fee_accounts { let account = get_account(&mut context, &account).await; - assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_CLEARED); + let last_byte = account.data.len() - METADATA_FEE_FLAG_OFFSET; + assert_eq!(account.data[last_byte], FEE_FLAG_CLEARED); } } diff --git a/programs/token-metadata/program/tests/print.rs b/programs/token-metadata/program/tests/print.rs index b50aeeb4..0a2b5faf 100644 --- a/programs/token-metadata/program/tests/print.rs +++ b/programs/token-metadata/program/tests/print.rs @@ -18,10 +18,7 @@ mod print { use borsh::BorshDeserialize; use solana_program::pubkey::Pubkey; use solana_sdk::{signature::Keypair, signer::Signer}; - use token_metadata::{ - instruction::{builders::UpdateBuilder, RuleSetToggle, UpdateArgs}, - state::{PrintSupply, ProgrammableConfig, TokenStandard}, - }; + use token_metadata::state::{PrintSupply, ProgrammableConfig, TokenStandard}; use super::*; diff --git a/programs/token-metadata/program/tests/seralization.rs b/programs/token-metadata/program/tests/seralization.rs index a207aa07..e69be1b9 100644 --- a/programs/token-metadata/program/tests/seralization.rs +++ b/programs/token-metadata/program/tests/seralization.rs @@ -3,7 +3,7 @@ pub mod utils; use solana_program_test::*; use token_metadata::{ - state::{Key, MasterEditionV2 as ProgramME, MAX_MASTER_EDITION_LEN}, + state::{Key, MasterEditionV2 as ProgramME}, utils::try_from_slice_checked, }; use utils::*; @@ -36,11 +36,7 @@ mod serialization { let (_nft, master) = setup(&mut context).await; let otherbytes = master.clone(); let _me: ProgramME = BorshDeserialize::deserialize(&mut &master[..]).unwrap(); - let _me2: ProgramME = - try_from_slice_checked(&otherbytes, Key::MasterEditionV2, MAX_MASTER_EDITION_LEN) - .unwrap(); - let _me2: ProgramME = - try_from_slice_checked(&otherbytes, Key::MasterEditionV2, MAX_MASTER_EDITION_LEN) - .unwrap(); + let _me2: ProgramME = try_from_slice_checked(&otherbytes, Key::MasterEditionV2, 0).unwrap(); + let _me2: ProgramME = try_from_slice_checked(&otherbytes, Key::MasterEditionV2, 0).unwrap(); } } diff --git a/programs/token-metadata/program/tests/update.rs b/programs/token-metadata/program/tests/update.rs index 7b68c7ba..df78da7b 100644 --- a/programs/token-metadata/program/tests/update.rs +++ b/programs/token-metadata/program/tests/update.rs @@ -235,6 +235,7 @@ mod update { // Change a few values that this delegate is allowed to change. let mut args = UpdateArgs::default_as_authority_item_delegate(); + #[allow(deprecated)] match &mut args { UpdateArgs::AsAuthorityItemDelegateV2 { new_update_authority, @@ -4065,12 +4066,12 @@ mod update { async fn success_printed_pnft_rule_set_update(spl_token_program: Pubkey) { let mut program_test = program_test(); program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); - let mut context = &mut program_test.start_with_context().await; + let context = &mut program_test.start_with_context().await; let mut asset = DigitalAsset::new(); asset .create_and_mint_with_supply( - &mut context, + context, TokenStandard::ProgrammableNonFungible, None, None, @@ -4086,7 +4087,7 @@ mod update { EditionMarker::new_from_asset(&asset, &test_master_edition, 1, spl_token_program); test_edition_marker - .create_from_asset(&mut context) + .create_from_asset(context) .await .unwrap(); diff --git a/programs/token-metadata/program/tests/utils/digital_asset.rs b/programs/token-metadata/program/tests/utils/digital_asset.rs index f7a15ee7..a2e1ffdf 100644 --- a/programs/token-metadata/program/tests/utils/digital_asset.rs +++ b/programs/token-metadata/program/tests/utils/digital_asset.rs @@ -31,7 +31,7 @@ use token_metadata::{ state::{ AssetData, Collection, CollectionDetails, Creator, MasterEditionV2, Metadata, PrintSupply, ProgrammableConfig, TokenDelegateRole, TokenMetadataAccount, TokenRecord, TokenStandard, - CREATE_FEE, EDITION, EDITION_MARKER_BIT_SIZE, FEE_FLAG_SET, METADATA_FEE_FLAG_INDEX, + CREATE_FEE, EDITION, EDITION_MARKER_BIT_SIZE, FEE_FLAG_SET, METADATA_FEE_FLAG_OFFSET, PREFIX, }, utils::unpack, @@ -1476,7 +1476,8 @@ impl DigitalAsset { let expected_lamports = rent_exempt + CREATE_FEE; assert_eq!(account.lamports, expected_lamports); - assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); + let last_byte = account.data.len() - METADATA_FEE_FLAG_OFFSET; + assert_eq!(account.data[last_byte], FEE_FLAG_SET); Ok(()) } @@ -1487,7 +1488,8 @@ impl DigitalAsset { ) -> Result<(), BanksClientError> { let account = get_account(context, &self.metadata).await; - assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); + let last_byte = account.data.len() - METADATA_FEE_FLAG_OFFSET; + assert_eq!(account.data[last_byte], FEE_FLAG_SET); Ok(()) } diff --git a/programs/token-metadata/program/tests/utils/edition_marker.rs b/programs/token-metadata/program/tests/utils/edition_marker.rs index ebaf65ff..9b79c8b9 100644 --- a/programs/token-metadata/program/tests/utils/edition_marker.rs +++ b/programs/token-metadata/program/tests/utils/edition_marker.rs @@ -243,7 +243,12 @@ impl EditionMarker { tx, solana_sdk::commitment_config::CommitmentLevel::Confirmed, ) - .await + .await?; + + #[cfg(feature = "padded")] + upsize_edition(context, &self.new_edition_pubkey).await; + + Ok(()) } pub async fn create_from_asset( @@ -333,7 +338,12 @@ impl EditionMarker { tx, solana_sdk::commitment_config::CommitmentLevel::Confirmed, ) - .await + .await?; + + #[cfg(feature = "padded")] + upsize_edition(context, &self.new_edition_pubkey).await; + + Ok(()) } pub async fn create_from_asset_with_invalid_token_program( @@ -393,7 +403,12 @@ impl EditionMarker { tx, solana_sdk::commitment_config::CommitmentLevel::Confirmed, ) - .await + .await?; + + #[cfg(feature = "padded")] + upsize_edition(context, &self.new_edition_pubkey).await; + + Ok(()) } pub async fn create_with_invalid_token_program( @@ -452,7 +467,12 @@ impl EditionMarker { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + upsize_edition(context, &self.new_edition_pubkey).await; + + Ok(()) } pub async fn transfer( diff --git a/programs/token-metadata/program/tests/utils/master_edition_v2.rs b/programs/token-metadata/program/tests/utils/master_edition_v2.rs index 02edde93..5b776867 100644 --- a/programs/token-metadata/program/tests/utils/master_edition_v2.rs +++ b/programs/token-metadata/program/tests/utils/master_edition_v2.rs @@ -112,7 +112,12 @@ impl MasterEditionV2 { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + upsize_master_edition(context, &self.pubkey).await; + + Ok(()) } pub async fn create_v3( @@ -136,7 +141,12 @@ impl MasterEditionV2 { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + upsize_master_edition(context, &self.pubkey).await; + + Ok(()) } pub async fn mint_editions( diff --git a/programs/token-metadata/program/tests/utils/metadata.rs b/programs/token-metadata/program/tests/utils/metadata.rs index fb77e47a..89d5728d 100644 --- a/programs/token-metadata/program/tests/utils/metadata.rs +++ b/programs/token-metadata/program/tests/utils/metadata.rs @@ -7,7 +7,7 @@ use token_metadata::{ state::{ Collection, CollectionDetails, Creator, DataV2, Metadata as TmMetadata, TokenMetadataAccount, TokenStandard, Uses, CREATE_FEE, FEE_FLAG_SET, - METADATA_FEE_FLAG_INDEX, PREFIX, + METADATA_FEE_FLAG_OFFSET, PREFIX, }, ID, }; @@ -159,7 +159,14 @@ impl Metadata { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + { + upsize_metadata(context, &self.pubkey).await; + } + + Ok(()) } #[allow(clippy::too_many_arguments)] @@ -228,7 +235,12 @@ impl Metadata { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + upsize_metadata(context, &self.pubkey).await; + + Ok(()) } pub async fn create_v3_no_freeze_auth( @@ -299,7 +311,12 @@ impl Metadata { context.last_blockhash, ); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + upsize_metadata(context, &self.pubkey).await; + + Ok(()) } pub async fn create_v3_default( @@ -682,7 +699,8 @@ impl Metadata { let expected_lamports = rent_exempt + CREATE_FEE; assert_eq!(account.lamports, expected_lamports); - assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); + let last_byte = account.data.len() - METADATA_FEE_FLAG_OFFSET; + assert_eq!(account.data[last_byte], FEE_FLAG_SET); Ok(()) } @@ -693,7 +711,8 @@ impl Metadata { ) -> Result<(), BanksClientError> { let account = get_account(context, &self.pubkey).await; - assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); + let last_byte = account.data.len() - METADATA_FEE_FLAG_OFFSET; + assert_eq!(account.data[last_byte], FEE_FLAG_SET); Ok(()) } diff --git a/programs/token-metadata/program/tests/utils/mod.rs b/programs/token-metadata/program/tests/utils/mod.rs index f522a618..17f336e1 100644 --- a/programs/token-metadata/program/tests/utils/mod.rs +++ b/programs/token-metadata/program/tests/utils/mod.rs @@ -6,7 +6,6 @@ mod metadata; mod programmable; mod rooster_manager; -pub use assert::*; use async_trait::async_trait; pub use digital_asset::*; pub use edition_marker::*; @@ -16,12 +15,19 @@ pub use programmable::create_default_metaplex_rule_set; pub use rooster_manager::*; use solana_program_test::*; use solana_sdk::{ - account::Account, program_pack::Pack, pubkey::Pubkey, signature::Signer, - signer::keypair::Keypair, system_instruction, transaction::Transaction, + account::{Account, AccountSharedData, ReadableAccount}, + program_pack::Pack, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + transaction::Transaction, }; use spl_token::state::Mint; pub use token_metadata::instruction; -use token_metadata::state::CollectionDetails; +use token_metadata::state::{ + CollectionDetails, MAX_EDITION_LEN, MAX_MASTER_EDITION_LEN, MAX_METADATA_LEN, +}; pub const DEFAULT_COLLECTION_DETAILS: Option = { #[allow(deprecated)] @@ -283,3 +289,111 @@ impl Airdrop for Keypair { context.banks_client.process_transaction(tx).await } } + +pub async fn upsize_metadata(context: &mut ProgramTestContext, address: &Pubkey) { + let account = get_account(context, address).await; + assert_eq!( + account.lamports, 15115600, + "Original lamports must be 15115600" + ); + let mut account_shared = AccountSharedData::from(account); + let original = account_shared.data(); + assert_eq!( + original.len(), + MAX_METADATA_LEN, + "Original metadata size must be MAX_METADATA_LEN bytes" + ); + + let mut extended = vec![0u8; 679]; + extended[..(original.len() - 1)].copy_from_slice(&original[..(original.len() - 1)]); + // Copy the fee flag from the original metadata. + extended[678] = original[606]; + + account_shared.set_data(extended); + // account_shared + // .checked_add_lamports(15616720 - 15115600) + // .expect("Overflow"); + context.set_account(address, &account_shared); + airdrop(context, address, 15616720 - 15115600) + .await + .unwrap(); + let account = get_account(context, address).await; + assert_eq!( + account.lamports, 15616720, + "Extended lamports must be 15616720" + ); + assert_eq!( + account.data.len(), + 679, + "Extended metadata size must be 679 bytes" + ); +} + +pub async fn upsize_master_edition(context: &mut ProgramTestContext, address: &Pubkey) { + let account = get_account(context, address).await; + assert_eq!( + account.lamports, 1030080, + "Original lamports must be 1030080" + ); + let mut account_shared = AccountSharedData::from(account); + let original = account_shared.data(); + assert_eq!( + original.len(), + MAX_MASTER_EDITION_LEN, + "Original metadata size must be MAX_MASTER_EDITION_LEN bytes" + ); + + let mut extended = vec![0u8; 282]; + extended[..(original.len() - 1)].copy_from_slice(&original[..(original.len() - 1)]); + // Copy the fee flag from the original metadata. + extended[281] = original[19]; + extended[280] = original[18]; + + account_shared.set_data(extended); + context.set_account(address, &account_shared); + airdrop(context, address, 2853600 - 1030080).await.unwrap(); + let account = get_account(context, address).await; + assert_eq!( + account.lamports, 2853600, + "Extended lamports must be 2853600" + ); + assert_eq!( + account.data.len(), + 282, + "Extended metadata size must be 282 bytes" + ); +} + +pub async fn upsize_edition(context: &mut ProgramTestContext, address: &Pubkey) { + let account: Account = get_account(context, address).await; + assert_eq!( + account.lamports, 1183200, + "Original lamports must be 1183200" + ); + let mut account_shared = AccountSharedData::from(account); + let original = account_shared.data(); + assert_eq!( + original.len(), + MAX_EDITION_LEN, + "Original metadata size must be MAX_EDITION_LEN bytes" + ); + + let mut extended = vec![0u8; 241]; + extended[..(original.len() - 1)].copy_from_slice(&original[..(original.len() - 1)]); + // Copy the fee flag from the original metadata. + extended[240] = original[41]; + + account_shared.set_data(extended); + context.set_account(address, &account_shared); + airdrop(context, address, 2568240 - 1183200).await.unwrap(); + let account = get_account(context, address).await; + assert_eq!( + account.lamports, 2568240, + "Extended lamports must be 2568240" + ); + assert_eq!( + account.data.len(), + 241, + "Extended metadata size must be 241 bytes" + ); +} From bb4c4d8f561839e9a563cc9335a7a3cd3efa985f Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Thu, 29 Aug 2024 17:36:35 -0400 Subject: [PATCH 2/6] Addressing first round of feedback. --- .../src/generated/errors/mplTokenMetadata.ts | 16 ++++++++++++ .../generated/errors/mpl_token_metadata.rs | 3 +++ idls/token_metadata.json | 5 ++++ programs/token-metadata/program/Cargo.toml | 2 +- .../program/src/assertions/edition.rs | 2 +- programs/token-metadata/program/src/error.rs | 4 +++ .../program/src/processor/metadata/print.rs | 2 +- .../program/tests/utils/digital_asset.rs | 25 +++++++++++++++++-- .../program/tests/utils/edition_marker.rs | 20 ++++++++++++--- .../token-metadata/program/tests/utils/mod.rs | 3 --- 10 files changed, 70 insertions(+), 12 deletions(-) diff --git a/clients/js/src/generated/errors/mplTokenMetadata.ts b/clients/js/src/generated/errors/mplTokenMetadata.ts index d9d45a2f..3ad7ec06 100644 --- a/clients/js/src/generated/errors/mplTokenMetadata.ts +++ b/clients/js/src/generated/errors/mplTokenMetadata.ts @@ -2939,6 +2939,22 @@ nameToErrorMap.set( ExpectedUninitializedAccountError ); +/** InvalidEditionAccountLength: Edition account has an invalid length */ +export class InvalidEditionAccountLengthError extends ProgramError { + override readonly name: string = 'InvalidEditionAccountLength'; + + readonly code: number = 0xc8; // 200 + + constructor(program: Program, cause?: Error) { + super('Edition account has an invalid length', program, cause); + } +} +codeToErrorMap.set(0xc8, InvalidEditionAccountLengthError); +nameToErrorMap.set( + 'InvalidEditionAccountLength', + InvalidEditionAccountLengthError +); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/clients/rust/src/generated/errors/mpl_token_metadata.rs b/clients/rust/src/generated/errors/mpl_token_metadata.rs index 21268700..7d456b00 100644 --- a/clients/rust/src/generated/errors/mpl_token_metadata.rs +++ b/clients/rust/src/generated/errors/mpl_token_metadata.rs @@ -610,6 +610,9 @@ pub enum MplTokenMetadataError { /// 199 (0xC7) - Expected account to be uninitialized #[error("Expected account to be uninitialized")] ExpectedUninitializedAccount, + /// 200 (0xC8) - Edition account has an invalid length + #[error("Edition account has an invalid length")] + InvalidEditionAccountLength, } impl solana_program::program_error::PrintProgramError for MplTokenMetadataError { diff --git a/idls/token_metadata.json b/idls/token_metadata.json index 6c6f2e6f..16a4abc5 100644 --- a/idls/token_metadata.json +++ b/idls/token_metadata.json @@ -7833,6 +7833,11 @@ "code": 199, "name": "ExpectedUninitializedAccount", "msg": "Expected account to be uninitialized" + }, + { + "code": 200, + "name": "InvalidEditionAccountLength", + "msg": "Edition account has an invalid length" } ], "metadata": { diff --git a/programs/token-metadata/program/Cargo.toml b/programs/token-metadata/program/Cargo.toml index b901c932..6ee7821a 100644 --- a/programs/token-metadata/program/Cargo.toml +++ b/programs/token-metadata/program/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license-file = "../../../LICENSE" name = "token_metadata" readme = "README.md" -repository = "https://github.com/metaplex-foundation/metaplex-program-library" +repository = "https://github.com/metaplex-foundation/mpl-token-metadata" version = "1.14.0" [features] diff --git a/programs/token-metadata/program/src/assertions/edition.rs b/programs/token-metadata/program/src/assertions/edition.rs index 750e35fa..55ef086d 100644 --- a/programs/token-metadata/program/src/assertions/edition.rs +++ b/programs/token-metadata/program/src/assertions/edition.rs @@ -31,8 +31,8 @@ pub fn assert_edition_is_not_programmable(edition_info: &AccountInfo) -> Program // Check if it's a master edition of a pNFT if (edition_data[0] == Key::MasterEditionV2 as u8 - // Check if it's an edition of a pNFT && (edition_data[edition_data.len() - MASTER_EDITION_TOKEN_STANDARD_OFFSET] == TokenStandard::ProgrammableNonFungible as u8)) + // Check if it's an edition of a pNFT || (edition_data[0] == Key::EditionV1 as u8 && edition_data[edition_data.len() - EDITION_TOKEN_STANDARD_OFFSET] == TokenStandard::ProgrammableNonFungible as u8) diff --git a/programs/token-metadata/program/src/error.rs b/programs/token-metadata/program/src/error.rs index 64fbc8d7..fd357436 100644 --- a/programs/token-metadata/program/src/error.rs +++ b/programs/token-metadata/program/src/error.rs @@ -787,6 +787,10 @@ pub enum MetadataError { /// 199 #[error("Expected account to be uninitialized")] ExpectedUninitializedAccount, + + /// 200 + #[error("Edition account has an invalid length")] + InvalidEditionAccountLength, } impl PrintProgramError for MetadataError { diff --git a/programs/token-metadata/program/src/processor/metadata/print.rs b/programs/token-metadata/program/src/processor/metadata/print.rs index 9c20f4f6..efb4407e 100644 --- a/programs/token-metadata/program/src/processor/metadata/print.rs +++ b/programs/token-metadata/program/src/processor/metadata/print.rs @@ -304,7 +304,7 @@ fn print_logic<'a>( let mut data = edition_account_info.data.borrow_mut(); if data.len() < MAX_EDITION_LEN { - return Err(MetadataError::InvalidMasterEditionAccountLength.into()); + return Err(MetadataError::InvalidEditionAccountLength.into()); } data[data_len - EDITION_TOKEN_STANDARD_OFFSET] = diff --git a/programs/token-metadata/program/tests/utils/digital_asset.rs b/programs/token-metadata/program/tests/utils/digital_asset.rs index a2e1ffdf..fcee77a6 100644 --- a/programs/token-metadata/program/tests/utils/digital_asset.rs +++ b/programs/token-metadata/program/tests/utils/digital_asset.rs @@ -38,7 +38,12 @@ use token_metadata::{ ID, }; -use super::{airdrop, create_mint, create_token_account, get_account, mint_tokens}; +use crate::upsize_edition; + +use super::{ + airdrop, create_mint, create_token_account, get_account, mint_tokens, upsize_master_edition, + upsize_metadata, +}; pub const DEFAULT_NAME: &str = "Digital Asset"; pub const DEFAULT_SYMBOL: &str = "DA"; @@ -359,7 +364,17 @@ impl DigitalAsset { self.edition = edition; self.token_standard = Some(token_standard); - context.banks_client.process_transaction(tx).await + context.banks_client.process_transaction(tx).await?; + + #[cfg(feature = "padded")] + { + upsize_metadata(context, &self.metadata).await; + if let Some(edition) = self.edition { + upsize_master_edition(context, &edition).await; + } + } + + Ok(()) } pub async fn mint( @@ -869,6 +884,12 @@ impl DigitalAsset { .await .unwrap(); + #[cfg(feature = "padded")] + { + upsize_metadata(context, &print_metadata).await; + upsize_edition(context, &print_edition).await; + } + Ok(DigitalAsset { mint: print_mint, token: Some(print_token.pubkey()), diff --git a/programs/token-metadata/program/tests/utils/edition_marker.rs b/programs/token-metadata/program/tests/utils/edition_marker.rs index 9b79c8b9..77959f70 100644 --- a/programs/token-metadata/program/tests/utils/edition_marker.rs +++ b/programs/token-metadata/program/tests/utils/edition_marker.rs @@ -246,7 +246,10 @@ impl EditionMarker { .await?; #[cfg(feature = "padded")] - upsize_edition(context, &self.new_edition_pubkey).await; + { + upsize_metadata(context, &self.new_metadata_pubkey).await; + upsize_edition(context, &self.new_edition_pubkey).await; + } Ok(()) } @@ -341,7 +344,10 @@ impl EditionMarker { .await?; #[cfg(feature = "padded")] - upsize_edition(context, &self.new_edition_pubkey).await; + { + upsize_metadata(context, &self.new_metadata_pubkey).await; + upsize_edition(context, &self.new_edition_pubkey).await; + } Ok(()) } @@ -406,7 +412,10 @@ impl EditionMarker { .await?; #[cfg(feature = "padded")] - upsize_edition(context, &self.new_edition_pubkey).await; + { + upsize_metadata(context, &self.new_metadata_pubkey).await; + upsize_edition(context, &self.new_edition_pubkey).await; + } Ok(()) } @@ -470,7 +479,10 @@ impl EditionMarker { context.banks_client.process_transaction(tx).await?; #[cfg(feature = "padded")] - upsize_edition(context, &self.new_edition_pubkey).await; + { + upsize_metadata(context, &self.new_metadata_pubkey).await; + upsize_edition(context, &self.new_edition_pubkey).await; + } Ok(()) } diff --git a/programs/token-metadata/program/tests/utils/mod.rs b/programs/token-metadata/program/tests/utils/mod.rs index 17f336e1..e2918d56 100644 --- a/programs/token-metadata/program/tests/utils/mod.rs +++ b/programs/token-metadata/program/tests/utils/mod.rs @@ -310,9 +310,6 @@ pub async fn upsize_metadata(context: &mut ProgramTestContext, address: &Pubkey) extended[678] = original[606]; account_shared.set_data(extended); - // account_shared - // .checked_add_lamports(15616720 - 15115600) - // .expect("Overflow"); context.set_account(address, &account_shared); airdrop(context, address, 15616720 - 15115600) .await From 57bcb79f11673379715f3cdde2c4b816d329831e Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 30 Aug 2024 10:35:15 -0400 Subject: [PATCH 3/6] Fixing bytes on chain. --- clients/js/src/hooked/resolvers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/js/src/hooked/resolvers.ts b/clients/js/src/hooked/resolvers.ts index 20f6dc32..30fd305b 100644 --- a/clients/js/src/hooked/resolvers.ts +++ b/clients/js/src/hooked/resolvers.ts @@ -20,9 +20,9 @@ import { printSupply, } from '../generated'; -const METADATA_SIZE: number = 679; +const METADATA_SIZE: number = 607; -const MASTER_EDITION_SIZE: number = 282; +const MASTER_EDITION_SIZE: number = 20; export const resolveCollectionDetails = ( context: any, From 2ba083a7c9c2c2a40ddd668a3e64999df78c1020 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 30 Aug 2024 11:39:05 -0400 Subject: [PATCH 4/6] Fixing unused imports. --- programs/token-metadata/program/src/utils/master_edition.rs | 6 ++---- programs/token-metadata/program/src/utils/metadata.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/programs/token-metadata/program/src/utils/master_edition.rs b/programs/token-metadata/program/src/utils/master_edition.rs index 7563ed68..4bd0232d 100644 --- a/programs/token-metadata/program/src/utils/master_edition.rs +++ b/programs/token-metadata/program/src/utils/master_edition.rs @@ -23,10 +23,8 @@ use crate::{ pda::MARKER, state::{ get_reservation_list, DataV2, EditionMarker, EditionMarkerV2, Key, MasterEdition, Metadata, - TokenMetadataAccount, Uses, EDITION, EDITION_MARKER_BIT_SIZE, - EDITION_TOKEN_STANDARD_OFFSET, MASTER_EDITION_FEE_FLAG_OFFSET, - MASTER_EDITION_TOKEN_STANDARD_OFFSET, MAX_EDITION_LEN, MAX_EDITION_MARKER_SIZE, - MAX_MASTER_EDITION_LEN, PREFIX, + TokenMetadataAccount, Uses, EDITION, EDITION_MARKER_BIT_SIZE, MAX_EDITION_LEN, + MAX_EDITION_MARKER_SIZE, MAX_MASTER_EDITION_LEN, PREFIX, }, }; diff --git a/programs/token-metadata/program/src/utils/metadata.rs b/programs/token-metadata/program/src/utils/metadata.rs index a73daff9..63e02ec1 100644 --- a/programs/token-metadata/program/src/utils/metadata.rs +++ b/programs/token-metadata/program/src/utils/metadata.rs @@ -1,4 +1,4 @@ -use borsh::{maybestd::io::Error as BorshError, BorshDeserialize, BorshSerialize}; +use borsh::{maybestd::io::Error as BorshError, BorshDeserialize}; use mpl_utils::{ create_or_allocate_account_raw, token::{get_mint_authority, SPL_TOKEN_PROGRAM_IDS}, From 65c3a71fd6be1d96f2d499931fbde3d6d9f979e6 Mon Sep 17 00:00:00 2001 From: blockiosaurus <90809591+blockiosaurus@users.noreply.github.com> Date: Sun, 8 Sep 2024 07:59:26 -0400 Subject: [PATCH 5/6] Adding scaling fees based on native SVM rent (#139) * Adding scaling fees based on native SVM rent. * Fix upload. --- .github/workflows/build-programs.yml | 1 + programs/token-metadata/program/src/state/fee.rs | 11 ++++++++++- programs/token-metadata/program/src/utils/fee.rs | 4 ++-- programs/token-metadata/program/tests/fees.rs | 6 +++--- .../program/tests/utils/digital_asset.rs | 7 ++++--- .../token-metadata/program/tests/utils/metadata.rs | 5 ++--- programs/token-metadata/program/tests/utils/mod.rs | 2 ++ 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-programs.yml b/.github/workflows/build-programs.yml index f84ca8dd..dc82a1c1 100644 --- a/.github/workflows/build-programs.yml +++ b/.github/workflows/build-programs.yml @@ -64,4 +64,5 @@ jobs: name: program-builds # First wildcard ensures exported paths are consistently under the programs folder. path: ./program*/.bin/*.so + include-hidden-files: true if-no-files-found: error diff --git a/programs/token-metadata/program/src/state/fee.rs b/programs/token-metadata/program/src/state/fee.rs index ad9123b3..ea4f911e 100644 --- a/programs/token-metadata/program/src/state/fee.rs +++ b/programs/token-metadata/program/src/state/fee.rs @@ -1,9 +1,18 @@ use super::*; +use solana_program::{rent::Rent, sysvar::Sysvar}; pub(crate) const FEE_AUTHORITY: Pubkey = pubkey!("Levytx9LLPzAtDJJD7q813Zsm8zg9e1pb53mGxTKpD7"); +const CREATE_FEE_SCALAR: usize = 1308; +const CREATE_FEE_OFFSET: u64 = 5440; // create_metadata_accounts_v3, create, print edition commands -pub const CREATE_FEE: u64 = 10_000_000; +pub fn get_create_fee() -> Result { + let rent = Rent::get()?.minimum_balance(CREATE_FEE_SCALAR); + + Ok(rent + .checked_add(CREATE_FEE_OFFSET) + .ok_or(MetadataError::NumericalOverflowError)?) +} pub const FEE_FLAG_SET: u8 = 1; pub const FEE_FLAG_CLEARED: u8 = 0; diff --git a/programs/token-metadata/program/src/utils/fee.rs b/programs/token-metadata/program/src/utils/fee.rs index 2e823dfa..99182353 100644 --- a/programs/token-metadata/program/src/utils/fee.rs +++ b/programs/token-metadata/program/src/utils/fee.rs @@ -3,7 +3,7 @@ use solana_program::{ sysvar::Sysvar, }; -use crate::state::{fee::CREATE_FEE, Metadata, TokenMetadataAccount, METADATA_FEE_FLAG_INDEX}; +use crate::state::{get_create_fee, Metadata, TokenMetadataAccount, METADATA_FEE_FLAG_INDEX}; #[repr(C)] #[derive(Debug, Clone, Copy)] @@ -16,7 +16,7 @@ pub(crate) fn levy(args: LevyArgs) -> ProgramResult { // Fund metadata account with rent + Metaplex fee. let rent = Rent::get()?; - let fee = CREATE_FEE + rent.minimum_balance(Metadata::size()); + let fee = get_create_fee()? + rent.minimum_balance(Metadata::size()); invoke( &solana_program::system_instruction::transfer( diff --git a/programs/token-metadata/program/tests/fees.rs b/programs/token-metadata/program/tests/fees.rs index f0753434..3f1d88c9 100644 --- a/programs/token-metadata/program/tests/fees.rs +++ b/programs/token-metadata/program/tests/fees.rs @@ -13,7 +13,7 @@ mod fees { }; use token_metadata::{ instruction::{collect_fees, BurnArgs, UpdateArgs}, - state::{CREATE_FEE, FEE_FLAG_CLEARED, METADATA_FEE_FLAG_INDEX}, + state::{FEE_FLAG_CLEARED, METADATA_FEE_FLAG_INDEX}, }; use super::*; @@ -132,7 +132,7 @@ mod fees { println!("Transaction size: {:?}", tx.message().serialize().len()); context.banks_client.process_transaction(tx).await.unwrap(); - let expected_balance = num_accounts * CREATE_FEE; + let expected_balance = num_accounts * SOLANA_CREATE_FEE; let recipient_balance = get_account(&mut context, &recipient.pubkey()) .await @@ -205,7 +205,7 @@ mod fees { ); context.banks_client.process_transaction(tx).await.unwrap(); - let expected_balance = CREATE_FEE; + let expected_balance = SOLANA_CREATE_FEE; let recipient_balance = get_account(&mut context, &recipient.pubkey()) .await diff --git a/programs/token-metadata/program/tests/utils/digital_asset.rs b/programs/token-metadata/program/tests/utils/digital_asset.rs index f7a15ee7..50c79115 100644 --- a/programs/token-metadata/program/tests/utils/digital_asset.rs +++ b/programs/token-metadata/program/tests/utils/digital_asset.rs @@ -31,13 +31,14 @@ use token_metadata::{ state::{ AssetData, Collection, CollectionDetails, Creator, MasterEditionV2, Metadata, PrintSupply, ProgrammableConfig, TokenDelegateRole, TokenMetadataAccount, TokenRecord, TokenStandard, - CREATE_FEE, EDITION, EDITION_MARKER_BIT_SIZE, FEE_FLAG_SET, METADATA_FEE_FLAG_INDEX, - PREFIX, + EDITION, EDITION_MARKER_BIT_SIZE, FEE_FLAG_SET, METADATA_FEE_FLAG_INDEX, PREFIX, }, utils::unpack, ID, }; +use crate::SOLANA_CREATE_FEE; + use super::{airdrop, create_mint, create_token_account, get_account, mint_tokens}; pub const DEFAULT_NAME: &str = "Digital Asset"; @@ -1473,7 +1474,7 @@ impl DigitalAsset { let rent = context.banks_client.get_rent().await.unwrap(); let rent_exempt = rent.minimum_balance(account.data.len()); - let expected_lamports = rent_exempt + CREATE_FEE; + let expected_lamports = rent_exempt + SOLANA_CREATE_FEE; assert_eq!(account.lamports, expected_lamports); assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); diff --git a/programs/token-metadata/program/tests/utils/metadata.rs b/programs/token-metadata/program/tests/utils/metadata.rs index fb77e47a..6e1c50fe 100644 --- a/programs/token-metadata/program/tests/utils/metadata.rs +++ b/programs/token-metadata/program/tests/utils/metadata.rs @@ -6,8 +6,7 @@ use token_metadata::{ instruction, state::{ Collection, CollectionDetails, Creator, DataV2, Metadata as TmMetadata, - TokenMetadataAccount, TokenStandard, Uses, CREATE_FEE, FEE_FLAG_SET, - METADATA_FEE_FLAG_INDEX, PREFIX, + TokenMetadataAccount, TokenStandard, Uses, FEE_FLAG_SET, METADATA_FEE_FLAG_INDEX, PREFIX, }, ID, }; @@ -679,7 +678,7 @@ impl Metadata { let rent = context.banks_client.get_rent().await.unwrap(); let rent_exempt = rent.minimum_balance(account.data.len()); - let expected_lamports = rent_exempt + CREATE_FEE; + let expected_lamports = rent_exempt + SOLANA_CREATE_FEE; assert_eq!(account.lamports, expected_lamports); assert_eq!(account.data[METADATA_FEE_FLAG_INDEX], FEE_FLAG_SET); diff --git a/programs/token-metadata/program/tests/utils/mod.rs b/programs/token-metadata/program/tests/utils/mod.rs index f522a618..b915182b 100644 --- a/programs/token-metadata/program/tests/utils/mod.rs +++ b/programs/token-metadata/program/tests/utils/mod.rs @@ -28,6 +28,8 @@ pub const DEFAULT_COLLECTION_DETAILS: Option = { Some(CollectionDetails::V1 { size: 0 }) }; +pub const SOLANA_CREATE_FEE: u64 = 10_000_000; + pub fn program_test() -> ProgramTest { let mut program_test = ProgramTest::new("token_metadata", token_metadata::ID, None); program_test.add_program("spl_token_2022", spl_token_2022::ID, None); From 0236c068fca3e3a8fb7d9e8057da58ba010af05d Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 9 Sep 2024 09:36:36 -0400 Subject: [PATCH 6/6] Removing duplicate test. --- .../program/src/state/metadata.rs | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/programs/token-metadata/program/src/state/metadata.rs b/programs/token-metadata/program/src/state/metadata.rs index 2c5b11bb..a3068f4e 100644 --- a/programs/token-metadata/program/src/state/metadata.rs +++ b/programs/token-metadata/program/src/state/metadata.rs @@ -484,35 +484,6 @@ mod tests { assert_eq!(error, MetadataError::IncorrectOwner.into()); } - #[test] - fn successfully_deserialize_metadata_with_different_size() { - let expected_metadata = expected_pesky_metadata(); - - let mut buf = Vec::new(); - borsh::to_writer(&mut buf, &expected_metadata).unwrap(); - // No padding is added to the metadata so it's too short. - - let pubkey = Keypair::new().pubkey(); - let owner = ID; - let mut lamports = 1_000_000_000; - let mut data = buf.clone(); - - let account_info = AccountInfo::new( - &pubkey, - false, - true, - &mut lamports, - &mut data, - &owner, - false, - 1_000_000_000, - ); - - let md = Metadata::from_account_info(&account_info).unwrap(); - assert_eq!(md.key, Key::MetadataV1); - assert_eq!(md, expected_metadata); - } - #[test] fn fail_to_deserialize_master_edition_into_metadata() { let master_edition = MasterEditionV2 {