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)
}