5

I am trying to write a program that: a) Creates an account (state) if one does not already exist b) Reallocs as needed for changed state

Error

Error: Account allocation failed: RPC response error -32002: Transaction simulation failed: This account may not be used to pay transaction fees
There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

Code

#[program]
pub mod todo {
    use super::*;

    pub fn create_and_realloc(ctx: Context<CreateAndRealloc>, replacing_tasks: Vec<Task>) -> Result<()> {
        let tasks = &mut ctx.accounts.tasks;

        // If length of tasks is not equal to the length of replacing_tasks, then
        // reallocate the tasks account.
        if tasks.0.len() != replacing_tasks.len() {
            let new_len = 8 + Task::INIT_SPACE * replacing_tasks.len();

            let rent = Rent::get()?;
            let new_minimum_balance = rent.minimum_balance(new_len);

            let tasks_account_info = tasks.to_account_info();

            let user = &ctx.accounts.user;
            let system_program = ctx.accounts.system_program.to_account_info();

            let lamports_diff = new_minimum_balance.saturating_sub(tasks_account_info.lamports());

            // The below commented-out has the same error.
            // anchor_lang::solana_program::program::invoke_signed(
            //     &anchor_lang::solana_program::system_instruction::transfer(
            //         user.key,
            //         tasks_account_info.key,
            //         lamports_diff,
            //     ),
            //     &[
            //         user.to_account_info().clone(),
            //         tasks_account_info.clone(),
            //         system_program.clone(),
            //     ],
            //     &[&[b"tasks"]],
            // )?;

            let cpi_context = CpiContext::new(
                system_program,
                anchor_lang::system_program::Transfer {
                    from: user.to_account_info().clone(),
                    to: tasks_account_info.clone(),
                },
            );
            anchor_lang::system_program::transfer(cpi_context, lamports_diff)?;

            tasks_account_info.realloc(new_len, true)?;
        }

        tasks.0 = replacing_tasks;

        Ok(())
    }
}

#[derive(Accounts)]
#[instruction(replacing_tasks: Vec<Task>)]
pub struct CreateAndRealloc<'info> {
    #[account(init_if_needed, space = 8 + replacing_tasks.len() * Task::INIT_SPACE, payer = user, seeds = [b"tasks"], bump)]
    pub tasks: Account<'info, Tasks>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
#[derive(Debug)]
pub struct Tasks(Vec<Task>);

#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace)]
pub struct Task {
    pub id: u32,
    /// The name of the task truncated to 32 characters.
    #[max_len(32)]
    pub name: String,
    pub completed: bool,
}

What I originally wanted to do was mark the tasks PDA as init_if_needed, mut, and realloc, but those are incompatible.


Question

How can one (within one instruction handler) init a PDA if needed, transfer lamports to the account, and realloc its size?

2 Answers 2

5

i think what you are looking for is this:

#[derive(Accounts)]
#[instruction(len: u16)]
pub struct IncreaseAccoutSize<'info> {

#[account(mut, 
    realloc = len as usize, 
    realloc::zero = true, 
    realloc::payer=signer)]
    pub data_holder: Account<'info, DataHolderNoZeroCopy>,
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(address = system_program::ID)]
    pub system_program: Program<'info, System>,
}

Here is an example with different ways to work with big accounts: https://github.com/solana-developers/anchor-zero-copy-example/blob/main/programs/zero-copy/src/lib.rs

The error you are getting indicates that you are using a PDA where there should be a system program account. Try substracting and adding lamports like here: beta.solpg.io/tutorials/tiny-adventure-two line 60

4
  • 1
    Is there no way to init the account and realloc in the same instruction handler?
    – Sky020
    Aug 14, 2023 at 12:23
  • The error you are getting indicates that some account is not owned by the system program i think. Could you try running it with skipPreflight = true and look at the onchain error?
    – Jonas H.
    Aug 14, 2023 at 14:24
  • I think you can not use anchor_lang::system_program::Transfer for a non system program account, try changing lamports direclty like here: beta.solpg.io/tutorials/tiny-adventure-two line 60
    – Jonas H.
    Aug 14, 2023 at 14:27
  • 1
    Thank you.I really dislike marking my own answers as the answer, and your replies did lead me to the answer.
    – Sky020
    Aug 14, 2023 at 20:27
2

Turns out the issue was not restarting the test validator with --reset. All of the following work:

// 1 - Explicit CPI
let cpi_context = CpiContext::new(
    system_program,
    anchor_lang::system_program::Transfer {
        from: user.to_account_info().clone(),
        to: tasks_account_info.clone(),
    },
);
anchor_lang::system_program::transfer(cpi_context, lamports_diff)?;
// 2 - invoke
anchor_lang::solana_program::program::invoke(
    &anchor_lang::solana_program::system_instruction::transfer(
        user.key,
        tasks_account_info.key,
        lamports_diff,
    ),
    &[
        user.to_account_info().clone(),
        tasks_account_info.clone(),
        system_program.clone(),
    ],
)?;
// 3 - Lamport mutation
let lamports_diff = new_minimum_balance.saturating_sub(tasks_account_info.lamports());
**ctx
    .accounts
    .user
    .to_account_info()
    .try_borrow_mut_lamports()? -= lamports_diff;
**tasks_account_info.try_borrow_mut_lamports()? += lamports_diff;

I found this out because after changing my test to use skipPreflight: true, the same error appeared, and it did not even try to run the instruction.

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.