Skip to content

Commit

Permalink
Merge pull request #9 from metaplex-foundation/feat/tm-resize
Browse files Browse the repository at this point in the history
Resizing TM accounts going forward
  • Loading branch information
blockiosaurus authored Sep 9, 2024
2 parents 14c2637 + d8fe756 commit 538f5b9
Show file tree
Hide file tree
Showing 36 changed files with 445 additions and 170 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-programs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
32 changes: 32 additions & 0 deletions clients/js/src/generated/errors/mplTokenMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2923,6 +2923,38 @@ 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
);

/** 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
Expand Down
4 changes: 2 additions & 2 deletions clients/js/src/hooked/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions clients/rust/src/generated/errors/mpl_token_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,12 @@ 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,
/// 200 (0xC8) - Edition account has an invalid length
#[error("Edition account has an invalid length")]
InvalidEditionAccountLength,
}

impl solana_program::program_error::PrintProgramError for MplTokenMetadataError {
Expand Down
6 changes: 4 additions & 2 deletions configs/program-scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions idls/token_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7828,6 +7828,16 @@
"code": 198,
"name": "MissingImmutableOwnerExtension",
"msg": "Missing immutable owner extension"
},
{
"code": 199,
"name": "ExpectedUninitializedAccount",
"msg": "Expected account to be uninitialized"
},
{
"code": 200,
"name": "InvalidEditionAccountLength",
"msg": "Edition account has an invalid length"
}
],
"metadata": {
Expand Down
21 changes: 11 additions & 10 deletions programs/token-metadata/program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
[package]
name = "token_metadata"
version = "1.14.0"
description = "Metaplex Metadata"
authors = ["Metaplex Developers <[email protected]>"]
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/mpl-token-metadata"
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"] }
Expand All @@ -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"
Expand Down
13 changes: 6 additions & 7 deletions programs/token-metadata/program/src/assertions/edition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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
&& (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[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[0] == Key::EditionV1 as u8
&& edition_data[edition_data.len() - EDITION_TOKEN_STANDARD_OFFSET]
== TokenStandard::ProgrammableNonFungible as u8)
{
return Err(MetadataError::InvalidTokenStandard.into());
Expand Down
8 changes: 8 additions & 0 deletions programs/token-metadata/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,14 @@ pub enum MetadataError {
/// 198
#[error("Missing immutable owner extension")]
MissingImmutableOwnerExtension,

/// 199
#[error("Expected account to be uninitialized")]
ExpectedUninitializedAccount,

/// 200
#[error("Edition account has an invalid length")]
InvalidEditionAccountLength,
}

impl PrintProgramError for MetadataError {
Expand Down
8 changes: 3 additions & 5 deletions programs/token-metadata/program/src/processor/fee/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -162,7 +162,9 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, 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());
Expand Down
10 changes: 6 additions & 4 deletions programs/token-metadata/program/src/processor/metadata/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -298,15 +298,17 @@ 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();

if data.len() < MAX_EDITION_LEN {
return Err(MetadataError::InvalidMasterEditionAccountLength.into());
return Err(MetadataError::InvalidEditionAccountLength.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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Verify>) -> ProgramResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Verify>) -> ProgramResult {
Expand Down
6 changes: 3 additions & 3 deletions programs/token-metadata/program/src/state/edition.rs
Original file line number Diff line number Diff line change
@@ -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))]
Expand Down Expand Up @@ -41,7 +41,7 @@ impl TokenMetadataAccount for Edition {
}

fn size() -> usize {
MAX_EDITION_LEN
0
}
}

Expand Down
11 changes: 10 additions & 1 deletion programs/token-metadata/program/src/state/fee.rs
Original file line number Diff line number Diff line change
@@ -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<u64, ProgramError> {
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;
Loading

0 comments on commit 538f5b9

Please sign in to comment.