I was following the Token Vault tutorial on Solana Playground. It compiled fine on SolPG but when I compile this program on my system, I get the following error about unsafe account. I want to make this account safe and not just mute the error by adding the comment. Please guide. Thank you.
Error
Struct field "token_account_owner_pda" is unsafe, but is not documented.
Please add a `/// CHECK:` doc comment explaining why no checks through types are necessary.
See https://www.anchor-lang.com/docs/the-accounts-struct#safety-checks for more information.
Program
use anchor_lang::prelude::*;
use anchor_spl::token::{transfer, Mint, Token, TokenAccount, Transfer};
// This is your program's public key and it will update
// automatically when you build the project.
declare_id!("HB5oC3fNVdJji92n1iaQBXVQiccRBvHz7e5jiMnLT4F8");
#[program]
mod token_vault {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
pub fn stake(ctx: Context<Stake>, amount: u64) -> Result<()> {
msg!("Stake amount: {}!", amount);
let stake_info = &mut ctx.accounts.stake_info;
stake_info.amount = amount;
stake_info.staker = ctx.accounts.signer.key();
let transfer_instruction = Transfer {
from: ctx.accounts.sender_token_account.to_account_info(),
to: ctx.accounts.vault_token_account.to_account_info(),
authority: ctx.accounts.signer.to_account_info(),
};
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
transfer_instruction,
);
transfer(cpi_ctx, amount)?;
Ok(())
}
pub fn destake(ctx: Context<DeStake>, amount: u64) -> Result<()> {
msg!("Destake amount: {}!", amount);
let stake_info = &mut ctx.accounts.stake_info;
if amount == 0 {
return Err(error!(ErrorCode::AmountZero));
}
if amount > stake_info.amount {
return Err(error!(ErrorCode::WithdrawAmountGreater));
}
if stake_info.amount == 0 {
return Err(error!(ErrorCode::StakeAmountZero));
}
let updated_amount = stake_info.amount.checked_sub(amount).unwrap();
ctx.accounts.stake_info.amount = updated_amount;
let transfer_instruction = Transfer {
from: ctx.accounts.vault_token_account.to_account_info(),
to: ctx.accounts.sender_token_account.to_account_info(),
authority: ctx.accounts.token_account_owner_pda.to_account_info(),
};
let bump = ctx.bumps.token_account_owner_pda;
let seeds = &[b"token_account_owner_pda".as_ref(), &[bump]];
let signer = &[&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
transfer_instruction,
signer,
);
transfer(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
// Derived PDAs
#[account(
init_if_needed,
payer = signer,
seeds=[b"token_account_owner_pda"],
bump,
space = 8
)]
token_account_owner_pda: AccountInfo<'info>,
#[account(
init_if_needed,
payer = signer,
seeds=[b"token_vault", mint_of_token_being_sent.key().as_ref()],
token::mint=mint_of_token_being_sent,
token::authority=token_account_owner_pda,
bump
)]
vault_token_account: Account<'info, TokenAccount>,
mint_of_token_being_sent: Account<'info, Mint>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
token_program: Program<'info, Token>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
pub struct Stake<'info> {
// Derived PDAs
#[account(
init_if_needed,
payer = signer,
seeds=[b"stake_info", signer.key().as_ref()],
bump,
space = 8 + StakeInfo::INIT_SPACE
)]
pub stake_info: Account<'info, StakeInfo>,
#[account(mut,
seeds=[b"token_account_owner_pda"],
bump
)]
token_account_owner_pda: AccountInfo<'info>,
#[account(mut,
seeds=[b"token_vault", mint_of_token_being_sent.key().as_ref()],
bump,
token::mint=mint_of_token_being_sent,
token::authority=token_account_owner_pda,
)]
vault_token_account: Account<'info, TokenAccount>,
#[account(mut)]
sender_token_account: Account<'info, TokenAccount>,
mint_of_token_being_sent: Account<'info, Mint>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
token_program: Program<'info, Token>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
pub struct DeStake<'info> {
// Derived PDAs
#[account(
mut,
seeds=[b"stake_info", signer.key().as_ref()],
bump,
)]
pub stake_info: Account<'info, StakeInfo>,
#[account(mut,
seeds=[b"token_account_owner_pda"],
bump
)]
token_account_owner_pda: AccountInfo<'info>,
#[account(mut,
seeds=[b"token_vault", mint_of_token_being_sent.key().as_ref()],
bump,
token::mint=mint_of_token_being_sent,
token::authority=token_account_owner_pda,
)]
vault_token_account: Account<'info, TokenAccount>,
#[account(mut)]
sender_token_account: Account<'info, TokenAccount>,
mint_of_token_being_sent: Account<'info, Mint>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
token_program: Program<'info, Token>,
rent: Sysvar<'info, Rent>,
}
#[account]
#[derive(InitSpace)]
pub struct StakeInfo {
pub staker: Pubkey,
pub amount: u64,
}
#[error_code]
pub enum ErrorCode {
#[msg("Amount must be greater than zero")]
AmountZero,
#[msg("Withdraw amount must be less than the deposit")]
WithdrawAmountGreater,
#[msg("Deposit amount is zero")]
StakeAmountZero,
}