2

On a low level, solana programs take instructions which only have 3 fields, an account vec, a bytearray of data, and the programID. This makes it easy to invoke programs from other programs by generating those fields and calling solana_program::program::invoke. However, with anchor, program modules have a context field which has Accounts structs to validate accounts, and any number of other arguments. I'm wondering how to invoke these types of programs using base solana_program::program::invoke calls. As an example, consider the following swap instruction which has input fields like so

pub fn swap<'info>(
        ctx: Context<'_, '_, '_, 'info, Swap<'info>>,
        side: Side,
        amount: u64,
        min_exchange_rate: ExchangeRate,
)

Where the swap account is defined as follows

pub struct Swap<'info> {
    pub market: MarketAccounts<'info>,
    #[account(signer)]
    pub authority: AccountInfo<'info>,
    #[account(mut, constraint = pc_wallet.key != &empty::ID)]
    pub pc_wallet: AccountInfo<'info>,
    pub dex_program: AccountInfo<'info>,
    pub token_program: AccountInfo<'info>,
    pub rent: AccountInfo<'info>,
}
pub struct MarketAccounts<'info> {
    #[account(mut)]
    pub market: AccountInfo<'info>,
    #[account(mut)]
    pub open_orders: AccountInfo<'info>,
    #[account(mut)]
    pub request_queue: AccountInfo<'info>,
    #[account(mut)]
    pub event_queue: AccountInfo<'info>,
    #[account(mut)]
    pub bids: AccountInfo<'info>,
    #[account(mut)]
    pub asks: AccountInfo<'info>,
    #[account(mut, constraint = order_payer_token_account.key != &empty::ID)]
    pub order_payer_token_account: AccountInfo<'info>,
    #[account(mut)]
    pub coin_vault: AccountInfo<'info>,
    #[account(mut)]
    pub pc_vault: AccountInfo<'info>,
    pub vault_signer: AccountInfo<'info>,
    #[account(mut, constraint = coin_wallet.key != &empty::ID)]
    pub coin_wallet: AccountInfo<'info>,
}

My assumption is that you can simply serialize the data fields in a single struct, but I'm struggling on how you should interact with the accounts. In the accounts vector, would I simply list them like they are ordered within the struct (so I would first do the market accounts, like [market, open_orders, ..., coin_wallet, authority, ..., rent]) or is there a better method?

5
  • I did end up trying serializing and adding everything in a row, but I got Custom program error: 0x65, which corresponds to "Fallback functions are not supported" I think this means anchor does not know which instruction I'm trying to execute. So I tried adding a byte to the beginning of the data instruction (hoping that perhaps processes are called by some sort of enum), but this also didn't work. Commented Sep 11, 2022 at 22:59
  • I think I figured it out! Will need to do a bit more testing (if I'm incorrect about simply serializing everything in order someone let me know, but the entrypoint for Anchor programs can be found Sha256("<namespace>:<rust-identifier>")[..8] as mentioned at github.com/coral-xyz/anchor/blob/master/lang/syn/src/codegen/… Commented Sep 11, 2022 at 23:40
  • Your assumption is right about what's happening under the hood. You can have a look at this blog blog.labeleven.dev/… or simply run anchor expand to see what's happening
    – sohrab
    Commented Sep 12, 2022 at 1:53
  • Oh that is interesting, makes more sense now. Commented Sep 12, 2022 at 22:28
  • If you have a solution for what it should look like please post an answer to your own question so others can benefit. Sohrab's link might also be useful to add as context but it's best to add an actual code example in the answer.
    – Henry E
    Commented Sep 19, 2022 at 9:35

1 Answer 1

1

From @sohrab's useful blogpost on invoking anchor programs in regular solana programs

// prepare instruction data
struct DoSomething {
    x: u8,
}
let instruction_data: Vec<u8> = sighash_of_the_method();
instruction_data.append(BorshSerialize.try_to_vec(DoSomething { x: 2 }));

let accounts = vec![
    AccountMeta::new(*token_account, false),
    AccountMeta::new_readonly(*signer, true),
    AccountMeta::new_readonly(*rent, false),
];

// prepare account infos
let account_infos = vec![
    token_account.info.clone(),
    signer.info.clone(),
    rent.info.clone(),
];

// prepare the instruction
let instruction = solana_program::instruction::Instruction {
    program_id: *program_id,
    accounts,
    instruction_data,
};

// invoke the program, providing the instruction
solana_program::program::invoke_signed(
   &instruction, 
   &account_infos, 
   signer_seeds
)?

Adding some descriptive tags: Cross program invocation (CPI) from a regular rust solana program to an anchor program

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.