I am trying to make a SOL transfer from the bank account into the client account, but the operation fails with the following error:
Cross-program invocation with unauthorized signer or writable account
which is strange because all involved accounts are writable and a program derived address (PDA) should be able to sign the instruction (If I understood the docs correctly).
This is my code:
#[derive(Accounts)]
pub struct ClaimPayment<'info> {
#[account(mut)]
pub client: Signer<'info>,
#[account(mut)]
pub bank: Account<'info, Bank>,
#[account(
mut,
seeds = [client.key().as_ref(), bank.key().as_ref()],
bump = client_pda.bump
)]
pub client_pda: Account<'info, ClientPDA>,
pub system_program: Program<'info, System>,
}
pub fn claim_payment(ctx: Context<ClaimPayment>, payment: u64) -> Result<()> {
let seeds = &[
ctx.accounts.client.to_account_info().key.as_ref(),
ctx.accounts.bank.to_account_info().key.as_ref(),
&[ctx.accounts.client_pda.bump],
];
let signer_seeds = &[&seeds[..]];
let transfer_cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.bank.to_account_info(),
to: ctx.accounts.client.to_account_info(),
},
)
.with_signer(signer_seeds);
system_program::transfer(transfer_cpi_context, payment)?;
let client_pda = &mut ctx.accounts.client_pda;
client_pda.paid = true;
let bank = &mut ctx.accounts.bank;
bank.payments += 1;
Ok(())
}
And the Javascript for testing the method:
let [clientPda, bump] = await PublicKey.findProgramAddress([provider.publicKey.toBuffer(), BANK_KEYPAIR.publicKey.toBuffer()], program.programId);
await program.methods
.claimPayment(1_000_000_000)
.accounts({
client: provider.publicKey,
bank: BANK_KEYPAIR.publicKey,
clientPda: clientPda,
})
.rpc();
Somebody knows which could be the problem? Would it make any difference if I use a PDA for bank
instead of a regular account?