My question is in the title, so my program can transfer SOL from a PDA to another user but the thing is, based on an event, I'd like to pay the winner, but the winner always has to approve (on the frontend) the transaction which is not user-friendly. The account is owned by my program, I tried it trough CPI but the winner still has to sign (to receive/claim) if he wins, the other way was without using CPI (try_borrow_mut...) but it still asks the user to sign but both methods work, does anyone have an alternative perhaps? I have thought about auto approve but I blieve that Phantom removed that functionality
CPI struct
#[derive(Accounts)]
pub struct PayFromPDA<'info> {
#[account(mut)]
pub puppet: Account<'info, LockAccount>, //escrow PDA
pub puppet_program: Program<'info, DepositApp>,
/// CHECK:
#[account(mut)]
pub destination: AccountInfo<'info>,
/// CHECK:
#[account(mut)]
fee_acc: AccountInfo<'info>,
/// CHECK: only used as a signing PDA
#[account(mut, )]
pub authority: UncheckedAccount<'info>,
}
impl<'info> PayFromPDA<'info> {
pub fn pay_ctx(&self) -> CpiContext<'_, '_, '_, 'info, Payo<'info>> {
let cpi_program = self.puppet_program.to_account_info();
let cpi_accounts = Payo {
room_account: self.puppet.to_account_info(), //PDA that we want to sign the transac
winner: self.destination.to_account_info(),
fee_acc: self.fee_acc.to_account_info(),
authority: self.authority.to_account_info()
};
CpiContext::new(cpi_program, cpi_accounts)
}
}
cpi function call
pub fn pay_winner(ctx: Context<PayFromPDA>, amount: u64, bump: u8, user:Pubkey) -> Result<()> {
rps_project::cpi::payo(ctx.accounts.pay_ctx()
.with_signer(&[&[
b"room_master".as_ref(),
user.as_ref(),
&[bump],
]])
, amount)?;
Ok(())
}
payout struct
#[derive(Accounts)]
pub struct Payo<'info> {
/// CHECK:
#[account(mut)]
pub room_account: Account<'info, LockAccount>,
/// CHECK:
#[account(mut)]
pub winner: AccountInfo<'info>,
/// CHECK:
#[account(mut, constraint = FEE_WALLET == fee_acc.key.to_string())]
pub fee_acc: AccountInfo<'info>,
#[account(mut)]
authority: Signer<'info>
}
payout function
//payout
pub fn payo(ctx: Context<Payo>, amount:u64) -> Result<()>{
let room_account = &mut ctx.accounts.room_account;
let payout:f64 = amount as f64 * 0.96;
let fee:f64 = amount as f64 * 0.04;
//fees
**room_account.to_account_info().try_borrow_mut_lamports()? -= fee as u64;
**ctx.accounts.fee_acc.to_account_info().try_borrow_mut_lamports()? += fee as u64;
//payout
**room_account.to_account_info().try_borrow_mut_lamports()? -= payout as u64;
**ctx.accounts.winner.to_account_info().try_borrow_mut_lamports()? += payout as u64;
Ok(())
}
update, call on frontend
const [roomPDA, roomBump] = await anchor.web3.PublicKey.findProgramAddress(
[utf8.encode("room_escrow_wallet"), user1.publicKey.toBuffer()],
program.programId
);
const [puppetMasterPDA, puppetMasterBump] =
await PublicKey.findProgramAddress([utf8.encode("room_master"), user1.publicKey.toBuffer()],
puppetMasterProgram.programId)
await program.methods.initRoomEscrow(roomBump)
.accounts({
authority: puppetMasterPDA,
signer: user1.publicKey,
roomAccount: roomPDA,
systemProgram: anchor.web3.SystemProgram.programId
}).signers([user1])
.rpc()
await puppetMasterProgram.methods.payWinner(new BN(2*lampsToSend), puppetMasterBump, user1.publicKey)
.accounts({
puppet: roomPDA,
puppetProgram: program.programId,
destination: random.publicKey,
feeAcc: fee_wallet,
authority: puppetMasterPDA,
}).rpc()
room_account
. Whos the signing authority of that account? I assume its a PDA? So thewinner
account doesn't need to sign for this. How is thepayo
instruction called?find_program_address()
so it would be a PDA owned by the system program and that payout function is trigerred at frontend by aawait roomMasterProgram.methods.payWinner()
, I updated with the code to I call it on the frontend