This guide uses the Anchor framework to demonstrate how to transfer SOL using a Cross Program Invocation (CPI). Included below are three different, but functionally equivalent implementations that you may come across when reading or writing Solana programs. Here is a final reference program on Solana Playground.
Starter Code
Here is a starter program on
Solana Playground.
The lib.rs
file includes the following program with a single sol_transfer
instruction.
The cpi.test.ts
file demonstrates how to invoke the custom sol_transfer
instruction and logs a link to the transaction details on SolanaFM.
The transaction details will show that the custom program was first invoked (instruction 1), which then invokes the System Program (instruction 1.1), resulting in a successful SOL transfer.
Transaction Details
You can build, deploy, and run the test of this example on Playground to view the transaction details on the SolanaFM explorer.
How to CPI with Anchor
In the starter code, the SolTransfer
struct specifies the accounts required by
the transfer instruction.
Anchor CpiContext
The sol_transfer
instruction included in the starter code shows a typical
approach for constructing CPIs using the
Anchor framework.
This approach involves creating a
CpiContext
,
which includes the program_id
and accounts required for the instruction being
called, followed by a helper function (transfer
) to invoke a specific
instruction.
The cpi_context
variable specifies the program ID (System Program) and
accounts (sender and recipient) required by the transfer instruction.
The cpi_context
and amount
are then passed into the transfer
function to
execute the CPI.
Invoke with Crate Helper
Under the hood, the CpiContext
example above is a wrapper around the
solana_program
crate's invoke
function which uses
system_instruction::transfer
to build the instruction.
The example below demonstrates how to use the invoke()
function to make a CPI
to the transfer instruction of the System Program using the
system_instruction::transfer
method.
First, add these imports to the top of lib.rs
:
Next, modify the sol_transfer
instruction with the following:
This implementation is functionally equivalent to the previous example.
Invoke with Instruction
You can also manually build the instruction to pass into the invoke()
function. This is useful when there is not a crate available to help build the
instruction you want to invoke.
This approach requires you to manually specify the AccountMeta
s required by
the instruction and correctly create the instruction data buffer.
The sol_transfer
instruction below is a fully expanded equivalent of the
previous two examples.
The sol_transfer
instruction above replicates this
example of manually building a
SOL transfer instruction. It follows the same pattern as building an
instruction to add to a transaction.
When building an instruction in Rust, use the following syntax to specify the
AccountMeta
for each account: