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?