6

Anchor docs show how to create PDA accounts via the account validation struct:

// validation struct
#[derive(Accounts)]
pub struct CreateUserStats<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    // space: 8 discriminator + 2 level + 4 name length + 200 name + 1 bump
    #[account(
        init,
        payer = user,
        space = 8 + 2 + 4 + 200 + 1, seeds = [b"user-stats", user.key().as_ref()], bump
    )]
    pub user_stats: Account<'info, UserStats>,
    pub system_program: Program<'info, System>,

But how can we create accounts dynamically, only when they don't exist for instance. I have seen an example using accounts.system_program.create_account():

/// pseudo code
fn initialize(accounts) {
    accounts.system_program.create_account(accounts.payer, accounts.counter);
    let counter = deserialize(accounts.counter);
    counter.count = 0;
}

but I don't know if there is something like this for creating PDA accounts (including seeds and bump).

So in summary:

  1. How can we check if a PDA account already exist within a function (not using struct).
  2. How can we create a PDA account within a function (not using struct).
  3. Is there a way to create a PDA account using struct only if doesn't exist already?

2 Answers 2

6

The best way to see what Anchor is currently doing is to use anchor expand to see the expanded macros. Then you can pull that logic inside your own function.

  1. How can we check if a PDA account already exist within a function (not using struct).

I believe Anchor just checks the account balance, e.g. my_account.lamports().

Please note even if the account does exist, Anchor does a few extra things:

  • Ensures the account size matches the requested space, even allocating extra space via CPI if required
  • Ensures there is enough balance for the requested account size, even transferring lamports from payer via CPI, if it is short
  • Ensures the right program is assigned to the account, even assigning owner via CPI if required
  1. How can we create a PDA account within a function (not using struct).

This is how Anchor does it under the hood:

let (__pda_address, __bump) = Pubkey::find_program_address(
    &[<seeds>],
    program_id,
);

let cpi_accounts = anchor_lang::system_program::CreateAccount {
    from: payer.to_account_info(),
    to: my_account.to_account_info(),
};
let cpi_context = anchor_lang::context::CpiContext::new(
    system_program.to_account_info(),
    cpi_accounts,
);
anchor_lang::system_program::create_account(
    cpi_context
        .with_signer(
            &[
                &[
                    <seeds>,
                    &[__bump][..],
                ][..],
            ],
        ),
    lamports,
    space as u64,
    program_id,
)?;
  1. Is there a way to create a PDA account using struct only if doesn't exist already?

You can use init_if_needed in your Accounts struct to instruct Anchor to create an account if only it does not already exist.

This feature is susceptible to re-initialisation attacks so it is not enabled by default. You can enable it in your Cargo.toml by adding init-if-needed feature to anchor-lang.

4
  • Re: your answer to the third question, there is init_if_needed. docs.rs/anchor-lang/latest/anchor_lang/…
    – HelmetFace
    Commented Oct 20, 2022 at 4:05
  • 1
    Hmm not sure what I was smoking to have forgotten about that. I've updated my answer. Thanks!
    – sohrab
    Commented Oct 24, 2022 at 0:56
  • What do you mean by Anchor expand @sohrab ? Is that a package? an option or command within VSCode editor? Commented Oct 24, 2022 at 4:53
  • It's part of the CLI: anchor-lang.com/docs/cli#expand I'll add the link to the answer.
    – sohrab
    Commented Oct 24, 2022 at 6:08
0

It's best to have a separate functions for

  • initializing accounts / PDAs
  • using those accounts / PDAs

Then typically you leave it up to your front end / client code to check whether or not it needs to include an initialization function, based on whether an account already exists or not.

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.