Is it possible to freeze a usdc token account of user? Au gave code example but u am not sure if it works

Is it possible to freeze a usdc token account of user? Au gave code example but u am not sure if it works

Manage alerts

Loading saved threads...

Yellow Wolf · External communityPost link
External question — Solana Stack Exchange Author: Yellow Wolf Original post: https://solana.stackexchange.com/questions/24507 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. so I tried to make a demo game where it uses USDC to be freeze as the time being transferred out, I wasn’t sure if this was possible, so I asked AI. Please check if the code is correct- use anchor_lang::prelude::*; use anchor_spl::token_interface::{ self, Mint, TokenAccount, TokenInterface, TransferChecked, FreezeAccount, ThawAccount, SetAuthority, spl_token_2022::instruction::AuthorityType }; declare_id!("YourGameProgramID11111111111111111111111"); /// 📌 GLOBAL CONSTANTS pub mod constants { use anchor_lang::prelude::Pubkey; use anchor_lang::pubkey; /// Mainnet Native USDC Mint Address (Legacy SPL) /// (If you are testing on devnet, swap this out for your devnet USDC mint address) pub const USDC_MINT_ADDRESS: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); /// The ONLY off-chain backend server keypair allowed to resolve losses pub const SERVER_OPERATOR_ADDRESS: Pubkey = pubkey!("3xxxxSERVERxxxOPERATORxxxDUMMYxxxADDRESSxxxx"); } #[program] pub mod betting_game_complete { use super::*; /// 1. USER DEPOSITS & SECURES THE BET /// User calls this function to lock up a specified amount of USDC to play. pub fn deposit_and_lock_bet(ctx: Context<DepositAndLockBet>, amount: u64) -> Result<()> { let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Move USDC from user's primary wallet into their custom Auxiliary Game Account let transfer_cpi = TransferChecked { from: ctx.accounts.user_main_usdc.to_account_info(), to: ctx.accounts.auxiliary_game_account.to_account_info(), authority: ctx.accounts.user.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), amount, ctx.accounts.usdc_mint.decimals )?; // Step B: Set Program PDA as the FREEZE AUTHORITY let set_freeze_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.user.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), set_freeze_cpi), AuthorityType::FreezeAccount, Some(ctx.accounts.game_signer_pda.key()), )?; // Step C: Set Program PDA as the CLOSE AUTHORITY let set_close_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.user.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), set_close_cpi), AuthorityType::CloseAccount, Some(ctx.accounts.game_signer_pda.key()), )?; // Step D: Freeze the account instantly let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let freeze_cpi = FreezeAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::freeze_account(CpiContext::new_with_signatures(cpi_program, freeze_cpi, signer_seeds))?; Ok(()) } /// 2. SERVER RESOLVES A PARTIAL LOSS (e.g., extracts 3 USDC out of 10) /// Only your authorized backend server can call this instruction. pub fn resolve_partial_loss(ctx: Context<ResolvePartialLoss>, loss_amount: u64) -> Result<()> { let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Server PDA Thaws the account temporarily let thaw_cpi = ThawAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::thaw_account(CpiContext::new_with_signatures(cpi_program.clone(), thaw_cpi, signer_seeds))?; // Step B: Unilaterally shift ownership to the Main Pool temporarily to execute transfer let set_owner_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::set_authority( CpiContext::new_with_signatures(cpi_program.clone(), set_owner_cpi, signer_seeds), AuthorityType::AccountOwner, Some(ctx.accounts.main_pool_usdc.key()), )?; // Step C: Transfer exactly the `loss_amount` (e.g., 3 USDC) to the house pool let transfer_cpi = TransferChecked { from: ctx.accounts.auxiliary_game_account.to_account_info(), to: ctx.accounts.main_pool_usdc.to_account_info(), authority: ctx.accounts.main_pool_usdc.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), loss_amount, ctx.accounts.usdc_mint.decimals )?; // Step D: Re-assign ownership back to the original User so it stays in their wallet balance let return_owner_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.main_pool_usdc.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), return_owner_cpi), AuthorityType::AccountOwner, Some(ctx.accounts.user_wallet.key()), )?; // Step E: Refreeze the account immediately to secure the remaining tokens let freeze_cpi = FreezeAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::freeze_account(CpiContext::new_with_signatures(cpi_program, freeze_cpi, signer_seeds))?; Ok(()) } /// 3. USER WITHDRAWS REMAINING BALANCE /// User signs this when they want to check out. It clears the position entirely. pub fn thaw_and_withdraw(ctx: Context<ThawAndWithdraw>) -> Result<()> { let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Thaw the account using the Program PDA's authority let thaw_cpi = ThawAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::thaw_account(CpiContext::new_with_signatures(cpi_program.clone(), thaw_cpi, signer_seeds))?; // Step B: Transfer ALL remaining locked USDC back to the user's primary wallet ATA let remaining_balance = ctx.accounts.auxiliary_game_account.amount; let transfer_cpi = TransferChecked { from: ctx.accounts.auxiliary_game_account.to_account_info(), to: ctx.accounts.user_main_usdc.to_account_info(), authority: ctx.accounts.user.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), remaining_balance, ctx.accounts.usdc_mint.decimals )?; // Step C: Close the empty auxiliary account completely using Close Authority to return rent to user let close_cpi = token_interface::CloseAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), destination: ctx.accounts.user.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::close_account(CpiContext::new_with_signatures(cpi_program, close_cpi, signer_seeds))?; Ok(()) } } /// 📋 STRUCT 1: DEPOSIT ACCOUNTS #[derive(Accounts)] pub struct DepositAndLockBet<'info> { #[account(mut)] pub user: Signer<'info>, #[account( constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS @ ErrorCode::InvalidMint )] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account(mut)] pub user_main_usdc: InterfaceAccount<'info, TokenAccount>, #[account( init, payer = user, seeds = [b"game_bet", user.key().as_ref()], bump, token::mint = usdc_mint, token::authority = user, token::token_program = token_program, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(seeds = [b"game_signer"], bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } /// 📋 STRUCT 2: RESOLVE LOSS ACCOUNTS (SERVER OPERATOR EXCLUSIVE) #[derive(Accounts)] pub struct ResolvePartialLoss<'info> { #[account( signer, constraint = server_operator.key() == constants::SERVER_OPERATOR_ADDRESS @ ErrorCode::UnauthorizedServer )] pub server_operator: Signer<'info>, #[account(mut)] /// CHECK: The target user address pub user_wallet: AccountInfo<'info>, #[account(constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS)] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account( mut, seeds = [b"game_bet", user_wallet.key().as_ref()], bump, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(mut, constraint = main_pool_usdc.owner == server_operator.key())] pub main_pool_usdc: InterfaceAccount<'info, TokenAccount>, #[account(seeds = [b"game_signer"], bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, } /// 📋 STRUCT 3: WITHDRAW ACCOUNTS #[derive(Accounts)] pub struct ThawAndWithdraw<'info> { #[account(mut)] pub user: Signer<'info>, #[account(constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS)] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account(mut)] pub user_main_usdc: InterfaceAccount<'info, TokenAccount>, #[account( mut, seeds = [b"game_bet", user.key().as_ref()], bump, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(seeds = b"game_signer", bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, } /// 🚨 PROGRAM ERRORS #[error_code] pub enum ErrorCode { #[msg("The token mint provided does not match the allowed USDC address.")] InvalidMint, #[msg("This instruction can only be signed by the official server wallet operator.")] UnauthorizedServer, }
Quote
Report

