shadow_nft_standard/instructions/
create_collection.rs1use anchor_lang::prelude::*;
4
5use crate::{
6 common::{
7 collection::{Collection, Symbol, MAX_SYMBOL_BYTE_LENGTH},
8 creator_group::{CreatorGroup, MAX_GROUP_SIZE},
9 },
10 error::ErrorCode,
11 verbose_msg,
12};
13
14pub fn handle_create_collection(
21 ctx: Context<CreateCollection>,
22 args: CreateCollectionArgs,
23) -> Result<()> {
24 if !ctx.accounts.creator_group.initialized() {
25 return Err(ErrorCode::UnintializedGroup.into());
26 }
27
28 verbose_msg!(
30 "Signatures present {}",
31 ctx.accounts.collection.sigs.count_ones()
32 );
33 if ctx.accounts.collection.sigs.count_ones() == 0 {
34 let symbol_byte_len = args.symbol.as_bytes().len();
36 if symbol_byte_len > MAX_SYMBOL_BYTE_LENGTH {
37 return Err(ErrorCode::SymbolToolarge.into());
38 }
39 ctx.accounts.collection.symbol[..symbol_byte_len].copy_from_slice(args.symbol.as_bytes());
40
41 let num_creators = ctx.accounts.creator_group.creators.len();
43 if num_creators != args.royalty_50bps.len() {
44 verbose_msg!("number of royalty bps entries != number of creators in group");
45 return Err(ErrorCode::IncorrectCreatorGroupSize.into());
46 }
47 let total_50bps = args.royalty_50bps.iter().fold(0_u8, |a, b| {
49 a.checked_add(*b)
50 .expect("Invalid royalties. Must add up to a max of 100%")
51 });
52 if total_50bps > 200 {
53 return Err(ErrorCode::InvalidRoyalties.into());
54 }
55 ctx.accounts.collection.royalty_50bps[..num_creators].copy_from_slice(&args.royalty_50bps);
56
57 ctx.accounts.collection.name = args.name;
59
60 ctx.accounts.collection.for_minter = args.for_minter;
62
63 ctx.accounts.collection.creator_group_key = ctx.accounts.creator_group.key();
65
66 ctx.accounts.collection.size = 0;
68 }
69
70 ctx.accounts.collection.sigs |= 1
72 << ctx
73 .accounts
74 .creator_group
75 .creators
76 .iter()
77 .position(|c| c == ctx.accounts.payer_creator.key)
78 .unwrap();
79
80 if ctx
81 .accounts
82 .collection
83 .initialized(&ctx.accounts.creator_group.creators)
84 {
85 ctx.accounts.creator_group.num_collections += 1;
88
89 verbose_msg!(
91 "Created collection {}: {}..",
92 &args.symbol,
93 &ctx.accounts.collection.name[..32.min(ctx.accounts.collection.name.len())]
94 );
95 } else {
96 verbose_msg!(
97 "Creating collection, {}/{} members have signed",
98 &ctx.accounts.collection.sigs.count_ones(),
99 &ctx.accounts.creator_group.creators.len(),
100 );
101 }
102
103 Ok(())
104}
105
106#[derive(Accounts)]
107#[instruction(
108 args: CreateCollectionArgs
109)]
110pub struct CreateCollection<'info> {
111 #[account(
113 init_if_needed,
114 payer = payer_creator,
115 space = {
116 const DISCRIMINATOR: usize = 8;
117 let group_key = ::core::mem::size_of::<Pubkey>();
118 let size = ::core::mem::size_of::<u32>();
119 let verified = ::core::mem::size_of::<bool>();
120 let symbol = ::core::mem::size_of::<Symbol>();
121 let bps = ::core::mem::size_of::<[u8; MAX_GROUP_SIZE]>();
122 let name = 4 + args.name.as_bytes().len();
123 let for_minter = ::core::mem::size_of::<bool>();
124
125 DISCRIMINATOR + group_key + size + verified + symbol + name + bps + for_minter
126
127 },
128 seeds = [
129 creator_group.key().as_ref(),
130 args.name.as_ref(),
131 ],
132 bump
133 )]
134 pub collection: Account<'info, Collection>,
135
136 pub creator_group: Account<'info, CreatorGroup>,
138
139 #[account(
141 mut,
142 constraint = creator_group.creators.contains(payer_creator.key)
143 )]
144 pub payer_creator: Signer<'info>,
145
146 pub system_program: Program<'info, System>,
147}
148
149#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone)]
150pub struct CreateCollectionArgs {
151 pub name: String,
153
154 pub symbol: String,
156
157 pub for_minter: bool,
159
160 pub royalty_50bps: Vec<u8>,
165}