1

Testing CPI calls on devnet (e.g. programs both deployed to devnet, anchor.toml set to devnet) yields this error:

 'Unknown program Ev6JrN5HqrKwXhoB9jucLdn51yzzDvWmBHkubXWavRio',
    'Program <theCpiCaller> consumed 27039 of 200000 compute units',
    'Program <theCpiCaller> failed: An account required by the instruction is missing'

Where the CPI gets invoked like this:

#[derive(Accounts)]
pub struct DummyMsgCpi<'info> {
    /// CHECK: Checked by dummy program
    pub dummy_acc: UncheckedAccount<'info>,
}

pub fn handler(ctx: Context<DummyMsgCpi>) -> Result<()> {
    let program_id: Pubkey =
        Pubkey::from_str("Ev6JrN5HqrKwXhoB9jucLdn51yzzDvWmBHkubXWavRio").unwrap();
    invoke(
        &Instruction {
            program_id,
            accounts: vec![AccountMeta::new_readonly(
                ctx.accounts.dummy_acc.key(),
                false,
            )],
            data: get_function_hash("global", "msg").to_vec(),
        },
        &[ctx.accounts.dummy_acc.to_account_info()],
    )?;
    Ok(())
}

pub fn get_function_hash(namespace: &str, name: &str) -> [u8; 8] {
    let preimage = format!("{}:{}", namespace, name);

    let mut hash = [0u8; 8];
    hash.copy_from_slice(
        &anchor_lang::solana_program::hash::hash(preimage.as_bytes()).to_bytes()
            [..8],
    );
    hash
}

And program Ev6JrN5HqrKwXhoB9jucLdn51yzzDvWmBHkubXWavRio looks like this for reference:


#[derive(Accounts)]
pub struct Msg<'info> {
    /// CHECK: no security
    pub dummy_acc: UncheckedAccount<'info>,
}

pub fn handler(ctx: Context<Msg>) -> Result<()> {
    msg!("You sent: {:?}", ctx.accounts.dummy_acc.key());
    Ok(())
}

Best guess is that's a problem generating the correct discriminator

2 Answers 2

3

The program ID for the dummy program should be included in the Accounts struct, don't think you can build a CPI that tries to use a hardcoded pubkey as one required accounts for the CPI within the program itself

For example:

#[derive(Accounts)]
pub struct DummyMsgCpi<'info> {
    /// CHECK: Checked by dummy program
    pub dummy_acc: UncheckedAccount<'info>,
    /// CHECK: provide dummy program ID here
    pub program_id: UncheckedAccount<'info>,
}

And then within the instruction:

program_id: ctx.accounts.program_id.key()

You may also need to make changes to how you built your CPI.

This repo as good references for building CPIs for both native and anchor: https://github.com/solana-developers/program-examples/tree/main/basics/cross-program-invocation

1
  • This is correct. Also have to add the key to account info when passing it to invoke, e.g: &[ctx.accounts.dummy_acc.to_account_info(), ctx.accounts.dummy_program.to_account_info()],
    – Whiteseal
    Feb 3, 2023 at 19:14
1

Full solution:

pub const DUMMY_PROGRAM_KEY: &str = "Ev6JrN5HqrKwXhoB9jucLdn51yzzDvWmBHkubXWavRio";

#[derive(Accounts)]
pub struct DummyMsgCpi<'info> {
    /// CHECK: Checked by dummy program
    pub dummy_acc: UncheckedAccount<'info>,
    /// CHECK: Validated by constraint
    #[account(
        address = Pubkey::from_str(DUMMY_PROGRAM_KEY).unwrap()
    )]
    pub dummy_program: UncheckedAccount<'info>, // <added
}

pub fn handler(ctx: Context<DummyMsgCpi>) -> Result<()> {
    let program_id: Pubkey =
        Pubkey::from_str(DUMMY_PROGRAM_KEY).unwrap();
    invoke(
        &Instruction {
            program_id: Pubkey::from_str(DUMMY_PROGRAM_KEY).unwrap(),
            accounts: vec![AccountMeta::new_readonly(
                ctx.accounts.dummy_acc.key(),
                false,
            )],
            data: get_function_hash("global", "msg").to_vec(),
        },
        &[
            ctx.accounts.dummy_acc.to_account_info(),
            ctx.accounts.dummy_program.to_account_info(), // <added
        ],
    )?;
    Ok(())
}

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.