1

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() 
5
  • Hmm by that code, the only account that requires the signing authority is the room_account. Whos the signing authority of that account? I assume its a PDA? So the winner account doesn't need to sign for this. How is the payo instruction called?
    – C.OG
    Commented Aug 14, 2022 at 20:16
  • its authority would be a PDA, which is generated trough find_program_address() so it would be a PDA owned by the system program and that payout function is trigerred at frontend by a await roomMasterProgram.methods.payWinner(), I updated with the code to I call it on the frontend
    – Sing
    Commented Aug 14, 2022 at 20:31
  • Ok, I think i misread your question. So the user has to approve because the fee for the transaction has to be paid
    – C.OG
    Commented Aug 14, 2022 at 20:50
  • You could use a node wallet to pay the transaction fee and send the transaction. Which wont require the user to approve
    – C.OG
    Commented Aug 14, 2022 at 20:52
  • Is there no other way than a node wallet? btw my authority field is set to isSigner: false for the master program and isSigner true for slave program, or is that how it is supposed to be?
    – Sing
    Commented Aug 14, 2022 at 20:55

1 Answer 1

1

I don't know if this will solve your problem but an easier way would be to have say a game wallet into which u can transfer your SOL and then whenever there is a winner u can transfer the SOL directly from ur game wallet..this will solve ur issue where the sol will be transferred without user intervention..if u r using next.js U can save the secret key of the game wallet in ur .env.local file and call this secret key in ur frontend and let it sign the transaction in this way ur end user's will not have any need to sign any transaction.

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.