Soldev

Cross Program Invocation (CPI)

Last updated:

A cross program invocation is when a program invokes the instructions of another program.

This can only go to a max depth of 4 programs (A -> B -> C -> D).

Accounts and permissions cascade across the invocations, meaning that the accounts and permissions given to program A, will cascade to the subsequent programs it calls.

Programs use the invoke and invoke_signed functions to trigger other programs behavior.

The invoke method actually just calls invoke_signed with an empty signers_seed array:

pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
    invoke_signed(instruction, account_infos, &[])
}

When a PDA invokes something that requires a signer it will use invoke_signed and sign for the transaction using the seeds and the bump. This is how you sign for transactions on accounts owned by a program instead of the system (like a normal user account which just requires a regular private key signer)

A simple transfer

Here's an example of a program written in Rust using Anchor that handles transfers:

use anchor_lang::prelude::*;
use anchor_lang::system_program::{transfer, Transfer};

declare_id!("9AvUNHjxscdkiKQ8tUn12QCMXtcnbR9BVGq3ULNzFMRi");

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

    pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {
        let from_pubkey = ctx.accounts.sender.to_account_info();
        let to_pubkey = ctx.accounts.recipient.to_account_info();
        let program_id = ctx.accounts.system_program.to_account_info();

        let cpi_context = CpiContext::new(
            program_id,
            Transfer {
                from: from_pubkey,
                to: to_pubkey,
            },
        );

        transfer(cpi_context, amount)?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct SolTransfer<'info> {
    #[account(mut)]
    sender: Signer<'info>,
    #[account(mut)]
    recipient: SystemAccount<'info>,
    system_program: Program<'info, System>,
}

This program takes a SolTransfer instruction with an amount and ferries it to the next program, using the provided program_id from the system_program found on the SolTransfer struct.

Token transfers

Token transfers are done through the anchor_spl crate.

anchor_spl::token::transfer(

)

PDA Transfers

When making transfers through a Program where the Accounts are program-owned, and found at PDAs, we use invoke_signed and hand in the signers_seeds alongside the instruction and account_infos array.