2

I just want to invoke initialize method of callee program from caller program in which pda account should be signer.

Callee Program


#[program]
mod callee {
    use super::*;

    // I want to call this method using cpi in which pda should be signer.
    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.new_account.data = data;
        msg!("Changed data to: {}!", data);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = signer, space = 8 + 8)]
    pub new_account: Account<'info, NewAccount>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct NewAccount {
    data: u64
}

Caller Program


#[constant]
const USER_ACCOUNT_SEEDS: &[u8] = b"USER_ACCOUNT_SEEDS";

#[program]
pub mod caller {
    use super::*;

    pub fn create_user_account(ctx: Context<CreateUserAccount>) -> Result<()> {
        ctx.accounts.user_account.bump = *ctx.bumps.get("user_account").unwrap();
        Ok(())
    }

    pub fn invoke_transaction<'info>(
        ctx: Context<'_, '_, '_, 'info, InvokeTransaction<'info>>,
        data: Vec<u8>,
    ) -> Result<()> {
        // Am i doing any mistake here
        let ix = solana_program::instruction::Instruction {
            program_id: ctx.accounts.program_to_be_called.key(),
            accounts: vec![
                AccountMeta::new(ctx.accounts.new_account.key(), true),
                AccountMeta::new(ctx.accounts.user_account.key(), true),
                AccountMeta::new_readonly(ctx.accounts.system_program.key(), false),
            ],
            data,
        };

        solana_program::program::invoke_signed(
            &ix,
            &[
                ctx.accounts.new_account.to_account_info(),
                ctx.accounts.user_account.to_account_info(),
                ctx.accounts.system_program.to_account_info(),
            ],
            &[&[
                USER_ACCOUNT_SEEDS,
                ctx.accounts.signer.key().as_ref(),
                &[ctx.accounts.user_account.bump],
            ]],
        )?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreateUserAccount<'info> {
    #[account(
        init,
        payer=signer,
        space= 8 + std::mem::size_of::<UserAccount>(),
        seeds=[USER_ACCOUNT_SEEDS,signer.key().as_ref()],
    bump)]
    pub user_account: Account<'info, UserAccount>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct UserAccount {
    pub owner: Pubkey,
    pub bump: u8,
}

#[derive(Accounts)]
pub struct InvokeTransaction<'info> {
    #[account(mut)]
    pub user_account: Account<'info, UserAccount>,

    /// CHECK: provide dummy program ID here
    pub program_to_be_called: UncheckedAccount<'info>,

    /// CHECK: provide dummy program ID here
    #[account(mut, signer)]
    pub new_account: AccountInfo<'info>,

    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}
  • Error i am getting Invalid program argument.
  • Note:- When i use signer of invoke_transaction as a signer in cpi this code works fine, but when i use user_account (pda) as a signer i gets above given error.

How i have solved this issue

  • As 0xShuk have mentioned that we can fix this issue by creating system program owned pda, i have initially tried to create system owned pda account by transferring sol from phantom wallet to my pda address but when i was trying this phantom was not allowing me to do this and it was throwing error as "Invalid address".

  • After this i have tried to create system owned pda account by using solana_program::system_instruction::create_account method but it was still not working, but by this way i was able to create system program owned account.

  • Solution:- Then i have created system program owned pda by just transferring sol using solana program, you can do this by using solana_sdk or web3js Solana.

pub fn create_system_program_owned_pda(ctx: Context<CreatePDA>) -> Result<()> {
        let lamports = 1000000;
        let (_pubkey, bump) = Pubkey::find_program_address(
            &[USER_TEMP_ACCOUNT, ctx.accounts.signer.key().as_ref()],
            &ID,
        );
        anchor_lang::solana_program::program::invoke_signed(
            &anchor_lang::solana_program::system_instruction::transfer(
                &ctx.accounts.signer.key(),
                &ctx.accounts.temp_account.key(),
                lamports,
            ),
            &[
                ctx.accounts.temp_account.to_account_info(),
                ctx.accounts.signer.to_account_info(),
            ],
            &[&[
                USER_TEMP_ACCOUNT,
                ctx.accounts.signer.key().as_ref(),
                &[bump],
            ]],
        )?;
        Ok(())
    }


