Skip to main content
edited title
Link

InvalidArgument when call function transferSolFromPdatrying send Lamports from PDA account

Source Link

InvalidArgument when call function transferSolFromPda

When I am running an anchor test, I am getting the following error, of invalid arguments when calling function transferSolFromPda. This is my anchor program

use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_instruction;
use anchor_lang::solana_program::program::{invoke, invoke_signed};

declare_id!("CCsXFXn6noPhKG4Y3QPAxsFg6RroKKSd8aRajPT3a8dr");

#[program]
mod anchor_test_lamports {
    use super::*;

    pub fn transfer_sol_from_pda(ctx: Context<TransferSol>, amount: u64, bump: u8) -> Result<()> {
        
        let binding = ctx.accounts.sender.key();
        let pda_signer_seeds: &[&[_]] = &[
            b"pda_seed",
            &binding.as_ref(),
            &[bump],
        ];
        
        let ix = system_instruction::transfer(
            &ctx.accounts.pda_account.key(),
            &ctx.accounts.receiver.key(),
            amount,
        );

        invoke_signed(
            &ix,
            &[
                ctx.accounts.pda_account.to_account_info(),
                ctx.accounts.receiver.to_account_info(),
            ],
            &[pda_signer_seeds],
        )?;

        Ok(())
    }
    pub fn transfer_sol_to_pda(ctx: Context<TransferSolToPda>, amount: u64) -> Result<()> {
        let ix = system_instruction::transfer(
            &ctx.accounts.sender.key(),
            &ctx.accounts.pda_account.key(),
            amount,
        );
        invoke(
            &ix,
            &[
                ctx.accounts.sender.to_account_info(),
                ctx.accounts.pda_account.to_account_info(),
            ],
        )?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct TransferSol<'info> {
    #[account(mut)]
    pub sender: Signer<'info>,
    /// CHECK: This is safe because we are only transferring SOL to this account. No need to deserialize or perform any operations on it.
    #[account(mut)]
    pub pda_account: AccountInfo<'info>,
    /// CHECK: This is safe because we are only transferring SOL to this account. No need to deserialize or perform any operations on it.
    #[account(mut)]
    pub receiver: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct TransferSolToPda<'info> {
    #[account(mut)]
    pub sender: Signer<'info>,
    /// CHECK: This is safe because we are only transferring SOL to this account. No need to deserialize or perform any operations on it.
    #[account(
        init_if_needed,
        payer = sender,
        seeds = [b"pda_seed".as_ref(), sender.key().as_ref()],
        bump,
        space = 50
    )]
    pub pda_account: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}

This is my test

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { AnchorTestLamports } from "../target/types/anchor_test_lamports";
import {
  PublicKey, SystemProgram, LAMPORTS_PER_SOL, Keypair, Connection, Signer

} from "@solana/web3.js";

describe("anchor-test-lamports", () => {
  // Configure the client to use the local cluster.
  anchor.setProvider(anchor.AnchorProvider.env());
  const connection = new Connection("http://localhost:8899", 'recent');

  const program = anchor.workspace.AnchorTestLamports as Program<AnchorTestLamports>;
  let sender;
  let pdaAccount;
  

  it('transfers SOL to PDA', async () => {
    // Generate a new keypair for the test sender account.
    sender = await createUserWithLamports(connection, 10);
    pdaAccount = PublicKey.findProgramAddressSync(
      [Buffer.from("pda_seed"), sender.publicKey.toBuffer()],
      program.programId
    )[0];
    await program.methods
      .transferSolToPda(new anchor.BN(1 * LAMPORTS_PER_SOL))
      .accounts({ pdaAccount: pdaAccount, sender: sender.publicKey, systemProgram: SystemProgram.programId })
      .signers([sender])
      .rpc();
    // Verify the balance of the PDA after the transfer.
    const pdaBalance = await program.provider.connection.getBalance(pdaAccount);
    console.log(`PDA balance: ${pdaBalance / LAMPORTS_PER_SOL} SOL`); 
    // Assert that the PDA received the SOL (this assertion might need adjustment based on the actual balance).
  });

  it('transfer back SOL to PDA', async () => {
    const receiver  = await createUserWithLamports(connection, 10);
    const senderBalance = await program.provider.connection.getBalance(sender.publicKey);

    console.log(`PDA balance: ${senderBalance / LAMPORTS_PER_SOL} SOL`); 

    await program.methods
      .transferSolFromPda(new anchor.BN(0.1 * LAMPORTS_PER_SOL), 255)
      .accounts({ pdaAccount: pdaAccount, sender: sender.publicKey,receiver: receiver.publicKey, systemProgram: SystemProgram.programId })
      .signers([sender])
      .rpc({
        skipPreflight:true
      });
    // Verify the balance of the PDA after the transfer.
    // Assert that the PDA received the SOL (this assertion might need adjustment based on the actual balance).
  });
});


export async function createUserWithLamports(
  connection: Connection,
  lamports: number,
): Promise<Signer> {
  const account = Keypair.generate();
  const signature = await connection.requestAirdrop(
    account.publicKey,
    lamports * LAMPORTS_PER_SOL
  );
  const block = await connection.getLatestBlockhash();
  await connection.confirmTransaction({ ...block, signature });
  return account;
}