---
title: CPIs without PDA Signers
description:
  How to use the invoke function for Cross-Program Invocations on Solana when no
  PDA signing is required, with Anchor CpiContext and Native Rust examples.
url: /docs/core/cpi/cpi-without-pda
type: tutorial
prerequisites:
  - /docs/core/cpi
  - /docs/core/instructions/instruction-structure
related:
  - /docs/core/cpi/cpi-with-pda
  - /docs/core/cpi/cpi-execution
  - /docs/core/cpi/cpi-cost-model
---

<Callout type="info" title="Summary">
  Use _rs`invoke`_ for CPIs where all required signers have already signed the
  original transaction. No PDA signing is needed. Signer and writable privileges
  extend from the caller to the callee.
</Callout>

## CPIs without PDA signers

When a CPI doesn't require PDA signers, the
[`invoke`](https://github.com/anza-xyz/solana-sdk/blob/clock%40v2.2.3/program/src/program.rs#L26)
function is used. The _rs`invoke`_ function calls the _rs`invoke_signed`_
function with an empty _rs`signers_seeds`_ array. The empty signers array
indicates that no PDAs are required for signing.

```rust title="Invoke function"
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
    invoke_signed(instruction, account_infos, &[])
}
```

The examples below make a CPI using Anchor and Native Rust. It includes a single
instruction that transfers SOL from one account to another.

#### Anchor

The following examples show two approaches to implementing CPIs in an
[Anchor](https://www.anchor-lang.com/docs) program. The examples are
functionally equivalent, but demonstrate different levels of abstraction.

- **Example 1**: Uses Anchor's _rs`CpiContext`_ and helper function.
- **Example 2**: Uses the _rs`system_instruction::transfer`_ function from the
  _rs`solana_program`_ crate.
- **Example 3**: Constructs the CPI instruction manually. This approach is
  useful when no crate exists to help build the instruction you want to invoke.

<CodeTabs>

```rs !! title="Example 1"
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>,
}
```

```rs !! title="Example 2"
use anchor_lang::prelude::*;
use anchor_lang::solana_program::{program::invoke, system_instruction};

declare_id!("BrcdB9sV7z9DvF9rDHG263HUxXgJM3iCQdF36TcxbFEn");

#[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 instruction =
            &system_instruction::transfer(&from_pubkey.key(), &to_pubkey.key(), amount);

        invoke(instruction, &[from_pubkey, to_pubkey, program_id])?;
        Ok(())
    }
}

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

```rs !! title="Example 3"
use anchor_lang::prelude::*;
use anchor_lang::solana_program::{program::invoke, instruction::Instruction};

declare_id!("BrcdB9sV7z9DvF9rDHG263HUxXgJM3iCQdF36TcxbFEn");

#[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();

        // Prepare instruction AccountMetas
        let account_metas = vec![
            AccountMeta::new(from_pubkey.key(), true),
            AccountMeta::new(to_pubkey.key(), false),
        ];

        // SOL transfer instruction discriminator
        let instruction_discriminator: u32 = 2;

        // Prepare instruction data
        let mut instruction_data = Vec::with_capacity(4 + 8);
        instruction_data.extend_from_slice(&instruction_discriminator.to_le_bytes());
        instruction_data.extend_from_slice(&amount.to_le_bytes());

        // Create instruction
        let instruction = Instruction {
            program_id: program_id.key(),
            accounts: account_metas,
            data: instruction_data,
        };

        // Invoke instruction
        invoke(&instruction, &[from_pubkey, to_pubkey, program_id])?;
        Ok(())
    }
}

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

```ts !! title="Test"
import * as anchor from "@coral-xyz/anchor";
import { BN, Program } from "@coral-xyz/anchor";
import { Cpi } from "../target/types/cpi";
import { Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";

describe("cpi", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.Cpi as Program<Cpi>;
  const sender = provider.wallet as anchor.Wallet;
  const recipient = new Keypair();

  const transferAmount = 0.01 * LAMPORTS_PER_SOL;

  it("SOL Transfer Anchor", async () => {
    const transactionSignature = await program.methods
      .solTransfer(new BN(transferAmount))
      .accounts({
        sender: sender.publicKey,
        recipient: recipient.publicKey
      })
      .rpc();

    console.log(`\nTransaction Signature: ${transactionSignature}`);
  });
});
```

