1

so I am trying to write an app where we can deposit native SOL to a wallet, that wallet would be a PDA, and based on some event I would like to transfer SOL to a particular wallet via a CPI call but I get the following error

SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account
    at Connection.sendEncodedTransaction ... {
  logs: [
    'Program RPSrbc8yWqcqi3SjdrT7AYFEJSLBwZy5sAnTGCesZyX invoke [1]',
    'Program log: Instruction: TransferCpi',
    "pSpMCuq3PjmkLy4VrUPjT1w4WgdmXUAuKT1gHdQRhW8's signer privilege escalated",
    'Program RPSrbc8yWqcqi3SjdrT7AYFEJSLBwZy5sAnTGCesZyX consumed 5813 of 200000 compute units',
    'Program RPSrbc8yWqcqi3SjdrT7AYFEJSLBwZy5sAnTGCesZyX failed: Cross-program invocation with unauthorized signer or writable account'

Apparently, my seed to sign the invoke call might be wrong in the instruction but I don't really see where, if maybe someone can enlighten me, please

This is how my seed is used for the CPI call,

in the test:

    const [puppetMasterPDA, puppetBump] = await anchor.web3.PublicKey.findProgramAddress([
      anchor.utils.bytes.utf8.encode("escrow_wallet"),
      puppetKeypair.publicKey.toBuffer()
    ], escrowMasterProgram.programId);

    await program.methods //wallet where we want to deposit
      .initializePuppet(puppetMasterPDA)
      .accounts({
        puppet: puppetKeypair.publicKey,
        user: playerOne.publicKey,
      })
      .signers([puppetKeypair])
      .rpc()

      //deposit transaction
    const tx = await program.methods.deposit(new BN(lamportz), playerOne.publicKey).accounts({ 
      anAccount: puppetKeypair.publicKey,
      player: playerOne.publicKey,
    }).rpc();
    console.log("Your deposit signature", tx);
    console.log("Escrow account address: ", puppetKeypair.publicKey.toBase58())
    console.log("Master PDA: ", puppetMasterPDA.toBase58())

    const puppetPDA = await program.account.data.fetch(puppetKeypair.publicKey);
    console.log("Escrow account authority: ", puppetPDA.authority.toBase58())

    //pay a winner
    try{
      const wtx = await escrowMasterProgram.methods
      .transferCpi(new BN(lamportz))
      .accounts({
        puppetProgram: program.programId,
        puppet: puppetKeypair.publicKey,
        authority: puppetMasterPDA,
        to:playerOne.publicKey
      })
      //.signers([puppetKeypair])
      .rpc()
      console.log("Your withdraw signature", wtx)
    }
    catch(err){
      console.log(err)
    }

CPI call:

        pub fn transfer_cpi(ctx: Context<PullStrings>, lamportz: u64) -> Result<()> {
        let seeds = &[b"escrow_wallet".as_ref(), ctx.accounts.puppet.to_account_info().key.as_ref()];
        let signer = [&seeds[..]];
        rps_project::cpi::set_data(ctx.accounts.set_data_ctx()
            .with_signer(&signer), lamportz, ctx.accounts.to.to_account_info().key())
    }
    
    my transfer from PDA function:
        pub fn initialize_puppet(_ctx: Context<Initialize>,  authority: Pubkey) -> Result<()> {
            _ctx.accounts.puppet.authority = authority;
            _ctx.accounts.puppet.deposit = 0;
            Ok(())
        }
    
        pub fn set_data(ctx: Context<SetData>, lamportz: u64, to: Pubkey) -> Result<()> {
            let seeds = &[b"escrow_wallet".as_ref(), ctx.accounts.puppet_escrow.to_account_info().key.as_ref()];
            let signer = [&seeds[..]];
            let puppet_escrow = &mut ctx.accounts.puppet_escrow;
            let transfer_instruction = &transfer(
                &puppet_escrow.to_account_info().key,
                &to, //game account where the deposit goes
                lamportz,
            );
            invoke_signed(
                transfer_instruction,
                &[
                    puppet_escrow.to_account_info().clone(),
                    ctx.accounts.authority.to_account_info().clone(),
                ],
                &signer
            )?;
            Ok(())
        }

and this is what my struct looks like:

    #[derive(Accounts)]
    pub struct SetData<'info> {
        #[account(mut, has_one = authority)]
        pub puppet_escrow: Account<'info, Data>,
        pub authority: Signer<'info>,
        /// CHECK:
        #[account(mut)]
        pub to: Account<'info, Data>
    }

    #[derive(Accounts)]
    pub struct PullStrings<'info> {
        #[account(mut)]
        pub puppet: Account<'info, Data>,
        pub puppet_program: Program<'info, RpsProject>,
        /// CHECK: only used as a signing PDA
        pub authority: UncheckedAccount<'info>,
        /// CHECK:
        #[account(mut)]
        pub to: AccountInfo<'info>
    }
    
    
    impl<'info> PullStrings<'info> {
        pub fn set_data_ctx(&self) -> CpiContext<'_, '_, '_, 'info, SetData<'info>> {
            let cpi_program = self.puppet_program.to_account_info();
            let cpi_accounts = SetData {
                puppet_escrow: self.puppet.to_account_info(),
                authority: self.authority.to_account_info(),
                to: self.to.to_account_info()
            };
            CpiContext::new(cpi_program, cpi_accounts)
        }
