4

I'm trying to write a code that transfers SOL from a vault(A PDA) to another address and my code looks like this

 pub fn withdraw(ctx: Context<Withdraw>,amount:u64) -> Result<()> {
        let fundraiser_state = &mut ctx.accounts.fundraiser_state;
        if Status::from(fundraiser_state.status)? != Status::CampaignEnded {
            fundraiser_state.status = Status::CampaignEnded.to_u8();
        }
         let fund_starter = ctx.accounts.fund_starter.key();
        //    let(fund_pda,fund_bump) = Pubkey::find_program_address(
        //       &[fund_starter.as_ref()],
        //       ctx.program_id);
        let bump = ctx.bumps.get("vault").unwrap();
         let seeds = &[fund_starter.as_ref(),&[*bump]];
         let signer = [&seeds[..]];
        
        invoke_signed(
            &system_instruction::transfer(
                &ctx.accounts.vault.key(),
                &ctx.accounts.destination.key(),
                amount,
            ),
             &[
                ctx.accounts.vault.to_account_info().clone(),
                ctx.accounts.destination.to_account_info().clone(),
                ctx.accounts.system_program.to_account_info().clone(),
             ],
             &signer,
            )?;

        Ok(())

and here is what the Account structs looks like

#[derive(Accounts)]
pub struct Withdraw <'info> {
    #[account(
        mut,
        seeds = [ACCOUNT_SEED.as_ref()],
        bump ,
        has_one = fund_starter,
    )] 
    fundraiser_state : Account<'info,Fundraising>,
    /// CHECK:
    #[account(
        mut,
        seeds =[fund_starter.key().as_ref()],
        bump 
    )] 
    vault : AccountInfo<'info>,
    /// CHECK :
    fund_starter : AccountInfo<'info>,
    ///CHECK:
    #[account(mut)]
    destination : Signer<'info>,
    system_program : Program<'info,System>,
}

I also write a test for it which brings 'Error: Signature verification failed', and it looks like this,

it("withdraw money from the fundraising", async () => {
    const receivers = (await feePayer());
    const amount  = new anchor.BN(100000);
    //const destinations = anchor.web3.Keypair.generate()
    const [adminPda,_bump] = await anchor.web3.PublicKey.findProgramAddress(
      [Buffer.from(ACCOUNT_SEED)],
      program.programId
    );
    const [vaultPda,vaultBump] = await anchor.web3.PublicKey.findProgramAddress(
      [admin.publicKey.toBuffer()],
      program.programId
    );
    console.log("the vault is :",vaultPda);
    console.log("the bump is :",vaultBump);
    let tx = await program.rpc.withdraw(amount,{
      accounts : {
        fundraiserState : adminPda,
        vault : vaultPda,
        fundStarter : admin.publicKey,
        destination: receivers.publicKey,
        systemProgram : SystemProgram.programId
      },
      //signers : [receivers]
    });
    console.log("tx is : ",tx);

The program builds succesfully but when i run the anchor test commands, i get the "Error: Signature verification failed",

Any help about what im doing wrong will be appreciated.

3
  • Why have you commented signers : [receivers], it should work if it is uncommented. Jul 16, 2022 at 10:19
  • I tried it dat way and i got the error :Program invalid Argument
    – Icus1
    Jul 16, 2022 at 10:30
  • But you should pass the signer. Try using program.methods instead of the above method and pass the signer as well. Check this for more details github.com/dhruvja/transfer-native-sol/blob/master/tests/… Jul 16, 2022 at 10:51

2 Answers 2

8

You can't use system_instruction::transfer to transfer Solana from an account owned by any program other than the System Program.

As you noted, the solution when an account is owned by your program is to borrow the lamports and manually update them on the relevant accounts, while making sure that the total number of lamports in all accounts remains constant.

**ctx.accounts.vault.try_borrow_mut_lamports()? -= amount;
**ctx.accounts.destination.try_borrow_mut_lamports()? += amount;

If the vault was instead a system program account, then you could use system_instruction::transfer

1
  • 2
    one caveat to manipulating the lamports field is that it must be done after any CPI calls to avoid tripping the inter-instruction the consensus-approved account change rules
    – trent.sol
    Jul 20, 2022 at 2:28
2

so,after trying so hard,i found this and was able to solve this

**ctx.accounts.vault.try_borrow_mut_lamports()? -= amount;
        **ctx.accounts.destination.try_borrow_mut_lamports()? += amount;

using this instead of the former one(invoke signed) solves the error.

1
  • this answer could use some elaboration so future readers can understand why it works
    – trent.sol
    Jul 18, 2022 at 18:00

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.