shadow_nft_standard/instructions/
create.rs1use crate::{
4 common::{
5 _get_creator_group_seed, check_multisig,
6 collection::Collection,
7 creator_group::CreatorGroup,
8 metadata::{calculate_metadata_space, Metadata},
9 url::Url,
10 },
11 error::ErrorCode,
12 verbose_msg,
13};
14use anchor_lang::{prelude::*, solana_program::program_option::COption};
15use shadow_nft_common::payer_pda::get_payer_pda;
16
17#[cfg(feature = "use-token-2022")]
18use crate::common::token_2022::{Mint, Token2022 as Token};
19#[cfg(not(feature = "use-token-2022"))]
20use anchor_spl::token::{Mint, Token};
21
22pub fn handle_create_metadata(
29 ctx: Context<CreateMetadataAccount>,
30 args: CreateMetaArgs,
31) -> Result<()> {
32 let incorrect_mint_authority =
34 ctx.accounts.asset_mint.mint_authority != COption::Some(ctx.accounts.metadata.key());
35
36 if incorrect_mint_authority {
37 return Err(ErrorCode::InvalidMintAuthority.into());
38 };
39
40 let some_freeze_authority = ctx.accounts.asset_mint.freeze_authority.is_some();
41 if some_freeze_authority {
42 return Err(ErrorCode::FreezeAuthorityPresent.into());
43 }
44 let unitary = (ctx.accounts.asset_mint.supply == 0) & (ctx.accounts.asset_mint.decimals == 0);
45 if !unitary {
46 return Err(ErrorCode::DivisibleToken.into());
47 }
48 verbose_msg!("Verified Non-Fungibility");
49
50 if !ctx
52 .accounts
53 .collection
54 .initialized(&ctx.accounts.creator_group.creators)
55 {
56 return Err(ErrorCode::UnintializedCollection.into());
57 };
58
59 if ctx.accounts.creator_group.creators.len() > 1 {
61 check_multisig(
62 &ctx.accounts.creator_group.creators,
63 ctx.accounts.payer_creator.key,
64 ctx.remaining_accounts,
65 )?;
66 }
67
68 let metadata = &mut ctx.accounts.metadata;
70 metadata.mint = ctx.accounts.asset_mint.key();
71 metadata.update_authority = args.update_authority;
72 metadata.collection_key = args.collection_key;
73 metadata.post_primary = false;
74 metadata.mutable = args.mutable;
75 metadata.name = args.name;
76 metadata.uri = args.uri;
77 verbose_msg!("Initialized Metadata Account");
78
79 ctx.accounts.collection.size = ctx
81 .accounts
82 .collection
83 .size
84 .checked_add(1)
85 .ok_or(ErrorCode::IntegerOverflow)?;
86 verbose_msg!("Collection now has {} items", ctx.accounts.collection.size);
87
88 Ok(())
89}
90
91#[derive(Accounts)]
92#[instruction(
93 args: CreateMetaArgs
94)]
95pub struct CreateMetadataAccount<'info> {
96 #[account(
98 init,
99 payer = payer_creator,
100 seeds = [
101 asset_mint.key().as_ref()
102 ],
103 bump,
104 space = calculate_metadata_space(&args.name, &args.uri)
105 )]
106 pub metadata: Account<'info, Metadata>,
107
108 #[account(
111 seeds = [
112 _get_creator_group_seed(&creator_group.creators).as_ref()
115 ],
116 bump,
117 )]
118 pub creator_group: Account<'info, CreatorGroup>,
119
120 #[account()]
122 pub asset_mint: Account<'info, Mint>,
123
124 #[account(
126 mut,
127 address = args.collection_key
128 )]
129 pub collection: Account<'info, Collection>,
130
131 #[account(
133 mut,
134 constraint = {
136 let is_authorized_creator = creator_group.creators.contains(payer_creator.key) & !collection.for_minter;
137 let is_correct_and_authorized_minter_program_payer_pda =
140 (*payer_creator.key == get_payer_pda(&asset_mint.key())) & collection.for_minter;
141
142 is_authorized_creator || is_correct_and_authorized_minter_program_payer_pda
143 }
144 )]
145 pub payer_creator: Signer<'info>,
146
147 pub token_program: Program<'info, Token>,
148
149 pub system_program: Program<'info, System>,
150}
151
152#[derive(AnchorSerialize, AnchorDeserialize)]
153pub struct CreateMetaArgs {
154 pub update_authority: Pubkey,
155 pub name: String,
156 pub uri: Url,
157 pub mutable: bool,
158 pub collection_key: Pubkey,
159}