A Cross-Program Invocation (CPI) is when one program calls an instruction on another program during execution. CPIs enable composability: any program's instructions can be invoked by any other program on the network.
Cross-program invocation example
CPIs without PDA Signers
Using the invoke function, Anchor CpiContext, Native Rust examples, SOL transfer CPI examples.
CPIs with PDA Signers
Using invoke_signed with signer seeds, Anchor CpiContext with PDA, Native Rust PDA signing examples.
CPI Execution and Privileges
11-step CPI execution flow, privilege extension rules, reentrancy, account validation.
CPI Cost Model and Data Sync
CPI cost formula, serialization costs, account data synchronization, PDA signing, return data.
Key facts
- Two functions:
invoke(no PDA signing) andinvoke_signed(with PDA signing). - Privilege extension: Account privileges (signer, writable) extend from caller to callee. A callee cannot escalate privileges beyond what the caller passed.
- Shared compute budget: A callee's CU consumption reduces the caller's remaining budget.
- Reentrancy: Direct self-recursion is allowed (A->A->A). Indirect
reentrancy is not (A->B->A returns
ReentrancyNotAllowed).
Limits
| Limit | Value | Source |
|---|---|---|
| Max instruction stack depth | 5 (9 with SIMD-0268) | MAX_INSTRUCTION_STACK_DEPTH, MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 |
| CPI invocation cost | 1,000 CUs (946 with SIMD-0339) | DEFAULT_INVOCATION_COST, INVOKE_UNITS_COST_SIMD_0339 |
| Max PDA signers per CPI | 16 | MAX_SIGNERS |
| Max CPI instruction data | 10 KiB (10,240 bytes) | MAX_INSTRUCTION_DATA_LEN |
| Max return data | 1,024 bytes | MAX_RETURN_DATA |
| Max CPI account infos | 128 (255 with SIMD-0339)* | MAX_CPI_ACCOUNT_INFOS, MAX_CPI_ACCOUNT_INFOS_SIMD_0339 |
| CPI serialization cost | 1 CU per 250 bytes | cpi_bytes_per_unit |
| Max account data realloc per CPI | 10,240 bytes (10 KiB) | MAX_PERMITTED_DATA_INCREASE |
invoke vs invoke_signed
Solana provides two functions for making CPIs:
| Function | Use case | PDA signing |
|---|---|---|
invoke | CPIs where all required signers have already signed the original transaction | No |
invoke_signed | CPIs where the calling program needs to sign on behalf of a PDA it owns | Yes, via signer seeds |
Under the hood, invoke simply calls invoke_signed with an empty signer seeds
array. Use invoke when you don't need PDA signing, and invoke_signed when
the program must authorize an action on behalf of a PDA.
Both functions ultimately trigger the same syscall
(sol_invoke_signed_rust)
and flow through the same runtime path
(cpi_common).
The only difference is whether signer seeds are provided. When seeds are
provided, the runtime derives PDA pubkeys and adds them to the set of valid
signers before privilege checking.
Is this page helpful?