I want to send Solana from a PDA to another wallet using a publicKey. The other wallet is not the signer and the transaction is happening via Program.
2 Answers
(Answer Relevant for Anchor)
You should pass recepient account into your instruction and send lamports via transfer instruction!
So what you need to do is:
Your instruction context should include recepient:
#[derive(Accounts)]
pub struct MyContext<'info> {
...
#[account(mut)]
pub sender: YourPDA<'info, T>,
#[account(mut)]
pub recepient: AccountInfo<'info>,
pub system_program: Program<'info, System>
}
Inside your instruction you transfer lamports to the recepient via solana program transfer instruction:
use anchor_lang::system_program::{Transfer, transfer};
let cpi_context = CpiContext::new_with_signer(
ctx.accounts.system_program.to_account_info(),
Transfer {
from: ctx.accounts.sender.to_account_info(),
to: ctx.accounts.recepient.clone(),
},
&[&pool_seeds[..]]
);
transfer(cpi_context, AMOUNT_OF_LAMPORTS)?;
Note that your PDA should sign it with seeds, that's why you should use new_with_signer
and pass PDA seeds.
Other way to do it is directly deduce lamports:
**sender.to_account_info().try_borrow_mut_lamports()? -= AMOUNT_OF_LAMPORTS;
**recepient.try_borrow_mut_lamports()? += AMOUNT_OF_LAMPORTS;
But this will work only if sender is owned by your program. So in case your PDA does not have any data and was not initialized with init previously, it is owned by System Program (by default), so you will need to follow first example.
And so on your frontend you should still pass the recepient account.
await program.methods.instruction().accounts({
sender: senderPDAPubkey,
recepient: recepientPubkey,
systemProgram: SystemProgram.programID
}).rpc()
Also if this recepient is known and could not be any sender you should consider adding some constraints to prevent abuse:
...
#[account(mut, constraint = recepient.key == KNOWN_PUBKEY)]
recepient: AccountInfo<'info>
Quote from Anchor 0.29.0 new features:
Instead of
**ctx.accounts.from.to_account_info().try_borrow_mut_lamports()? -= amount;
**ctx.accounts.to.to_account_info().try_borrow_mut_lamports()? += amount;
you can
ctx.accounts.from.sub_lamports(amount)?;
ctx.accounts.to.add_lamports(amount)?;
Similarly for getting the lamports, instead of
let lamports = ctx.accounts.my_account.to_account_info().lamports();
you can
let lamports = ctx.accounts.my_account.get_lamports();
Note: The new methods are not only more ergonomic but they are also more performant than the previous examples.
Reference: https://www.anchor-lang.com/release-notes/0.29.0#lamport-utilities