# Cross Program Invocations (CPI)

This tutorial covers how to call one program from another, a process known as cross-program-invocation (CPI).

# Clone the Repo

To get started, clone the repo.

git clone https://github.com/project-serum/anchor

And change directories to the example (opens new window).

cd anchor/examples/tutorial/basic-3

# Defining a Puppet Program

We start with the program that will be called by another program, the puppet.

use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod puppet {
    use super::*;
    pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
        Ok(())
    }
    pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
        let puppet = &mut ctx.accounts.puppet;
        puppet.data = data;
        Ok(())
    }
}
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 8)]
    pub puppet: Account<'info, Data>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SetData<'info> {
    #[account(mut)]
    pub puppet: Account<'info, Data>,
}
#[account]
pub struct Data {
    pub data: u64,
}

If you've followed along the other tutorials, this should be straight forward. We have a program with two instructions, initialize, which does nothing other than the initialization of the account (remember, the program transparently prepends a unique 8 byte discriminator the first time an account is used), and set_data, which takes a previously initialized account, and sets its data field.

Now, suppose we wanted to call set_data from another program.

# Defining a Puppet Master Program

We define a new puppet-master crate, which successfully executes the Puppet program's set_data instruction via CPI.

use anchor_lang::prelude::*;
use puppet::cpi::accounts::SetData;
use puppet::program::Puppet;
use puppet::{self, Data};
declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
#[program]
mod puppet_master {
    use super::*;
    pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> ProgramResult {
        let cpi_program = ctx.accounts.puppet_program.to_account_info();
        let cpi_accounts = SetData {
            puppet: ctx.accounts.puppet.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        puppet::cpi::set_data(cpi_ctx, data)
    }
}
#[derive(Accounts)]
pub struct PullStrings<'info> {
    #[account(mut)]
    pub puppet: Account<'info, Data>,
    pub puppet_program: Program<'info, Puppet>,
}

Things to notice

  • We create a CpiContext object with the target instruction's accounts and program, here SetData and puppet_program.
  • To invoke an instruction on another program, just use the cpi module on the crate, here, puppet::cpi::set_data.
  • Our Accounts struct has a new type, CpiAccount, containing the target program's Puppet account. Think of CpiAccount exactly like ProgramAccount, except used for accounts not owned by the current program.

TIP

When using another Anchor program for CPI, make sure to specify the cpi feature in your Cargo.toml. If you look at the Cargo.toml for this example, you'll see puppet = { path = "../puppet", features = ["cpi"] }.

# Signer Seeds

Often it's useful for a program to sign instructions. For example, if a program controls a token account and wants to send tokens to another account, it must sign. In Solana, this is done by specifying "signer seeds" on CPI. To do this using the example above, simply change CpiContext::new(cpi_accounts, cpi_program) to CpiContext::new_with_signer(cpi_accounts, cpi_program, signer_seeds).

For more background on signing with program derived addresses, see the official Solana documentation (opens new window).

# Return values

Solana currently has no way to return values from CPI, alas. However, you can approximate this by having the callee write return values to an account and the caller read that account to retrieve the return value. In future work, Anchor should do this transparently.

# Conclusion

Now that you can have your programs call other programs, you should be able to access all the work being done by other developers in your own applications!

# Next Steps

Up until now, we've treated programs on Solana as stateless. In the next tutorial we will learn how to add a global state to our program.