4
  • in the test in forgot to mention ` const puppetKeypair = Keypair.generate() //const authKP = Keypair.generate() const [puppetMasterPDA, puppetBump] = await anchor.web3.PublicKey.findProgramAddress([ anchor.utils.bytes.utf8.encode("escrow_wallet"), puppetKeypair.publicKey.toBuffer() ], escrowMasterProgram.programId); `
    – Sing
    Jul 27, 2022 at 23:15
  • please replace all of these screenshots with code blocks
    – trent.sol
    Jul 28, 2022 at 0:06
  • also please just update the description rather than adding addendums as comments. the new code is of no help without knowing where it is positioned in the test
    – trent.sol
    Jul 28, 2022 at 0:08
  • I did the update into code block my bad
    – Sing
    Jul 28, 2022 at 0:41

3 Answers 3

2

For any call involving signing for a program derived account, before attempting to submit the actual signed transaction I'd advise logging with msg!() the provided PDA, as well as the one that you have derived within the program side by side.

Something like this:

pub fn set_data(ctx: Context<SetData>, lamportz: u64, to: Pubkey) -> Result<()> {
    let (derived_ata, _) = anchor_lang::prelude::Pubkey::find_program_address(
        &[
            b"escrow_wallet".as_ref(),
            ctx.accounts.puppet_escrow.to_account_info().key.as_ref(),
        ],
        &ctx.program_id,
    );

    msg!("derived_ata: {:?}", derived_ata);
    msg!(
        "provided_ata: {:?}",
        ctx.accounts.puppet_escrow.to_account_info().key
    );
}

If they don't match you can then work backwards and try to see at which point the seed schema derivation on-chain mismatches the one you've performed in your tests.

I can't see all of your code, so don't know how you derive the PDA for puppet_escrow when it is being initialised, but it is likely failing to sign correctly for puppet_escrow as you are providing itself as the seed for the program to rederive its address.

If this is only SOL rather than SPL being transferred, you can witdhraw it as such from a PDA owned by the invoked program.

   **lottery.to_account_info().try_borrow_mut_lamports()? -= balance;
   **recipient.to_account_info().try_borrow_mut_lamports()? += balance; 

This repo has example of SOL deposits to and from PDAs.

0

It looks like you might be modifying the ‘To’ account but haven’t set it to be writable (mut).

3
  • tried to add #[account(mut)] as you suggested but then I get a new error ` 'Program failed to complete: Could not create program address with signer seeds: Provided seeds do not result in a valid address',` I'm guessing the seeds I provided are wrong somehow, do perhaps know why/where?
    – Sing
    Jul 27, 2022 at 23:57
  • 1
    I don’t see a bump in the signer seeds, maybe that? solanacookbook.com/core-concepts/pdas.html#generating-pdas
    – zfedoran
    Jul 28, 2022 at 1:44
  • 2
    new question -> new post. don't start discussions in the comments
    – trent.sol
    Jul 28, 2022 at 7:06
-1

try to run without .signers[] part.

1
  • why though? please elaborate so OP and anyone else who comes along later understands why you would suggest this
    – trent.sol
    Jul 28, 2022 at 7:05

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.