Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce compute units consumption #1074

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 12 additions & 25 deletions core/rust/utils/src/account.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
pubkey::Pubkey,
rent::Rent,
Expand All @@ -25,31 +24,19 @@ pub fn create_or_allocate_account_raw<'a>(
.max(1)
.saturating_sub(new_account_info.lamports());

if required_lamports > 0 {
msg!("Transfer {} lamports to the new account", required_lamports);
invoke(
&system_instruction::transfer(payer_info.key, new_account_info.key, required_lamports),
&[
payer_info.clone(),
new_account_info.clone(),
system_program_info.clone(),
],
)?;
}

let accounts = &[new_account_info.clone(), system_program_info.clone()];

msg!("Allocate space for the account");
invoke_signed(
&system_instruction::allocate(new_account_info.key, size.try_into().unwrap()),
accounts,
&[signer_seeds],
)?;

msg!("Assign the account to the owning program");
invoke_signed(
&system_instruction::assign(new_account_info.key, &program_id),
accounts,
&system_instruction::create_account(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

payer_info.key,
new_account_info.key,
required_lamports,
size as u64,
&program_id,
),
&[
payer_info.clone(),
new_account_info.clone(),
system_program_info.clone(),
],
&[signer_seeds],
)?;

Expand Down
89 changes: 57 additions & 32 deletions token-metadata/program/src/processor/metadata/create.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use mpl_utils::assert_initialized;
use mpl_utils::{assert_initialized, create_or_allocate_account_raw};
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke, program_pack::Pack,
pubkey::Pubkey, rent::Rent, system_instruction, sysvar::Sysvar,
Expand All @@ -8,12 +8,14 @@ use spl_token::{native_mint::DECIMALS, state::Mint};
use crate::{
error::MetadataError,
instruction::{Context, Create, CreateArgs},
pda::{EDITION, PREFIX},
state::{
Metadata, ProgrammableConfig, TokenMetadataAccount, TokenStandard, MAX_MASTER_EDITION_LEN,
TOKEN_STANDARD_INDEX,
Key, MasterEdition, MasterEditionV2, Metadata, ProgrammableConfig, TokenMetadataAccount,
TokenStandard, MAX_MASTER_EDITION_LEN, TOKEN_STANDARD_INDEX,
},
utils::{
create_master_edition, process_create_metadata_accounts_logic,
assert_mint_authority_matches_mint, assert_owned_by, assert_token_program_matches_package,
process_create_metadata_accounts_logic, transfer_mint_authority,
CreateMetadataAccountsLogicArgs,
},
};
Expand Down Expand Up @@ -48,16 +50,16 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
return Err(MetadataError::InvalidTokenStandard.into());
}

// if the account does not exist, we will allocate a new mint
assert_token_program_matches_package(ctx.accounts.spl_token_program_info)?;

// if the mint account does not exist, we will allocate a new mint

if ctx.accounts.mint_info.data_is_empty() {
// mint account must be a signer in the transaction
if !ctx.accounts.mint_info.is_signer {
return Err(MetadataError::MintIsNotSigner.into());
}

msg!("Init mint");

invoke(
&system_instruction::create_account(
ctx.accounts.payer_info.key,
Expand Down Expand Up @@ -105,7 +107,10 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
} else {
// validates the existing mint account

assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?;
let mint: Mint = assert_initialized(ctx.accounts.mint_info, MetadataError::Uninitialized)?;
assert_mint_authority_matches_mint(&mint.mint_authority, ctx.accounts.authority_info)?;

// NonFungible assets must have decimals == 0 and supply no greater than 1
if matches!(
asset_data.token_standard,
Expand Down Expand Up @@ -144,6 +149,10 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
asset_data.collection_details.clone(),
)?;

let mut metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?;
metadata.token_standard = Some(asset_data.token_standard);
metadata.primary_sale_happened = asset_data.primary_sale_happened;

// creates the master edition account (only for NonFungible assets)

if matches!(
Expand All @@ -153,21 +162,46 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
let print_supply = print_supply.ok_or(MetadataError::MissingPrintSupply)?;

if let Some(master_edition) = ctx.accounts.master_edition_info {
create_master_edition(
program_id,
let edition_authority_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
ctx.accounts.mint_info.key.as_ref(),
EDITION.as_bytes(),
&[metadata
.edition_nonce
.ok_or(MetadataError::NotAMasterEdition)?],
];
let edition_key = Pubkey::create_program_address(edition_authority_seeds, program_id)?;

if *master_edition.key != edition_key {
return Err(MetadataError::DerivedKeyInvalid.into());
}

create_or_allocate_account_raw(
*program_id,
master_edition,
ctx.accounts.system_program_info,
ctx.accounts.payer_info,
MAX_MASTER_EDITION_LEN,
edition_authority_seeds,
)?;

let mut edition = MasterEditionV2::from_account_info(master_edition)?;
edition.key = Key::MasterEditionV2;
edition.supply = 0;
edition.max_supply = print_supply.to_option();
edition.save(master_edition)?;

// while you can't mint only mint 1 token from your master record, you can
// mint as many limited editions as you like within your max supply
transfer_mint_authority(
master_edition.key,
master_edition,
ctx.accounts.mint_info,
ctx.accounts.update_authority_info,
ctx.accounts.authority_info,
ctx.accounts.payer_info,
ctx.accounts.metadata_info,
ctx.accounts.spl_token_program_info,
ctx.accounts.system_program_info,
print_supply.to_option(),
)?;

// for pNFTs, we store the token standard value at the end of the
// master edition account
if matches!(
asset_data.token_standard,
TokenStandard::ProgrammableNonFungible
Expand All @@ -177,8 +211,13 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
if data.len() < MAX_MASTER_EDITION_LEN {
return Err(MetadataError::InvalidMasterEditionAccountLength.into());
}

// for pNFTs, we store the token standard value at the end of the
// master edition account
data[TOKEN_STANDARD_INDEX] = TokenStandard::ProgrammableNonFungible as u8;

metadata.programmable_config = Some(ProgrammableConfig::V1 {
rule_set: asset_data.rule_set,
});
}
} else {
return Err(MetadataError::MissingMasterEditionAccount.into());
Expand All @@ -187,21 +226,7 @@ fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> Pro
msg!("Ignoring print supply for selected token standard");
}

let mut metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?;
metadata.token_standard = Some(asset_data.token_standard);

// sets the programmable config for programmable assets

if matches!(
asset_data.token_standard,
TokenStandard::ProgrammableNonFungible
) {
metadata.programmable_config = Some(ProgrammableConfig::V1 {
rule_set: asset_data.rule_set,
});
}

// saves the state
// saves the metadata state
metadata.save(&mut ctx.accounts.metadata_info.try_borrow_mut_data()?)?;

Ok(())
Expand Down
8 changes: 6 additions & 2 deletions token-metadata/program/src/state/master_edition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,12 @@ impl MasterEdition for MasterEditionV2 {
}

fn save(&self, account: &AccountInfo) -> ProgramResult {
let mut storage = &mut account.data.borrow_mut()[..TOKEN_STANDARD_INDEX];
BorshSerialize::serialize(self, &mut storage)?;
let mut bytes = Vec::with_capacity(TOKEN_STANDARD_INDEX);
BorshSerialize::serialize(&self, &mut bytes)?;

let data = &mut account.data.borrow_mut();
data[..bytes.len()].copy_from_slice(&bytes);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we talked about this before, but is this better or worse than sol_memcpy?


Ok(())
}
}
Expand Down