mpl_token_metadata/processor/metadata/
create.rs1use mpl_utils::assert_initialized;
2use solana_program::{
3 account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke, program_pack::Pack,
4 pubkey::Pubkey, rent::Rent, system_instruction, sysvar::Sysvar,
5};
6use spl_token::{native_mint::DECIMALS, state::Mint};
7
8use crate::{
9 error::MetadataError,
10 instruction::{Context, Create, CreateArgs},
11 state::{
12 Metadata, ProgrammableConfig, TokenMetadataAccount, TokenStandard, MAX_MASTER_EDITION_LEN,
13 TOKEN_STANDARD_INDEX,
14 },
15 utils::{
16 create_master_edition, process_create_metadata_accounts_logic,
17 CreateMetadataAccountsLogicArgs,
18 },
19};
20
21pub fn create<'a>(
26 program_id: &Pubkey,
27 accounts: &'a [AccountInfo<'a>],
28 args: CreateArgs,
29) -> ProgramResult {
30 let context = Create::to_context(accounts)?;
31
32 match args {
33 CreateArgs::V1 { .. } => create_v1(program_id, context, args),
34 }
35}
36
37fn create_v1(program_id: &Pubkey, ctx: Context<Create>, args: CreateArgs) -> ProgramResult {
39 let CreateArgs::V1 {
41 ref asset_data,
42 decimals,
43 print_supply,
44 } = args;
45
46 if matches!(asset_data.token_standard, TokenStandard::NonFungibleEdition) {
48 return Err(MetadataError::InvalidTokenStandard.into());
49 }
50
51 if ctx.accounts.mint_info.data_is_empty() {
54 if !ctx.accounts.mint_info.is_signer {
56 return Err(MetadataError::MintIsNotSigner.into());
57 }
58
59 msg!("Init mint");
60
61 invoke(
62 &system_instruction::create_account(
63 ctx.accounts.payer_info.key,
64 ctx.accounts.mint_info.key,
65 Rent::get()?.minimum_balance(spl_token::state::Mint::LEN),
66 spl_token::state::Mint::LEN as u64,
67 &spl_token::ID,
68 ),
69 &[
70 ctx.accounts.payer_info.clone(),
71 ctx.accounts.mint_info.clone(),
72 ],
73 )?;
74
75 let decimals = match asset_data.token_standard {
76 TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible => 0,
79 TokenStandard::FungibleAsset | TokenStandard::Fungible => match decimals {
82 Some(decimals) => decimals,
83 None => DECIMALS,
85 },
86 _ => {
87 return Err(MetadataError::InvalidTokenStandard.into());
88 }
89 };
90
91 invoke(
93 &spl_token::instruction::initialize_mint2(
94 ctx.accounts.spl_token_program_info.key,
95 ctx.accounts.mint_info.key,
96 ctx.accounts.authority_info.key,
97 Some(ctx.accounts.authority_info.key),
98 decimals,
99 )?,
100 &[
101 ctx.accounts.mint_info.clone(),
102 ctx.accounts.authority_info.clone(),
103 ],
104 )?;
105 } else {
106 let mint: Mint = assert_initialized(ctx.accounts.mint_info, MetadataError::Uninitialized)?;
109 if matches!(
111 asset_data.token_standard,
112 TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible
113 ) && (mint.decimals > 0 || mint.supply > 1)
114 {
115 return Err(MetadataError::InvalidMintForTokenStandard.into());
116 }
117 if matches!(
119 asset_data.token_standard,
120 TokenStandard::ProgrammableNonFungible
121 ) && (mint.supply > 0)
122 {
123 return Err(MetadataError::MintSupplyMustBeZero.into());
124 }
125 }
126
127 process_create_metadata_accounts_logic(
130 program_id,
131 CreateMetadataAccountsLogicArgs {
132 metadata_account_info: ctx.accounts.metadata_info,
133 mint_info: ctx.accounts.mint_info,
134 mint_authority_info: ctx.accounts.authority_info,
135 payer_account_info: ctx.accounts.payer_info,
136 update_authority_info: ctx.accounts.update_authority_info,
137 system_account_info: ctx.accounts.system_program_info,
138 },
139 asset_data.as_data_v2(),
140 false,
141 asset_data.is_mutable,
142 false,
143 true,
144 asset_data.collection_details.clone(),
145 )?;
146
147 if matches!(
150 asset_data.token_standard,
151 TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible
152 ) {
153 let print_supply = print_supply.ok_or(MetadataError::MissingPrintSupply)?;
154
155 if let Some(master_edition) = ctx.accounts.master_edition_info {
156 create_master_edition(
157 program_id,
158 master_edition,
159 ctx.accounts.mint_info,
160 ctx.accounts.update_authority_info,
161 ctx.accounts.authority_info,
162 ctx.accounts.payer_info,
163 ctx.accounts.metadata_info,
164 ctx.accounts.spl_token_program_info,
165 ctx.accounts.system_program_info,
166 print_supply.to_option(),
167 )?;
168
169 if matches!(
172 asset_data.token_standard,
173 TokenStandard::ProgrammableNonFungible
174 ) {
175 let mut data = master_edition.data.borrow_mut();
176
177 if data.len() < MAX_MASTER_EDITION_LEN {
178 return Err(MetadataError::InvalidMasterEditionAccountLength.into());
179 }
180
181 data[TOKEN_STANDARD_INDEX] = TokenStandard::ProgrammableNonFungible as u8;
182 }
183 } else {
184 return Err(MetadataError::MissingMasterEditionAccount.into());
185 }
186 } else if print_supply.is_some() {
187 msg!("Ignoring print supply for selected token standard");
188 }
189
190 let mut metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?;
191 metadata.token_standard = Some(asset_data.token_standard);
192 metadata.primary_sale_happened = asset_data.primary_sale_happened;
193
194 if matches!(
197 asset_data.token_standard,
198 TokenStandard::ProgrammableNonFungible
199 ) {
200 metadata.programmable_config = Some(ProgrammableConfig::V1 {
201 rule_set: asset_data.rule_set,
202 });
203 }
204
205 metadata.save(&mut ctx.accounts.metadata_info.try_borrow_mut_data()?)?;
207
208 Ok(())
209}