Cross Program Invocation (CPI)
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.