3

I've read solana and anchor's documentation on doing CPI, but still don't really understand.

what is the basis that must be understood when doing CPI program?

for example I want to call function function from metaplex bubblegum like CreateTree instruction.

but I still fail in trying to understand it.

use {
    anchor_lang::prelude::*,
    mpl_bubblegum::{
        ID as BUBBLEGUM_PROGRAM_ID,
        program::Bubblegum,
    }
};

declare_id!("...");

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

    pub fn ping(ctx: Context<PingContext>) -> Result<()> {
        msg!("Pong!");

        msg!("Calling the bubblegum program... {} | {}", ctx.accounts.bubblegum_program.key(), BUBBLEGUM_PROGRAM_ID);

        invoke(
            mpl_bubblegum::cpi::create_tree(
                CpiContext::new(
                    ctx.accounts.bubblegum_program.to_account_info(),
                ),
                // ...
            ),
            &[
                // ...
            ],
        );

        Ok(())
    }
}


#[derive(Accounts)]
pub struct PingContext<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(mut)]
    pub bubblegum_program: UncheckedAccount<'info>
}

1 Answer 1

4

You're mixing up different interfaces here. The invoke function is used when the program you're calling isn't an Anchor program and so doesn't implement the CpiContext pattern.

Invoke takes two parameters: An Instruction and then an accountInfo slice(&[ // ... ]). Here it looks like bubblegum has an Anchor-CPI interface so you don't need any of that.

CPI helper functions always take a CpiContext as their first argument. Whether or not they take others depends on the implementation of that particular function. Check the definition of mpl_bubblegum_cpi::create_tree() to see what extra args it requires if any.

The CpiContext argument is constructed with a CpiContext::new() function which takes two arguments: The first is the account-info of the program you're calling. The second is a struct instance that contains the accounts the CPI instruction needs. The struct type is also provided by the interface and can be imported.

For this particular case I'm guessing this is what you need. Construct an instance of it and pass it to CpiContext::new().

Keep in mind that the accounts will need to also be passed from your client to PingContext just like the bubblegum program, so they can be used to populate the struct with the values it needs.

Here's a basic template to follow.

whitelist::cpi::create_whitelist(
    CpiContext::new(
        ctx.accounts.whitelist_program.to_account_info(),
        CreateWhitelist {
            authority: ctx.accounts.authority.to_account_info(),
            whitelist: ctx.accounts.whitelist.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        }),
)?;


2
  • thanks. your explanation is very easy to understand and I immediately realized my mistake. thanks again for taking the time. Mar 30, 2023 at 13:28
  • You're very welcome
    – Ademola
    Mar 30, 2023 at 18:11

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.