use anchor_lang::{
prelude::*,
system_program::{transfer, Transfer},
};
use anchor_spl::token::{Mint, Token, TokenAccount};
use mpl_token_metadata::instruction::thaw_delegated_account;
use solana_program::program::{invoke, invoke_signed};
use spl_token::instruction::revoke;
use crate::{cmp_pubkeys, CandyError, CandyMachine, FreezePDA};
#[derive(Accounts)]
pub struct ThawNFT<'info> {
#[account(mut, seeds = [FreezePDA::PREFIX.as_bytes(), candy_machine.key().as_ref()], bump, has_one = candy_machine)]
freeze_pda: Account<'info, FreezePDA>,
#[account(mut)]
candy_machine: UncheckedAccount<'info>,
#[account(mut, has_one = mint, has_one = owner)]
token_account: Account<'info, TokenAccount>,
owner: UncheckedAccount<'info>,
mint: Account<'info, Mint>,
edition: UncheckedAccount<'info>,
#[account(mut)]
payer: Signer<'info>,
token_program: Program<'info, Token>,
#[account(address = mpl_token_metadata::id())]
token_metadata_program: UncheckedAccount<'info>,
system_program: Program<'info, System>,
}
pub fn handle_thaw_nft(ctx: Context<ThawNFT>) -> Result<()> {
let freeze_pda = &mut ctx.accounts.freeze_pda;
let candy_machine = &mut ctx.accounts.candy_machine;
let current_timestamp = Clock::get()?.unix_timestamp;
let can_thaw = if candy_machine.data_is_empty() {
true
} else {
let candy_struct: Account<CandyMachine> =
Account::try_from(&candy_machine.to_account_info())?;
freeze_pda.thaw_eligible(current_timestamp, &candy_struct)
};
msg!("Can thaw: {}", can_thaw);
if !can_thaw {
return err!(CandyError::InvalidThawNft);
}
let token_account = &ctx.accounts.token_account;
let mint = &ctx.accounts.mint;
let edition = &ctx.accounts.edition;
let payer = &ctx.accounts.payer;
let owner = &ctx.accounts.owner;
let token_program = &ctx.accounts.token_program;
let token_metadata_program = &ctx.accounts.token_metadata_program;
let freeze_seeds = [
FreezePDA::PREFIX.as_bytes(),
candy_machine.key.as_ref(),
&[*ctx.bumps.get("freeze_pda").unwrap()],
];
if token_account.is_frozen() {
msg!("Token account is frozen! Now attempting to thaw!");
invoke_signed(
&thaw_delegated_account(
mpl_token_metadata::ID,
freeze_pda.key(),
token_account.key(),
edition.key(),
mint.key(),
),
&[
freeze_pda.to_account_info(),
token_account.to_account_info(),
edition.to_account_info(),
mint.to_account_info(),
token_program.to_account_info(),
token_metadata_program.to_account_info(),
],
&[&freeze_seeds],
)?;
if freeze_pda.freeze_fee > 0 && freeze_pda.frozen_count > 0 {
transfer(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
Transfer {
from: freeze_pda.to_account_info(),
to: payer.to_account_info(),
},
),
freeze_pda.freeze_fee,
)?;
}
freeze_pda.frozen_count = freeze_pda.frozen_count.saturating_sub(1);
} else {
msg!("Token account is not frozen!");
}
if cmp_pubkeys(&payer.key(), &owner.key()) {
msg!("Revoking authority");
invoke(
&revoke(&spl_token::ID, &token_account.key(), &payer.key(), &[])?,
&[token_account.to_account_info(), payer.to_account_info()],
)?;
} else {
msg!("Cannot revoke delegate authority: token account owner is not signer. Re-run as owner to revoke or just call revoke manually.");
}
Ok(())
}