Here is the list of accounts I passed in (the ... is extra constraints and stuff I removed for readability)

    pub struct SendTokens<'info>  {
        #[account(mut, seeds = [...], bump)] 
        pub pda: Box<Account<'info, VmData>>,
        #[account(mut, seeds=[])],
        pub escrow_wallet: Account<'info, TokenAccount>,
        pub receiver_1: SystemAccount<'info>,
        #[account(mut, ...)]
        receiver_1_token_wallet: Account<'info, TokenAccount>,
        #[account(mut, ...)]
        pub receiver_2: SystemAccount<'info>,
        pub receiver_2_token_wallet: Account<'info, TokenAccount>,
        pub receiver_3_token_wallet: Account<'info, TokenAccount>,
        pub token_program: Program<'info, Token>,

here is the instruction:

pub fn send_tokens(ctx: Context<SendTokens>) -> Result<()> {
    let nonce = ctx.accounts.vm_pda.nonce.to_be_bytes();
        let inner_seeds = vec![...];
        let seeds = vec![inner_seeds.as_slice()];

    let transfer_to_taker_ix = spl_token::instruction::transfer(
    msg!("Calling the token program to transfer tokens to the taker...");

and finally, snippet of the error:

Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account"
"Program log: Calling the token program to transfer tokens to the taker...", "[PDA]'s signer privilege escalated",

some extra information:

  • the pda does have authority over the escrow_wallet (checked on solana explorer)
  • I also tried to use anchor_spl::token::Transfer and CpiContext::new_with_signer() with anchor_spl::token::transfer(cpi_ctx, 1)?; (same error)
  • I am able to use the anchor_spl::token::transfer(cpi_ctx, amount)?; to transfer tokens from a user token wallet into a pda owned token (escrow) wallet
  • there are 3 receivers because I am trying to split the tokens 3 ways, but it doesn't work even when I only have the one transfer
  • I"m facing the same issue, but I'm trying to use the PDA to mint a token. Just like @coco, I've logged PDA's seeds and it also brings the correct array of bytes. Any insight on this matter? Nov 23, 2022 at 2:41

1 Answer 1


This is probably an issue with the address derived from your signer seeds not matching the pda pubkey that's passed in from the client. Here's example working code that shows transferring spl-tokens from a token account with a PDA authority:

let transfer_instruction = Transfer {
    from: funds_pot.to_account_info(),
    to: destination_account.to_account_info(),
    authority: campaign.to_account_info(),

let campaign_seeds = &[
let signer = &[&campaign_seeds[..]];

let cpi_ctx = CpiContext::new(token_program.to_account_info(), transfer_instruction)
anchor_spl::token::transfer(cpi_ctx, amount_to_withdraw)?;

Here campaign is a PDA and the authority of funds_pot which is a token account. That's how its seeds are used to derive a signer for the transfer instruction.

  • Hi! I printed out the derived address from the seeds I used with msg!(), and it matches the pda I passed in :(
    – coco
    Aug 26, 2022 at 20:44
  • Do you have a link to a github repo or somewhere else I can see the full code
    – Ademola
    Aug 28, 2022 at 12:24

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.