1

I've been having this problem for a few weeks now. First, I'm creating a PDA for managing campaigns:

pub struct CreateCampaign<'info> {
        #[account(
            init,
            payer = seller,
            space = 200,
            seeds = [
                seller.key().as_ref(),
                b"_mff_",
                timestamp.to_le_bytes().as_ref(),
            ],
            bump
        )]
        pub campaign: Account<'info, Campaign>,
        #[account(mut)]
        pub seller: Signer<'info>,
...

Then I proceed to create a mint, initialize it and set the mint authority for the PDA address:


let create_account_accounts = CreateAccount {
            from: ctx.accounts.seller.to_account_info(),
            to: ctx.accounts.mint.to_account_info(),
        };

        let create_account_cpi_ctx = CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            create_account_accounts,
        );

        let rent_balance = Rent::get()?.minimum_balance(82);

        system_program::create_account(
            create_account_cpi_ctx,
            rent_balance,
            82,
            &ctx.accounts.token_program.key(),
        )?;


        let initialize_mint_accounts = token::InitializeMint {
            mint: ctx.accounts.mint.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };

        let initialize_mint_cpi_ctx = CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            initialize_mint_accounts,
        );

        token::initialize_mint(
            initialize_mint_cpi_ctx,
            0,
            &ctx.accounts.campaign.key(), // transfer the mint authority to PDA
            Some(&ctx.accounts.campaign.key()),
        )?;

Until this point, everything works as expected. Checking the token mint account in solana explorer confirms that the mint authority is set to the PDA. The problem starts when I try to mint any token using the PDA as signer. This is my context.

 #[derive(Accounts)]
    pub struct BuyerEditBid<'info> {
        #[account(mut, has_one = buyer)]
        pub bid: Account<'info, Bid>,
        #[account(mut)]
        pub campaign: Account<'info, Campaign>,

        #[account(mut)]
        pub buyer: Signer<'info>,

        #[account(mut)]
        pub mint: Account<'info, token::Mint>,

        /// CHECK: We'll create this with Metaplex if bid is closed
        // #[account(mut)]
        // pub metadata: UncheckedAccount<'info>,
        /// CHECK: We'll create this with Anchor if bid is closed
        #[account(mut)]
        pub token_account: UncheckedAccount<'info>,
...

Then I run this function:

fn mint_closed_bid_token(accounts: &mut BuyerEditBid) -> Result<()> {
        
        associated_token::create(CpiContext::new(
            accounts.associated_token_program.to_account_info(),
            associated_token::Create {
                payer: accounts.buyer.to_account_info(),
                associated_token: accounts.token_account.to_account_info(),
                authority: accounts.buyer.to_account_info(),
                mint: accounts.mint.to_account_info(),
                system_program: accounts.system_program.to_account_info(),
                token_program: accounts.token_program.to_account_info(),
                rent: accounts.rent.to_account_info(),
            },
        ))?;

        let seller = accounts.campaign.seller.key();
        let timestamp = accounts.campaign.timestamp.to_le_bytes();
        let seeds = [seller.as_ref(), "_mff_".as_bytes(), timestamp.as_ref()];
        let signer_seeds = &[&seeds[..]][..];


        msg!("seeds signer {:?}", signer_seeds);

        let result = token::mint_to(
            CpiContext::new_with_signer(
                accounts.token_program.to_account_info(),
                token::MintTo {
                    mint: accounts.mint.to_account_info(),
                    to: accounts.token_account.to_account_info(),
                    authority: accounts.campaign.to_account_info(),
                },
                signer_seeds,
            ),
            u64::from(accounts.bid.amount),
        );

To my surprise, I get the not so unusual error: Cross-program invocation with unauthorized signer or writable account Any ideas why? I've checked the seeds, even used PublicKey::find_program_addressto confirm that the seeds are correct. In fact, they are. Why do I get this (not so descriptive) error? I can't mark my campaign account as signer. Should this error run in PDA owned accounts, when their seeds are correct?

Thanks in advance

1 Answer 1

2

You're deriving the signer_seeds wrongly. Here's how to do it:

let seeds = &[
     seller.as_ref(),
     "_mff_".as_bytes(),
     &[campaign_bump]
];

let signer_seeds = &[&seeds[..]];

The campaign_bump, exactly as its name implies, is the bump of the campaign account.

2
  • Thanks, it worked. I wasn't storing the bump in the PDA as after anchor 0.21.0, we can't use the bump in the initializer (I mean, bump = bump), I thought it wasn't necessary. It is though, in order to sign. I appreciate your help, thanks a lot. Dec 2, 2022 at 2:04
  • Glad to be of help
    – Ademola
    Dec 2, 2022 at 14:04

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.