#[derive(Accounts)]
pub struct CreatePDA<'info> {
    /// CHECK: provide dummy program ID here
    #[account(mut)]
    pub temp_account: AccountInfo<'info>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Hope this will help you and eventually thank you so much 0xShuk.

1 Answer 1

0

The issue is with user_account in your caller program. You are invoking the callee program with user_account as the signer which means that this account will pay the rent exemption fee for new_account. But since user_account is owned by the caller program, it can't be used to pay the rent fees, hence, it can't act as a signer.

The solution is to use different seeds for signing the CPI and for user_account. Your caller program should be like this:

[constant]
const USER_ACCOUNT_SEEDS: &[u8] = b"USER_ACCOUNT_SEEDS";
const CPI_SEEDS: &[u8] = b"CPI_SEEDS";

#[program]
pub mod caller {
    use super::*;

    pub fn create_user_account(ctx: Context<CreateUserAccount>) -> Result<()> {
        let (_authority, cpi_bump) = Pubkey::find_program_address(
            &[CPI_SEEDS, ctx.accounts.signer.key().as_ref()], &ID
        );

        ctx.accounts.user_account.bump = cpi_bump;
        Ok(())
    }

    pub fn invoke_transaction<'info>(
        ctx: Context<'_, '_, '_, 'info, InvokeTransaction<'info>>,
        data: Vec<u8>,
    ) -> Result<()> {
        // Am i doing any mistake here
        let ix = solana_program::instruction::Instruction {
            program_id: ctx.accounts.program_to_be_called.key(),
            accounts: vec![
                AccountMeta::new(ctx.accounts.new_account.key(), true),
                AccountMeta::new(ctx.accounts.cpi_authority.key(), true),
                AccountMeta::new_readonly(ctx.accounts.system_program.key(), false),
            ],
            data,
        };

        solana_program::program::invoke_signed(
            &ix,
            &[
                ctx.accounts.new_account.to_account_info(),
                ctx.accounts.cpi_authority.to_account_info(),
                ctx.accounts.system_program.to_account_info(),
            ],
            &[&[
                CPI_SEEDS,
                ctx.accounts.signer.key().as_ref(),
                &[ctx.accounts.user_account.bump],
            ]],
        )?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreateUserAccount<'info> {
    #[account(
        init,
        payer=signer,
        space= 8 + std::mem::size_of::<UserAccount>(),
        seeds=[USER_ACCOUNT_SEEDS,signer.key().as_ref()],
    bump)]
    pub user_account: Account<'info, UserAccount>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct UserAccount {
    pub owner: Pubkey,
    pub bump: u8,
}

#[derive(Accounts)]
pub struct InvokeTransaction<'info> {
    #[account(mut)]
    pub user_account: Account<'info, UserAccount>,

    /// CHECK: CPI Authority
    #[account(mut,
        seeds=[CPI_SEEDS,signer.key().as_ref()],
        bump = user_account.bump
    )]
    pub cpi_authority: AccountInfo<'info>,

    /// CHECK: provide dummy program ID here
    pub program_to_be_called: UncheckedAccount<'info>,

    /// CHECK: provide dummy program ID here
    #[account(mut, signer)]
    pub new_account: AccountInfo<'info>,

    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Make sure to transfer some SOL to cpi_authority account before calling invoke_transaction, else your will get 0x1 error.

12
  • It means a pda Account cannot be used as rent payer, I have already tried same solution given by you but problem here is that in this case cpi_authority will be signer. Apr 30, 2023 at 2:51
  • As you said that pda cannot be used as rent payer, can you please share any reference of it or article so I can get more information about it. Apr 30, 2023 at 2:52
  • I didn't say that PDA can't be a rent payer. The pda can pay rent as demonstrated above. But the account paying rent must owned by System program. In your case, user_account is owned by the caller program when you initialised it in create_user_account instruction. So it no longer eligible to interact with System program to create account. That's why we used a different pda which isn't initialised yet and will be considered as System account once funded.
    – 0xShuk
    Apr 30, 2023 at 5:06
  • Ok will do research on it, but it would be great if you share some resources using which i can achieve this and it will be also helpful for other devs. Apr 30, 2023 at 5:13
  • 1
    Hello 0xShuk, I have resolved issue by creating system owned pda by just transferring sol from program, earlier i was trying to do this by transferring sol from phantom wallet but phantom wallet was not allowing. May 2, 2023 at 10:41

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.