9

There are some references about UncheckedAccount in the Anchor book, but documentation is scarce and is not clear when they should be used.

Could someone provide some real use cases where this kind of accounts could be useful? Which are the potential risks of using them?

3 Answers 3

9

Every Instruction receives Accounts as arguments, most accounts are passed in with Account<'info, Type> or Program<'info, ProgramType> in the anchor framework. This is because anchor expects you to do some checks on every account you pass in, if these checks are not properly done, you are at risk of a security flaw. Account<'info, Type> checks that the Account passed in is a program-owned account of the type Type. Type can be your custom type in your custom program or it can be a type of another program like Mint or TokenAccount. Program<'info, ProgramType> checks that the Account passed in is a program account(executable is set to true). ProgramType is usually used for System or Token or when performing cpi calls.

UncheckedAccount just means that you are not performing any implicit checks on that account by anchor. It is the same as AccountInfo but it was named UncheckedAccount to explicitly signify that you are aware you are blindly trusting this account. It should be avoided whenever possible as it can lead to security flaws easily. However, you can use it for a simple SOL transfer.

pub SendSol<'info>
{
   #[account(mut)]
   pub from: Signer<'info>, // UncheckedAccount can be used here but Signer checks that `from` signs the transaction

   #[account(mut)]
   pub to: SystemAccount<'info>, // UncheckedAccount or AccountInfo can also be used instead but SystemAccount is used to ensure it is owned by the System Program

   pub system_program: Program<'info, System>, // UncheckedAccount can also be used here but Program checks that this Account is executable and owned by System

}

pub fn send_sol(ctx: Context<SendSol>, amount: u64) -> Result<()>
{
        let sol_transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
            ctx.accounts.from.key,  
            ctx.accounts.to.key, 
            amount
        );

    invoke(
        &sol_transfer_ix, 
        &[
            ctx.accounts.from.to_account_info(),
            ctx.accounts.to.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ]
    )?;

}

3
  • 2
    In the example that you proposed, instead of UncheckedAccount, you should use SystemAccount. In most cases SystemAccount can be used instead of UncheckedAccount.
    – Void
    Commented Oct 13, 2022 at 7:05
  • Thank you for this. This is my first time I am hearing of SystemAccount Commented Oct 13, 2022 at 14:55
  • 1
    Seems worth updating the answer possibly to reflect this
    – Henry E
    Commented Oct 13, 2022 at 19:32
1

The simple answer is that you're never supposed to use unchecked_account.

The more complicated answer is that sometimes you're forced to use it because the account you're accessing doesn't conform to any of the standard account types that anchor has, like SystemAccount, Signer, or Account. Usually this is the case if you're trying to load an account from a program that doesn't use anchor but even in those scenarios you can actually manually implement your own custom Account<'info, CustomAccount> type that handles deserializing and checking the program owner of the account automatically.

So if you're really thorough there's no excuse for using unchecked_account but in reality sometimes you end up using it if you're feeling too lazy to implement a custom type.

1

Maybe to give context to the other answers, try to avoid UncheckAccount/AccountInfo where possible. However, there may be situations where this is unavoidable.

CPI's are a possible scenario where you may end up needing to use these two types. Because of rust's package manager, cyclic imports are not allowed. What this means for smart contracts on solana is: If one smart contract (A) wants to be able to make cpi calls to another program (B), They likely take the other program in via a Program<'info, ProgramB>

For this to be recognized, they also have to import the other program's crate/library.

If the other program (B) wants to further ensure that what calls it, is indeed program A - it can not import program A and verify via Program<'info, ProgramA>

because this would lead to a cyclic import cycle

The only way (at time of writing this) would be via AccountInfo or UncheckedAccount and add the

#[account(executable)]

macro check on it in Program B

#[account(executable, address = Pubkey::new([program a's pubkey]))]
pub program_a: UncheckedAccount<'info>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.