1use {
4 crate::{
5 error::TokenWrapError,
6 get_wrapped_mint_address, get_wrapped_mint_address_with_seed, get_wrapped_mint_authority,
7 get_wrapped_mint_authority_signer_seeds, get_wrapped_mint_authority_with_seed,
8 get_wrapped_mint_backpointer_address_signer_seeds,
9 get_wrapped_mint_backpointer_address_with_seed, get_wrapped_mint_signer_seeds,
10 instruction::TokenWrapInstruction,
11 metadata::extract_token_metadata,
12 metaplex::token_2022_metadata_to_metaplex,
13 mint_customizer::{
14 default_token_2022::DefaultToken2022Customizer, interface::MintCustomizer,
15 },
16 state::Backpointer,
17 },
18 mpl_token_metadata::{
19 accounts::Metadata as MetaplexMetadata,
20 instructions::{
21 CreateMetadataAccountV3, CreateMetadataAccountV3InstructionArgs,
22 UpdateMetadataAccountV2, UpdateMetadataAccountV2InstructionArgs,
23 },
24 },
25 solana_account_info::{next_account_info, AccountInfo},
26 solana_cpi::{invoke, invoke_signed},
27 solana_msg::msg,
28 solana_program_error::{ProgramError, ProgramResult},
29 solana_program_pack::Pack,
30 solana_pubkey::Pubkey,
31 solana_rent::Rent,
32 solana_system_interface::instruction::{allocate, assign},
33 solana_sysvar::{clock::Clock, Sysvar},
34 spl_associated_token_account_client::address::get_associated_token_address_with_program_id,
35 spl_token_2022::{
36 extension::{
37 transfer_fee::TransferFeeConfig, BaseStateWithExtensions, ExtensionType,
38 PodStateWithExtensions,
39 },
40 instruction::initialize_mint2,
41 onchain::{
42 extract_multisig_accounts, invoke_transfer_checked, invoke_transfer_checked_with_fee,
43 },
44 pod::{PodAccount, PodMint},
45 state::AccountState,
46 },
47 spl_token_metadata_interface::{
48 instruction::{initialize as initialize_token_metadata, remove_key, update_field},
49 state::{Field, TokenMetadata},
50 },
51 std::collections::HashMap,
52};
53
54pub fn process_create_mint<M: MintCustomizer>(
56 program_id: &Pubkey,
57 accounts: &[AccountInfo],
58 idempotent: bool,
59) -> ProgramResult {
60 let account_info_iter = &mut accounts.iter();
61
62 let wrapped_mint_account = next_account_info(account_info_iter)?;
63 let wrapped_backpointer_account = next_account_info(account_info_iter)?;
64 let unwrapped_mint_account = next_account_info(account_info_iter)?;
65 let _system_program_account = next_account_info(account_info_iter)?;
66 let wrapped_token_program_account = next_account_info(account_info_iter)?;
67
68 let (wrapped_mint_address, mint_bump) = get_wrapped_mint_address_with_seed(
69 unwrapped_mint_account.key,
70 wrapped_token_program_account.key,
71 );
72
73 let (wrapped_backpointer_address, backpointer_bump) =
74 get_wrapped_mint_backpointer_address_with_seed(wrapped_mint_account.key);
75
76 if *wrapped_mint_account.key != wrapped_mint_address {
79 Err(TokenWrapError::WrappedMintMismatch)?
80 }
81
82 if *wrapped_backpointer_account.key != wrapped_backpointer_address {
83 Err(TokenWrapError::BackpointerMismatch)?
84 }
85
86 if unwrapped_mint_account.owner != &spl_token::id()
88 && unwrapped_mint_account.owner != &spl_token_2022::id()
89 {
90 Err(ProgramError::InvalidAccountOwner)?
91 }
92
93 if wrapped_mint_account.data_len() > 0 || wrapped_backpointer_account.data_len() > 0 {
96 msg!("Wrapped mint or backpointer account already initialized");
97 if !idempotent {
98 Err(ProgramError::AccountAlreadyInitialized)?
99 }
100 if wrapped_mint_account.owner != wrapped_token_program_account.key {
101 Err(TokenWrapError::InvalidWrappedMintOwner)?
102 }
103 if wrapped_backpointer_account.owner != program_id {
104 Err(TokenWrapError::InvalidBackpointerOwner)?
105 }
106 return Ok(());
107 }
108
109 let bump_seed = [mint_bump];
112 let signer_seeds = get_wrapped_mint_signer_seeds(
113 unwrapped_mint_account.key,
114 wrapped_token_program_account.key,
115 &bump_seed,
116 );
117
118 let space = if *wrapped_token_program_account.key == spl_token_2022::id() {
119 M::get_token_2022_mint_space()?
120 } else {
121 spl_token::state::Mint::get_packed_len()
122 };
123
124 let rent = Rent::get()?;
125 let mint_rent_required = rent.minimum_balance(space);
126 if wrapped_mint_account.lamports() < mint_rent_required {
127 msg!(
128 "Error: wrapped_mint_account requires pre-funding of {} lamports",
129 mint_rent_required
130 );
131 Err(ProgramError::AccountNotRentExempt)?
132 }
133
134 invoke_signed(
137 &allocate(&wrapped_mint_address, space as u64),
138 &[wrapped_mint_account.clone()],
139 &[&signer_seeds],
140 )?;
141 invoke_signed(
142 &assign(&wrapped_mint_address, wrapped_token_program_account.key),
143 &[wrapped_mint_account.clone()],
144 &[&signer_seeds],
145 )?;
146
147 if *wrapped_token_program_account.key == spl_token_2022::id() {
148 M::initialize_extensions(wrapped_mint_account, wrapped_token_program_account)?;
149 }
150
151 let (freeze_authority, decimals) = M::get_freeze_auth_and_decimals(unwrapped_mint_account)?;
152 let wrapped_mint_authority = get_wrapped_mint_authority(wrapped_mint_account.key);
153
154 invoke(
155 &initialize_mint2(
156 wrapped_token_program_account.key,
157 wrapped_mint_account.key,
158 &wrapped_mint_authority,
159 freeze_authority.as_ref(),
160 decimals,
161 )?,
162 &[wrapped_mint_account.clone()],
163 )?;
164
165 let backpointer_space = std::mem::size_of::<Backpointer>();
168 let backpointer_rent_required = rent.minimum_balance(backpointer_space);
169 if wrapped_backpointer_account.lamports() < backpointer_rent_required {
170 msg!(
171 "Error: wrapped_backpointer_account requires pre-funding of {} lamports",
172 backpointer_rent_required
173 );
174 Err(ProgramError::AccountNotRentExempt)?
175 }
176
177 let bump_seed = [backpointer_bump];
178 let backpointer_signer_seeds =
179 get_wrapped_mint_backpointer_address_signer_seeds(wrapped_mint_account.key, &bump_seed);
180 invoke_signed(
181 &allocate(&wrapped_backpointer_address, backpointer_space as u64),
182 &[wrapped_backpointer_account.clone()],
183 &[&backpointer_signer_seeds],
184 )?;
185 invoke_signed(
186 &assign(&wrapped_backpointer_address, program_id),
187 &[wrapped_backpointer_account.clone()],
188 &[&backpointer_signer_seeds],
189 )?;
190
191 let mut backpointer_account_data = wrapped_backpointer_account.try_borrow_mut_data()?;
194 let backpointer = bytemuck::from_bytes_mut::<Backpointer>(&mut backpointer_account_data[..]);
195 backpointer.unwrapped_mint = *unwrapped_mint_account.key;
196
197 Ok(())
198}
199
200pub fn process_wrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
202 if amount == 0 {
203 Err(TokenWrapError::ZeroWrapAmount)?
204 }
205
206 let account_info_iter = &mut accounts.iter();
207
208 let recipient_wrapped_token_account = next_account_info(account_info_iter)?;
209 let wrapped_mint = next_account_info(account_info_iter)?;
210 let wrapped_mint_authority = next_account_info(account_info_iter)?;
211 let unwrapped_token_program = next_account_info(account_info_iter)?;
212 let wrapped_token_program = next_account_info(account_info_iter)?;
213 let unwrapped_token_account = next_account_info(account_info_iter)?;
214 let unwrapped_mint = next_account_info(account_info_iter)?;
215 let unwrapped_escrow = next_account_info(account_info_iter)?;
216 let transfer_authority = next_account_info(account_info_iter)?;
217
218 let expected_wrapped_mint =
221 get_wrapped_mint_address(unwrapped_mint.key, wrapped_token_program.key);
222 if expected_wrapped_mint != *wrapped_mint.key {
223 Err(TokenWrapError::WrappedMintMismatch)?
224 }
225
226 let (expected_authority, bump) = get_wrapped_mint_authority_with_seed(wrapped_mint.key);
227 if *wrapped_mint_authority.key != expected_authority {
228 Err(TokenWrapError::MintAuthorityMismatch)?
229 }
230
231 let expected_escrow = get_associated_token_address_with_program_id(
232 wrapped_mint_authority.key,
233 unwrapped_mint.key,
234 unwrapped_token_program.key,
235 );
236
237 if *unwrapped_escrow.key != expected_escrow {
238 Err(TokenWrapError::EscrowMismatch)?
239 }
240
241 {
242 let escrow_data = unwrapped_escrow.try_borrow_data()?;
243 let escrow_account = PodStateWithExtensions::<PodAccount>::unpack(&escrow_data)?;
244 if escrow_account.base.owner != expected_authority {
245 Err(TokenWrapError::EscrowOwnerMismatch)?
246 }
247 }
248
249 let unwrapped_mint_data = unwrapped_mint.try_borrow_data()?;
252 let unwrapped_mint_state = PodStateWithExtensions::<PodMint>::unpack(&unwrapped_mint_data)?;
253
254 let clock = Clock::get()?.epoch;
256 let fee = unwrapped_mint_state
257 .get_extension::<TransferFeeConfig>()
258 .ok()
259 .and_then(|cfg| cfg.calculate_epoch_fee(clock, amount))
260 .unwrap_or(0);
261 let net_amount = amount
262 .checked_sub(fee)
263 .ok_or(ProgramError::ArithmeticOverflow)?;
264
265 if unwrapped_token_program.key == &spl_token_2022::id() {
266 invoke_transfer_checked_with_fee(
268 unwrapped_token_program.key,
269 unwrapped_token_account.clone(),
270 unwrapped_mint.clone(),
271 unwrapped_escrow.clone(),
272 transfer_authority.clone(),
273 &accounts[9..],
274 amount,
275 unwrapped_mint_state.base.decimals,
276 fee,
277 &[],
278 )?;
279 } else {
280 invoke_transfer_checked(
281 unwrapped_token_program.key,
282 unwrapped_token_account.clone(),
283 unwrapped_mint.clone(),
284 unwrapped_escrow.clone(),
285 transfer_authority.clone(),
286 &accounts[9..],
287 amount,
288 unwrapped_mint_state.base.decimals,
289 &[],
290 )?;
291 }
292
293 let bump_seed = [bump];
295 let signer_seeds = get_wrapped_mint_authority_signer_seeds(wrapped_mint.key, &bump_seed);
296
297 invoke_signed(
298 &spl_token_2022::instruction::mint_to(
299 wrapped_token_program.key,
300 wrapped_mint.key,
301 recipient_wrapped_token_account.key,
302 wrapped_mint_authority.key,
303 &[],
304 net_amount,
305 )?,
306 &[
307 wrapped_mint.clone(),
308 recipient_wrapped_token_account.clone(),
309 wrapped_mint_authority.clone(),
310 ],
311 &[&signer_seeds],
312 )?;
313
314 Ok(())
315}
316
317pub fn process_unwrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
319 if amount == 0 {
320 Err(TokenWrapError::ZeroWrapAmount)?
321 }
322
323 let account_info_iter = &mut accounts.iter();
324
325 let unwrapped_escrow = next_account_info(account_info_iter)?;
326 let recipient_unwrapped_token = next_account_info(account_info_iter)?;
327 let wrapped_mint_authority = next_account_info(account_info_iter)?;
328 let unwrapped_mint = next_account_info(account_info_iter)?;
329 let wrapped_token_program = next_account_info(account_info_iter)?;
330 let unwrapped_token_program = next_account_info(account_info_iter)?;
331 let wrapped_token_account = next_account_info(account_info_iter)?;
332 let wrapped_mint = next_account_info(account_info_iter)?;
333 let transfer_authority = next_account_info(account_info_iter)?;
334 let additional_accounts = account_info_iter.as_slice();
335
336 let expected_wrapped_mint =
339 get_wrapped_mint_address(unwrapped_mint.key, wrapped_token_program.key);
340 if expected_wrapped_mint != *wrapped_mint.key {
341 Err(TokenWrapError::WrappedMintMismatch)?
342 }
343
344 let (expected_authority, bump) = get_wrapped_mint_authority_with_seed(wrapped_mint.key);
345 if *wrapped_mint_authority.key != expected_authority {
346 Err(TokenWrapError::MintAuthorityMismatch)?
347 }
348
349 let expected_escrow = get_associated_token_address_with_program_id(
350 wrapped_mint_authority.key,
351 unwrapped_mint.key,
352 unwrapped_token_program.key,
353 );
354 if *unwrapped_escrow.key != expected_escrow {
355 Err(TokenWrapError::EscrowMismatch)?
356 }
357
358 let multisig_signer_keys = extract_multisig_accounts(transfer_authority, additional_accounts)?
361 .iter()
362 .map(|a| a.key)
363 .collect::<Vec<_>>();
364
365 invoke(
366 &spl_token_2022::instruction::burn(
367 wrapped_token_program.key,
368 wrapped_token_account.key,
369 wrapped_mint.key,
370 transfer_authority.key,
371 &multisig_signer_keys,
372 amount,
373 )?,
374 &accounts[6..],
375 )?;
376
377 let unwrapped_mint_data = unwrapped_mint.try_borrow_data()?;
380 let unwrapped_mint_state = PodStateWithExtensions::<PodMint>::unpack(&unwrapped_mint_data)?;
381 let bump_seed = [bump];
382 let signer_seeds = get_wrapped_mint_authority_signer_seeds(wrapped_mint.key, &bump_seed);
383
384 invoke_transfer_checked(
385 unwrapped_token_program.key,
386 unwrapped_escrow.clone(),
387 unwrapped_mint.clone(),
388 recipient_unwrapped_token.clone(),
389 wrapped_mint_authority.clone(),
390 additional_accounts,
391 amount,
392 unwrapped_mint_state.base.decimals,
393 &[&signer_seeds],
394 )?;
395
396 Ok(())
397}
398
399pub fn process_close_stuck_escrow(accounts: &[AccountInfo]) -> ProgramResult {
401 let account_info_iter = &mut accounts.iter();
402
403 let escrow_account = next_account_info(account_info_iter)?;
404 let destination_account = next_account_info(account_info_iter)?;
405 let unwrapped_mint = next_account_info(account_info_iter)?;
406 let wrapped_mint = next_account_info(account_info_iter)?;
407 let wrapped_mint_authority = next_account_info(account_info_iter)?;
408 let _token_2022_program = next_account_info(account_info_iter)?;
409
410 if *escrow_account.owner != spl_token_2022::id()
413 || unwrapped_mint.owner != &spl_token_2022::id()
414 {
415 return Err(ProgramError::IncorrectProgramId);
416 }
417
418 let expected_wrapped_mint_pubkey =
419 get_wrapped_mint_address(unwrapped_mint.key, wrapped_mint.owner);
420 if *wrapped_mint.key != expected_wrapped_mint_pubkey {
421 Err(TokenWrapError::WrappedMintMismatch)?
422 }
423
424 let (expected_authority, bump) = get_wrapped_mint_authority_with_seed(wrapped_mint.key);
425 if *wrapped_mint_authority.key != expected_authority {
426 Err(TokenWrapError::MintAuthorityMismatch)?
427 }
428
429 let expected_escrow_address = get_associated_token_address_with_program_id(
430 wrapped_mint_authority.key,
431 unwrapped_mint.key,
432 unwrapped_mint.owner,
433 );
434
435 if *escrow_account.key != expected_escrow_address {
436 return Err(TokenWrapError::EscrowMismatch.into());
437 }
438
439 let escrow_data = escrow_account.try_borrow_data()?;
440 let escrow_state = PodStateWithExtensions::<PodAccount>::unpack(&escrow_data)?;
441
442 if escrow_state.base.owner != *wrapped_mint_authority.key {
443 return Err(TokenWrapError::EscrowOwnerMismatch.into());
444 }
445
446 if u64::from(escrow_state.base.amount) != 0 {
448 return Err(ProgramError::InvalidAccountData);
449 }
450
451 if escrow_state.base.state != (AccountState::Initialized as u8) {
453 return Err(ProgramError::InvalidAccountData);
454 }
455
456 let current_account_extensions = escrow_state.get_extension_types()?;
457 drop(escrow_data);
458
459 let mint_data = unwrapped_mint.try_borrow_data()?;
460 let mint_state = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
461 let mint_extensions = mint_state.get_extension_types()?;
462 let mut required_account_extensions =
463 ExtensionType::get_required_init_account_extensions(&mint_extensions);
464
465 if !required_account_extensions.contains(&ExtensionType::ImmutableOwner) {
467 required_account_extensions.push(ExtensionType::ImmutableOwner);
468 }
469
470 let in_good_state = current_account_extensions.len() == required_account_extensions.len()
473 && required_account_extensions
474 .iter()
475 .all(|item| current_account_extensions.contains(item));
476
477 if in_good_state {
478 return Err(TokenWrapError::EscrowInGoodState.into());
479 }
480
481 let bump_seed = [bump];
483 let signer_seeds = get_wrapped_mint_authority_signer_seeds(wrapped_mint.key, &bump_seed);
484
485 invoke_signed(
486 &spl_token_2022::instruction::close_account(
487 escrow_account.owner,
488 escrow_account.key,
489 destination_account.key,
490 wrapped_mint_authority.key,
491 &[],
492 )?,
493 &[
494 escrow_account.clone(),
495 destination_account.clone(),
496 wrapped_mint_authority.clone(),
497 ],
498 &[&signer_seeds],
499 )?;
500
501 Ok(())
502}
503
504type FieldExtractor = Vec<(Field, fn(&TokenMetadata) -> &str)>;
505
506fn update_fields_if_changed<'a>(
507 token_program: &Pubkey,
508 mint: &AccountInfo<'a>,
509 authority: &AccountInfo<'a>,
510 fields: FieldExtractor,
511 wrapped_metadata: &TokenMetadata,
512 unwrapped_metadata: &TokenMetadata,
513 signer_seeds: &[&[&[u8]]],
514) -> ProgramResult {
515 for (field, extractor) in fields {
516 let current_value = extractor(wrapped_metadata);
517 let new_value = extractor(unwrapped_metadata);
518 if current_value != new_value {
519 invoke_signed(
520 &update_field(
521 token_program,
522 mint.key,
523 authority.key,
524 field.clone(),
525 new_value.to_string(),
526 ),
527 &[mint.clone(), authority.clone()],
528 signer_seeds,
529 )?;
530 }
531 }
532 Ok(())
533}
534
535pub fn process_sync_metadata_to_token_2022(accounts: &[AccountInfo]) -> ProgramResult {
538 let account_info_iter = &mut accounts.iter();
539 let wrapped_mint_info = next_account_info(account_info_iter)?;
540 let wrapped_mint_authority_info = next_account_info(account_info_iter)?;
541 let unwrapped_mint_info = next_account_info(account_info_iter)?;
542 let token_program_info = next_account_info(account_info_iter)?;
543 let source_metadata_info = account_info_iter.next();
544 let owner_program_info = account_info_iter.next();
545
546 if *token_program_info.key != spl_token_2022::id() {
547 return Err(ProgramError::IncorrectProgramId);
548 }
549
550 if *wrapped_mint_info.owner != spl_token_2022::id() {
551 return Err(ProgramError::IncorrectProgramId);
552 }
553
554 let (expected_wrapped_mint, _) =
555 get_wrapped_mint_address_with_seed(unwrapped_mint_info.key, &spl_token_2022::id());
556 if *wrapped_mint_info.key != expected_wrapped_mint {
557 return Err(TokenWrapError::WrappedMintMismatch.into());
558 }
559 let (expected_authority, authority_bump) =
560 get_wrapped_mint_authority_with_seed(wrapped_mint_info.key);
561 if *wrapped_mint_authority_info.key != expected_authority {
562 return Err(TokenWrapError::MintAuthorityMismatch.into());
563 }
564
565 let unwrapped_metadata = extract_token_metadata(
566 unwrapped_mint_info,
567 source_metadata_info,
568 owner_program_info,
569 )?;
570
571 let authority_bump_seed = [authority_bump];
572 let authority_signer_seeds =
573 get_wrapped_mint_authority_signer_seeds(wrapped_mint_info.key, &authority_bump_seed);
574
575 let wrapped_mint_data = wrapped_mint_info.try_borrow_data()?;
576 let wrapped_mint_metadata = PodStateWithExtensions::<PodMint>::unpack(&wrapped_mint_data)?
577 .get_variable_len_extension::<TokenMetadata>()
578 .ok();
579 drop(wrapped_mint_data);
580
581 let cpi_accounts = [
582 wrapped_mint_info.clone(),
583 wrapped_mint_authority_info.clone(),
584 ];
585
586 if let Some(wrapped_metadata) = wrapped_mint_metadata {
587 let base_fields: FieldExtractor = vec![
593 (Field::Name, |m| &m.name),
594 (Field::Symbol, |m| &m.symbol),
595 (Field::Uri, |m| &m.uri),
596 ];
597
598 update_fields_if_changed(
599 token_program_info.key,
600 wrapped_mint_info,
601 wrapped_mint_authority_info,
602 base_fields,
603 &wrapped_metadata,
604 &unwrapped_metadata,
605 &[&authority_signer_seeds],
606 )?;
607
608 let mut wrapped_meta_map: HashMap<String, String> =
610 wrapped_metadata.additional_metadata.into_iter().collect();
611
612 for (key, value) in &unwrapped_metadata.additional_metadata {
613 if wrapped_meta_map.get(key) != Some(value) {
615 invoke_signed(
616 &update_field(
617 token_program_info.key,
618 wrapped_mint_info.key,
619 wrapped_mint_authority_info.key,
620 Field::Key(key.clone()),
621 value.clone(),
622 ),
623 &cpi_accounts,
624 &[&authority_signer_seeds],
625 )?;
626 }
627 wrapped_meta_map.remove(key);
629 }
630
631 for key in wrapped_meta_map.keys() {
633 invoke_signed(
634 &remove_key(
635 token_program_info.key,
636 wrapped_mint_info.key,
637 wrapped_mint_authority_info.key,
638 key.clone(),
639 false,
640 ),
641 &cpi_accounts,
642 &[&authority_signer_seeds],
643 )?;
644 }
645 } else {
646 invoke_signed(
649 &initialize_token_metadata(
650 token_program_info.key,
651 wrapped_mint_info.key,
652 wrapped_mint_authority_info.key,
653 wrapped_mint_info.key,
654 wrapped_mint_authority_info.key,
655 unwrapped_metadata.name.clone(),
656 unwrapped_metadata.symbol.clone(),
657 unwrapped_metadata.uri.clone(),
658 ),
659 &cpi_accounts,
660 &[&authority_signer_seeds],
661 )?;
662
663 for (key, value) in &unwrapped_metadata.additional_metadata {
665 invoke_signed(
666 &update_field(
667 token_program_info.key,
668 wrapped_mint_info.key,
669 wrapped_mint_authority_info.key,
670 Field::Key(key.clone()),
671 value.clone(),
672 ),
673 &cpi_accounts,
674 &[&authority_signer_seeds],
675 )?;
676 }
677 }
678
679 Ok(())
680}
681
682pub fn process_sync_metadata_to_spl_token(accounts: &[AccountInfo]) -> ProgramResult {
685 let account_info_iter = &mut accounts.iter();
686 let metaplex_metadata_info = next_account_info(account_info_iter)?;
687 let wrapped_mint_authority_info = next_account_info(account_info_iter)?;
688 let wrapped_mint_info = next_account_info(account_info_iter)?;
689 let unwrapped_mint_info = next_account_info(account_info_iter)?;
690 let metaplex_program_info = next_account_info(account_info_iter)?;
691 let system_program_info = next_account_info(account_info_iter)?;
692 let rent_sysvar_info = next_account_info(account_info_iter)?;
693 let source_metadata_info = account_info_iter.next();
694 let owner_program_info = account_info_iter.next();
695
696 if *wrapped_mint_info.owner != spl_token::id() {
699 return Err(TokenWrapError::NoSyncingToToken2022.into());
700 }
701
702 let (expected_wrapped_mint, _) =
703 get_wrapped_mint_address_with_seed(unwrapped_mint_info.key, &spl_token::id());
704 if *wrapped_mint_info.key != expected_wrapped_mint {
705 return Err(TokenWrapError::WrappedMintMismatch.into());
706 }
707
708 let (expected_authority, authority_bump) =
709 get_wrapped_mint_authority_with_seed(wrapped_mint_info.key);
710 if *wrapped_mint_authority_info.key != expected_authority {
711 return Err(TokenWrapError::MintAuthorityMismatch.into());
712 }
713
714 let (expected_metaplex_metadata, _) = MetaplexMetadata::find_pda(wrapped_mint_info.key);
715 if *metaplex_metadata_info.key != expected_metaplex_metadata {
716 return Err(TokenWrapError::MetaplexMetadataMismatch.into());
717 }
718
719 if metaplex_program_info.key != &mpl_token_metadata::ID {
720 return Err(ProgramError::IncorrectProgramId);
721 }
722
723 let unwrapped_metadata = extract_token_metadata(
726 unwrapped_mint_info,
727 source_metadata_info,
728 owner_program_info,
729 )?;
730
731 let authority_bump_seed = [authority_bump];
734 let authority_signer_seeds =
735 get_wrapped_mint_authority_signer_seeds(wrapped_mint_info.key, &authority_bump_seed);
736
737 let new_metadata = token_2022_metadata_to_metaplex(&unwrapped_metadata)?;
738
739 if metaplex_metadata_info.data_is_empty() {
743 let create_ix = CreateMetadataAccountV3 {
744 metadata: *metaplex_metadata_info.key,
745 mint: *wrapped_mint_info.key,
746 mint_authority: *wrapped_mint_authority_info.key,
747 payer: *wrapped_mint_authority_info.key,
748 update_authority: (*wrapped_mint_authority_info.key, true),
749 system_program: *system_program_info.key,
750 rent: Some(*rent_sysvar_info.key),
751 }
752 .instruction(CreateMetadataAccountV3InstructionArgs {
753 data: new_metadata,
754 is_mutable: true,
755 collection_details: None,
756 });
757
758 invoke_signed(
759 &create_ix,
760 &[
761 metaplex_metadata_info.clone(),
762 wrapped_mint_info.clone(),
763 wrapped_mint_authority_info.clone(),
764 system_program_info.clone(),
765 rent_sysvar_info.clone(),
766 ],
767 &[&authority_signer_seeds],
768 )?;
769 } else {
770 let update_ix = UpdateMetadataAccountV2 {
772 metadata: *metaplex_metadata_info.key,
773 update_authority: *wrapped_mint_authority_info.key,
774 }
775 .instruction(UpdateMetadataAccountV2InstructionArgs {
776 data: Some(new_metadata),
777 primary_sale_happened: None,
780 new_update_authority: None,
781 is_mutable: None,
782 });
783 invoke_signed(
784 &update_ix,
785 &[
786 metaplex_metadata_info.clone(),
787 wrapped_mint_authority_info.clone(),
788 ],
789 &[&authority_signer_seeds],
790 )?;
791 }
792
793 Ok(())
794}
795
796pub fn process_instruction(
798 program_id: &Pubkey,
799 accounts: &[AccountInfo],
800 input: &[u8],
801) -> ProgramResult {
802 match TokenWrapInstruction::unpack(input)? {
803 TokenWrapInstruction::CreateMint { idempotent } => {
804 msg!("Instruction: CreateMint");
807 process_create_mint::<DefaultToken2022Customizer>(program_id, accounts, idempotent)
808 }
809 TokenWrapInstruction::Wrap { amount } => {
810 msg!("Instruction: Wrap");
811 process_wrap(accounts, amount)
812 }
813 TokenWrapInstruction::Unwrap { amount } => {
814 msg!("Instruction: Unwrap");
815 process_unwrap(accounts, amount)
816 }
817 TokenWrapInstruction::CloseStuckEscrow => {
818 msg!("Instruction: CloseStuckEscrow");
819 process_close_stuck_escrow(accounts)
820 }
821 TokenWrapInstruction::SyncMetadataToToken2022 => {
822 msg!("Instruction: SyncMetadataToToken2022");
823 process_sync_metadata_to_token_2022(accounts)
824 }
825 TokenWrapInstruction::SyncMetadataToSplToken => {
826 msg!("Instruction: SyncMetadataToSplToken");
827 process_sync_metadata_to_spl_token(accounts)
828 }
829 }
830}