Passing Remaining Accounts
Forward remaining_accounts to CPIs when the callee expects variable account lists (routing, oracles, multi-hop swaps).
Search across all documentation pages
Forward remaining_accounts to CPIs when the callee expects variable account lists (routing, oracles, multi-hop swaps).
let mut cpi_ctx = CpiContext::new(program, accounts);
cpi_ctx = cpi_ctx.with_remaining_accounts(
ctx.remaining_accounts.iter().map(|a| a.to_account_info()).collect(),
);When to reach for this: Callee program documents accounts beyond its fixed IDL layout.
use anchor_lang::prelude::*;
declare_program!(target);
pub const MAX_REMAINING: usize = 8;
#[error_code]
pub enum RelayError {
#[msg("Remaining account has unexpected owner")]
BadAccount,
#[msg("Too many remaining accounts")]
TooMany,
}
#[derive(Accounts)]
pub struct Relay<'info> {
pub admin: Signer<'info>,
pub target_program: Program<'info, target::program::Target>,
}
pub fn relay(ctx: Context<Relay>) -> Result<()> {
require!(
ctx.remaining_accounts.len() <= MAX_REMAINING,
RelayError::TooMany
);
for acc in ctx.remaining_accounts.iter() {
require_keys_eq!(*acc.owner, target::ID, RelayError::BadAccount);
}
let mut cpi_ctx = CpiContext::new(
ctx.accounts.target_program.to_account_info(),
target::cpi::accounts::Handle { admin: ctx.accounts.admin.to_account_info() },
);
cpi_ctx = cpi_ctx.with_remaining_accounts(ctx.remaining_accounts.to_vec());
target::cpi::handle(cpi_ctx)?;
Ok(())
}What this demonstrates:
Clients append accounts after fixed IDL accounts in transaction metas. Mismatch causes callee failure or security bugs.
Iterating large remaining sets costs CU; cap count with require!(len <= MAX).
| Alternative | Use When | Don't Use When |
|---|---|---|
| Fixed account struct | Known layout | Dynamic routers |
| Account metas in instruction data | Rare patterns | Standard remaining_accounts |
Custom codes in IDL plus #[msg] strings.
emit_cpi! costs more CU but improves capture reliability.
Avoid except behind feature flags.
From IDL via Anchor or Codama codegen.
Yes, #[event] types are listed.
Yes with @ MyError::Variant.
Each log line and formatted string.
anchor test expecting error codes.
Yes, but not an API contract; use events.
See Related for remaining cpi.
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 16, 2026