Post Reply

Quoted from Forex.com.bd-Editorial External question — Solana Stack Exchange Author: Yellow Wolf Source score (net votes, not local likes): 0 Original post: https://solana.stackexchange.com/questions/24507 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. so I tried to make a demo game where it uses USDC to be freeze as the time being transferred out, I wasn’t sure if this was possible, so I asked AI. Please check if the code is correct- use anchor_lang::prelude::*; use anchor_spl::token_interface::{ self, Mint, TokenAccount, TokenInterface, TransferChecked, FreezeAccount, ThawAccount, SetAuthority, spl_token_2022::instruction::AuthorityType }; declare_id!("YourGameProgramID11111111111111111111111"); /// 📌 GLOBAL CONSTANTS pub mod constants { use anchor_lang::prelude::Pubkey; use anchor_lang::pubkey; /// Mainnet Native USDC Mint Address (Legacy SPL) /// (If you are testing on devnet, swap this out for your devnet USDC mint address) pub const USDC_MINT_ADDRESS: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); /// The ONLY off-chain backend server keypair allowed to resolve losses pub const SERVER_OPERATOR_ADDRESS: Pubkey = pubkey!("3xxxxSERVERxxxOPERATORxxxDUMMYxxxADDRESSxxxx"); } #[program] pub mod betting_game_complete { use super::*; /// 1. USER DEPOSITS & SECURES THE BET /// User calls this function to lock up a specified amount of USDC to play. pub fn deposit_and_lock_bet(ctx: Context<DepositAndLockBet>, amount: u64) -> Result<()> { let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Move USDC from user's primary wallet into their custom Auxiliary Game Account let transfer_cpi = TransferChecked { from: ctx.accounts.user_main_usdc.to_account_info(), to: ctx.accounts.auxiliary_game_account.to_account_info(), authority: ctx.accounts.user.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), amount, ctx.accounts.usdc_mint.decimals )?; // Step B: Set Program PDA as the FREEZE AUTHORITY let set_freeze_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.user.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), set_freeze_cpi), AuthorityType::FreezeAccount, Some(ctx.accounts.game_signer_pda.key()), )?; // Step C: Set Program PDA as the CLOSE AUTHORITY let set_close_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.user.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), set_close_cpi), AuthorityType::CloseAccount, Some(ctx.accounts.game_signer_pda.key()), )?; // Step D: Freeze the account instantly let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let freeze_cpi = FreezeAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::freeze_account(CpiContext::new_with_signatures(cpi_program, freeze_cpi, signer_seeds))?; Ok(()) } /// 2. SERVER RESOLVES A PARTIAL LOSS (e.g., extracts 3 USDC out of 10) /// Only your authorized backend server can call this instruction. pub fn resolve_partial_loss(ctx: Context<ResolvePartialLoss>, loss_amount: u64) -> Result<()> { let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Server PDA Thaws the account temporarily let thaw_cpi = ThawAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::thaw_account(CpiContext::new_with_signatures(cpi_program.clone(), thaw_cpi, signer_seeds))?; // Step B: Unilaterally shift ownership to the Main Pool temporarily to execute transfer let set_owner_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::set_authority( CpiContext::new_with_signatures(cpi_program.clone(), set_owner_cpi, signer_seeds), AuthorityType::AccountOwner, Some(ctx.accounts.main_pool_usdc.key()), )?; // Step C: Transfer exactly the `loss_amount` (e.g., 3 USDC) to the house pool let transfer_cpi = TransferChecked { from: ctx.accounts.auxiliary_game_account.to_account_info(), to: ctx.accounts.main_pool_usdc.to_account_info(), authority: ctx.accounts.main_pool_usdc.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), loss_amount, ctx.accounts.usdc_mint.decimals )?; // Step D: Re-assign ownership back to the original User so it stays in their wallet balance let return_owner_cpi = SetAuthority { account_or_mint: ctx.accounts.auxiliary_game_account.to_account_info(), current_authority: ctx.accounts.main_pool_usdc.to_account_info(), }; token_interface::set_authority( CpiContext::new(cpi_program.clone(), return_owner_cpi), AuthorityType::AccountOwner, Some(ctx.accounts.user_wallet.key()), )?; // Step E: Refreeze the account immediately to secure the remaining tokens let freeze_cpi = FreezeAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::freeze_account(CpiContext::new_with_signatures(cpi_program, freeze_cpi, signer_seeds))?; Ok(()) } /// 3. USER WITHDRAWS REMAINING BALANCE /// User signs this when they want to check out. It clears the position entirely. pub fn thaw_and_withdraw(ctx: Context<ThawAndWithdraw>) -> Result<()> { let bump = ctx.bumps.game_signer_pda; let signer_seeds: &[&[&[u8]]] = &[&[b"game_signer", &[bump]]]; let cpi_program = ctx.accounts.token_program.to_account_info(); // Step A: Thaw the account using the Program PDA's authority let thaw_cpi = ThawAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::thaw_account(CpiContext::new_with_signatures(cpi_program.clone(), thaw_cpi, signer_seeds))?; // Step B: Transfer ALL remaining locked USDC back to the user's primary wallet ATA let remaining_balance = ctx.accounts.auxiliary_game_account.amount; let transfer_cpi = TransferChecked { from: ctx.accounts.auxiliary_game_account.to_account_info(), to: ctx.accounts.user_main_usdc.to_account_info(), authority: ctx.accounts.user.to_account_info(), mint: ctx.accounts.usdc_mint.to_account_info(), }; token_interface::transfer_checked( CpiContext::new(cpi_program.clone(), transfer_cpi), remaining_balance, ctx.accounts.usdc_mint.decimals )?; // Step C: Close the empty auxiliary account completely using Close Authority to return rent to user let close_cpi = token_interface::CloseAccount { account: ctx.accounts.auxiliary_game_account.to_account_info(), destination: ctx.accounts.user.to_account_info(), authority: ctx.accounts.game_signer_pda.to_account_info(), }; token_interface::close_account(CpiContext::new_with_signatures(cpi_program, close_cpi, signer_seeds))?; Ok(()) } } /// 📋 STRUCT 1: DEPOSIT ACCOUNTS #[derive(Accounts)] pub struct DepositAndLockBet<'info> { #[account(mut)] pub user: Signer<'info>, #[account( constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS @ ErrorCode::InvalidMint )] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account(mut)] pub user_main_usdc: InterfaceAccount<'info, TokenAccount>, #[account( init, payer = user, seeds = [b"game_bet", user.key().as_ref()], bump, token::mint = usdc_mint, token::authority = user, token::token_program = token_program, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(seeds = [b"game_signer"], bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } /// 📋 STRUCT 2: RESOLVE LOSS ACCOUNTS (SERVER OPERATOR EXCLUSIVE) #[derive(Accounts)] pub struct ResolvePartialLoss<'info> { #[account( signer, constraint = server_operator.key() == constants::SERVER_OPERATOR_ADDRESS @ ErrorCode::UnauthorizedServer )] pub server_operator: Signer<'info>, #[account(mut)] /// CHECK: The target user address pub user_wallet: AccountInfo<'info>, #[account(constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS)] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account( mut, seeds = [b"game_bet", user_wallet.key().as_ref()], bump, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(mut, constraint = main_pool_usdc.owner == server_operator.key())] pub main_pool_usdc: InterfaceAccount<'info, TokenAccount>, #[account(seeds = [b"game_signer"], bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, } /// 📋 STRUCT 3: WITHDRAW ACCOUNTS #[derive(Accounts)] pub struct ThawAndWithdraw<'info> { #[account(mut)] pub user: Signer<'info>, #[account(constraint = usdc_mint.key() == constants::USDC_MINT_ADDRESS)] pub usdc_mint: InterfaceAccount<'info, Mint>, #[account(mut)] pub user_main_usdc: InterfaceAccount<'info, TokenAccount>, #[account( mut, seeds = [b"game_bet", user.key().as_ref()], bump, )] pub auxiliary_game_account: InterfaceAccount<'info, TokenAccount>, #[account(seeds = b"game_signer", bump)] pub game_signer_pda: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, } /// 🚨 PROGRAM ERRORS #[error_code] pub enum ErrorCode { #[msg("The token mint provided does not match the allowed USDC address.")] InvalidMint, #[msg("This instruction can only be signed by the official server wallet operator.")] UnauthorizedServer, }

Cancel quote

Checking account access…