</CodeTabs>

#### Rust

The following example shows how to make a CPI from a program written in Native
Rust. It includes a single instruction that transfers SOL from one account to
another. (The test file uses [LiteSVM](https://github.com/LiteSVM/litesvm) to
test the program.)

<CodeTabs>

```rs !! title="Example"
use borsh::BorshDeserialize;
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
};

// Declare program entrypoint
entrypoint!(process_instruction);

// Define program instructions
#[derive(BorshDeserialize)]
enum ProgramInstruction {
    SolTransfer { amount: u64 },
}

impl ProgramInstruction {
    fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
        Self::try_from_slice(input).map_err(|_| ProgramError::InvalidInstructionData)
    }
}

pub fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Deserialize instruction data
    let instruction = ProgramInstruction::unpack(instruction_data)?;

    // Process instruction
    match instruction {
        ProgramInstruction::SolTransfer { amount } => {
            // Parse accounts
            let [sender_info, recipient_info, system_program_info] = accounts else {
                return Err(ProgramError::NotEnoughAccountKeys);
            };

            // Verify the sender is a signer
            if !sender_info.is_signer {
                return Err(ProgramError::MissingRequiredSignature);
            }

            // Create and invoke the transfer instruction
            let transfer_ix = system_instruction::transfer(
                sender_info.key,
                recipient_info.key,
                amount,
            );

            invoke(
                &transfer_ix,
                &[
                    sender_info.clone(),
                    recipient_info.clone(),
                    system_program_info.clone(),
                ],
            )?;

            Ok(())
        }
    }
}
```

```ts !! title="Test"
import * as path from "path";
import {
  Keypair,
  LAMPORTS_PER_SOL,
  PublicKey,
  SystemProgram,
  Transaction,
  TransactionInstruction
} from "@solana/web3.js";
import { LiteSVM } from "litesvm";

test("sol transfer cpi", () => {
  const svm = new LiteSVM();

  const programId = PublicKey.unique();
  const programPath = path.join(__dirname, "program.so");
  svm.addProgramFromFile(programId, programPath);

  // Create sender and recipient
  const sender = new Keypair();
  const recipient = new Keypair();

  // Fund sender
  const amount = BigInt(LAMPORTS_PER_SOL);
  svm.airdrop(sender.publicKey, amount); // 1 SOL

  // Create instruction data buffer
  const transferAmount = amount / BigInt(2); // 0.5 SOL
  const instructionIndex = 0; // instruction index 0 for SolTransfer enum

  const data = Buffer.alloc(9); // 1 byte for instruction enum + 8 bytes for u64
  data.writeUInt8(instructionIndex, 0); // first byte identifies the instruction
  data.writeBigUInt64LE(transferAmount, 1); // remaining bytes are instruction arguments

  // Create instruction
  const instruction = new TransactionInstruction({
    programId,
    keys: [
      { pubkey: sender.publicKey, isSigner: true, isWritable: true },
      { pubkey: recipient.publicKey, isSigner: false, isWritable: true },
      { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
    ],
    data
  });

  // Create and send transaction
  const transaction = new Transaction().add(instruction);
  transaction.recentBlockhash = svm.latestBlockhash();
  transaction.sign(sender);

  svm.sendTransaction(transaction);

  // Check balances
  const recipientBalance = svm.getBalance(recipient.publicKey);
  const senderBalance = svm.getBalance(sender.publicKey);

  const transactionFee = BigInt(5000);
  expect(recipientBalance).toBe(transferAmount);
  expect(senderBalance).toBe(amount - transferAmount - transactionFee);
});
```

</CodeTabs>
