1use mpl_token_metadata::{
4 accounts::{MasterEdition, Metadata},
5 instructions::{
6 CreateMasterEditionV3Cpi, CreateMasterEditionV3CpiAccounts,
7 CreateMasterEditionV3InstructionArgs, CreateMetadataAccountV3Cpi,
8 CreateMetadataAccountV3CpiAccounts, CreateMetadataAccountV3InstructionArgs,
9 },
10 types::DataV2,
11};
12
13use crate::{
14 cpi::Cpi,
15 state::{COLLECTION_NAME, COLLECTION_PREFIX, COLLECTION_URI, META_SYMBOL},
16};
17
18use {
19 bonfida_utils::{
20 checks::{check_account_key, check_account_owner, check_signer},
21 BorshSize, InstructionsAccount,
22 },
23 borsh::{BorshDeserialize, BorshSerialize},
24 mpl_token_metadata::types::Creator,
25 solana_program::{
26 account_info::{next_account_info, AccountInfo},
27 entrypoint::ProgramResult,
28 msg,
29 program::{invoke, invoke_signed},
30 program_error::ProgramError,
31 program_pack::Pack,
32 pubkey::Pubkey,
33 system_program, sysvar,
34 },
35 spl_associated_token_account::instruction::create_associated_token_account,
36 spl_token::{
37 instruction::{initialize_mint, mint_to},
38 state::Mint,
39 },
40};
41
42#[derive(BorshDeserialize, BorshSerialize, BorshSize)]
43pub struct Params {}
44
45#[derive(InstructionsAccount)]
46pub struct Accounts<'a, T> {
47 #[cons(writable)]
49 pub collection_mint: &'a T,
50
51 #[cons(writable)]
52 pub edition: &'a T,
53
54 #[cons(writable)]
56 pub metadata_account: &'a T,
57
58 pub central_state: &'a T,
60
61 #[cons(writable)]
62 pub central_state_nft_ata: &'a T,
64
65 pub fee_payer: &'a T,
67
68 pub spl_token_program: &'a T,
70
71 pub metadata_program: &'a T,
73
74 pub system_program: &'a T,
76
77 pub spl_name_service_program: &'a T,
79
80 pub ata_program: &'a T,
81
82 pub rent_account: &'a T,
84}
85
86impl<'a, 'b: 'a> Accounts<'a, AccountInfo<'b>> {
87 pub fn parse(
88 accounts: &'a [AccountInfo<'b>],
89 _program_id: &Pubkey,
90 ) -> Result<Self, ProgramError> {
91 let accounts_iter = &mut accounts.iter();
92 let accounts = Accounts {
93 collection_mint: next_account_info(accounts_iter)?,
94 edition: next_account_info(accounts_iter)?,
95 metadata_account: next_account_info(accounts_iter)?,
96 central_state: next_account_info(accounts_iter)?,
97 central_state_nft_ata: next_account_info(accounts_iter)?,
98 fee_payer: next_account_info(accounts_iter)?,
99 spl_token_program: next_account_info(accounts_iter)?,
100 metadata_program: next_account_info(accounts_iter)?,
101 system_program: next_account_info(accounts_iter)?,
102 spl_name_service_program: next_account_info(accounts_iter)?,
103 ata_program: next_account_info(accounts_iter)?,
104 rent_account: next_account_info(accounts_iter)?,
105 };
106
107 check_account_key(accounts.central_state, &crate::central_state::KEY)?;
109 check_account_key(accounts.spl_token_program, &spl_token::ID)?;
110 check_account_key(accounts.metadata_program, &mpl_token_metadata::ID)?;
111 check_account_key(accounts.system_program, &system_program::ID)?;
112 check_account_key(accounts.spl_name_service_program, &spl_name_service::ID)?;
113 check_account_key(accounts.ata_program, &spl_associated_token_account::ID)?;
114 check_account_key(accounts.rent_account, &sysvar::rent::ID)?;
115
116 check_account_owner(accounts.collection_mint, &system_program::ID)?;
118 check_account_owner(accounts.edition, &system_program::ID)?;
119 check_account_owner(accounts.metadata_account, &system_program::ID)?;
120 check_account_owner(accounts.central_state_nft_ata, &system_program::ID)?;
121
122 check_signer(accounts.fee_payer)?;
124
125 Ok(accounts)
126 }
127}
128
129pub fn process(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
130 let accounts = Accounts::parse(accounts, program_id)?;
131
132 let (collection_mint, collection_mint_nonce) =
133 Pubkey::find_program_address(&[COLLECTION_PREFIX, &program_id.to_bytes()], program_id);
134 check_account_key(accounts.collection_mint, &collection_mint)?;
135
136 let (metadata_key, _) = Metadata::find_pda(&collection_mint);
137 check_account_key(accounts.metadata_account, &metadata_key)?;
138
139 let (edition_key, _) = MasterEdition::find_pda(&collection_mint);
140 check_account_key(accounts.edition, &edition_key)?;
141
142 msg!("+ Creating mint");
144 let seeds: &[&[u8]] = &[
145 COLLECTION_PREFIX,
146 &program_id.to_bytes(),
147 &[collection_mint_nonce],
148 ];
149 Cpi::create_account(
150 &spl_token::ID,
151 accounts.system_program,
152 accounts.fee_payer,
153 &accounts.collection_mint.clone(),
154 seeds,
155 Mint::LEN,
156 )?;
157 msg!("+ Initialize mint");
158 let ix = initialize_mint(
160 &spl_token::ID,
161 &collection_mint,
162 &crate::central_state::KEY,
163 Some(&crate::central_state::KEY),
164 0,
165 )?;
166 invoke_signed(
167 &ix,
168 &[
169 accounts.spl_token_program.clone(),
170 accounts.collection_mint.clone(),
171 accounts.rent_account.clone(),
172 ],
173 &[seeds],
174 )?;
175
176 msg!("+ Creating central state ATA");
178 let ix = create_associated_token_account(
179 accounts.fee_payer.key,
180 &crate::central_state::KEY,
181 &collection_mint,
182 &spl_token::ID,
183 );
184 invoke(
185 &ix,
186 &[
187 accounts.ata_program.clone(),
188 accounts.fee_payer.clone(),
189 accounts.central_state_nft_ata.clone(),
190 accounts.central_state.clone(),
191 accounts.collection_mint.clone(),
192 accounts.system_program.clone(),
193 accounts.spl_token_program.clone(),
194 accounts.rent_account.clone(),
195 ],
196 )?;
197
198 msg!("+ Minting NFT");
201 let seeds: &[&[u8]] = &[&program_id.to_bytes(), &[crate::central_state::NONCE]];
202 let ix = mint_to(
203 &spl_token::ID,
204 &collection_mint,
205 accounts.central_state_nft_ata.key,
206 &crate::central_state::KEY,
207 &[],
208 1,
209 )?;
210
211 invoke_signed(
212 &ix,
213 &[
214 accounts.spl_token_program.clone(),
215 accounts.collection_mint.clone(),
216 accounts.central_state_nft_ata.clone(),
217 accounts.central_state.clone(),
218 ],
219 &[seeds],
220 )?;
221
222 msg!("+ Creating collection");
224 let central_creator = Creator {
225 address: crate::central_state::KEY,
226 verified: true,
227 share: 100,
228 };
229 CreateMetadataAccountV3Cpi::new(
230 accounts.metadata_program,
231 CreateMetadataAccountV3CpiAccounts {
232 metadata: accounts.metadata_account,
233 mint: accounts.collection_mint,
234 mint_authority: accounts.central_state,
235 payer: accounts.fee_payer,
236 update_authority: (accounts.central_state, true),
237 system_program: accounts.system_program,
238 rent: Some(accounts.rent_account),
239 },
240 CreateMetadataAccountV3InstructionArgs {
241 data: DataV2 {
242 name: COLLECTION_NAME.to_string(),
243 uri: COLLECTION_URI.to_string(),
244 symbol: META_SYMBOL.to_string(),
245 seller_fee_basis_points: 0,
246 creators: Some(vec![central_creator]),
247 uses: None,
248 collection: None,
249 },
250 is_mutable: true,
251 collection_details: None,
252 },
253 )
254 .invoke_signed(&[seeds])?;
255
256 msg!("+ Creating master edition");
258 CreateMasterEditionV3Cpi::new(
259 accounts.metadata_program,
260 CreateMasterEditionV3CpiAccounts {
261 edition: accounts.edition,
262 mint: accounts.collection_mint,
263 update_authority: accounts.central_state,
264 token_program: accounts.spl_token_program,
265 system_program: accounts.system_program,
266 rent: Some(accounts.rent_account),
267 mint_authority: accounts.central_state,
268 metadata: accounts.metadata_account,
269 payer: accounts.fee_payer,
270 },
271 CreateMasterEditionV3InstructionArgs {
272 max_supply: Some(0),
273 },
274 )
275 .invoke_signed(&[seeds])?;
276
277 Ok(())
278}