Every cross-program invocation on Solana starts with the calling program copying information the runtime already has. For each account it forwards, the program builds an account meta and an account info, then hands both arrays to a syscall that reads them back. SIMD-0670 proposes to replace all of that with a 4-byte index.
SIMD-0670, merged on 24 October with status Review, adds sol_invoke_signed_v2 next to today’s invoke. To see what it saves, we wrote a small Pinocchio program that prepares the same CPI both ways and ran it in solana-sbpf, the virtual machine Agave runs programs in, counting every instruction up to the syscall. The v2 path costs about a third as much per account, falls to a constant 17 to 20 compute units when the account list is fixed, and avoids a stack limit that today’s path hits before its own documented maximum.
Where the 90 bytes come from
A program receives its accounts as a serialized buffer and, to call another program, has to describe the accounts it forwards twice. The Rust interface takes an AccountMeta per account, a 32-byte address plus two flags, and an account info; the proposal counts 34 and 56 bytes for them. The C interface, which Pinocchio uses, replaces the address with a pointer, so its meta is 16 bytes with padding, and its account info is 56:
The new record is an index into the caller’s own instruction accounts plus the two flags. The runtime already holds those accounts, so it looks them up instead of reading copies:
#[repr(C)]
pub struct CpiAccount {
pub account_index: u16, // index in the current instruction
pub is_writable: u8,
pub is_signer: u8,
}
pub fn sol_invoke_signed_v2(instruction_addr: u64, signers_seeds_addr: u64, signers_seeds_len: u64);One naming trap: Pinocchio’s CPI module, solana-instruction-view, already has a type called CpiAccount. That one is the 56-byte account info. The proposal’s CpiAccount is the 4-byte record.
What preparing a CPI costs, measured
Our test program forwards N accounts to a callee in four ways: Pinocchio 0.11.2’s stack path (invoke_signed_with_bounds), its heap path for larger calls, v2 descriptors filled from the account flags, and a v2 descriptor list fixed at compile time. We built it with platform-tools v1.57 and ran it in solana-sbpf 0.26.0, configured as Agave configures it, with inputs serialized the way Agave serializes them. A stub syscall records the instruction count when the program reaches the CPI, and the count for reaching any syscall with the same accounts is subtracted. One executed instruction is one compute unit.
Today’s path costs about 60 compute units per account on the stack and about 66 on the heap. The v2 path, filling each descriptor from the account’s flags, costs about 18. When a program always forwards the same accounts, the descriptor list can live in read-only data, and the cost stops depending on the number of accounts at all.
The proposal’s own benchmark, a Pinocchio program calling a program that logs how many accounts it received, reports different absolute numbers: from 1,376 to 1,174 compute units at 8 accounts, and from 2,813 to 1,267 at 64. The shape is the same. Today’s path grows with every account. The proposed one barely moves.
The stack limit comes before the documented one
A Solana program function gets a 4 KiB stack frame. Pinocchio allows up to 64 accounts on its stack path, which is 3,584 bytes of 56-byte account infos. Its CPI helpers are #[inline(always)], though, so that array lands in the caller’s frame next to the caller’s own 16-byte metas:
cargo-build-sbf prints a frame-overflow error for the 60- and 64-account versions but still produces the binary. At run time both stop with an access violation in the stack frame. A program that needs more accounts moves to the heap path, which allocates 72 bytes per account: 9,216 bytes of the default 32 KiB heap at 128 accounts, Pinocchio’s ceiling. With v2 the whole list of 254 accounts fits in about a quarter of one frame.
What the proposal leaves unchanged
Only the program’s side gets cheaper. The runtime still translates every account, applies the same privilege checks and charges the same compute: the proposal keeps the per-account charge at 34 bytes, because the runtime reads the same information either way. Old programs keep working, since today’s syscall is not changed or removed.
Indices also mean a program can only forward accounts that are already in its own instruction. That is true of today’s invoke as well, which needs an account info the program can only get from its input, so nothing a program could do before is lost.
Where it stands
The proposal is merged with status Review. Agave’s implementation was open as #15979 at the time of writing, and a new syscall also needs a feature gate before mainnet programs can call it. Program libraries will need a v2 path of their own; our test declared the syscall by hand. For a busy program the saving adds up: compute units are what priority fees are priced against, and a router that forwards dozens of accounts on every call pays the per-account cost every time.
What this does not show
We measured the program’s preparation only. The syscalls were stubs, so the runtime’s CPI work and the callee are not included. The numbers are for one program built with one toolchain. Other code, including Anchor’s, will differ, though not in shape. The v2 syscall was not running anywhere we could test, so we checked that our descriptors matched the proposal’s layout rather than that a runtime accepted them.
CPI Overhead: Questions People Actually Ask
What does SIMD-0670 change about Solana CPIs?
It proposes a second invoke syscall, sol_invoke_signed_v2, that describes each account passed to a cross-program invocation with a 4-byte record: the account’s index in the caller’s own instruction plus writable and signer flags. Today a program passes two records per account, an account meta and an account info: 90 bytes through the Rust interface by the proposal’s count, 72 through the C interface Pinocchio uses. The runtime’s CPI logic and its compute charge stay the same; only the program-side preparation shrinks.
How many compute units does SIMD-0670 save?
In our measurement inside Solana’s VM, preparing a CPI the way Pinocchio does today costs about 60 compute units per forwarded account (529 for 8 accounts, 1,969 for 32). Filling v2 descriptors from the accounts costs about 18 per account (171 and 606), and a descriptor list fixed at compile time costs 17 to 20 regardless of size. The runtime’s own CPI charge is not included and does not change.
What is the maximum number of accounts a Solana CPI can forward from the stack?
With Pinocchio 0.11.2’s stack path, our test program ran with 56 forwarded accounts and failed with a stack access violation at 60 and 64. Each account costs 72 bytes of the 4 KiB stack frame, and Pinocchio inlines its account array into the caller. With SIMD-0670’s 4-byte descriptors, 254 accounts take 1,016 bytes.
Is sol_invoke_signed_v2 available on mainnet?
No. SIMD-0670 was merged on 24 October 2026 with status Review, and Agave’s implementation was an open pull request at the time of writing. Like every new syscall it needs a feature gate before programs can call it on mainnet.
Does sol_invoke_signed_v2 change CPI security?
The proposal says the security surface is identical: the runtime applies the same privilege checks and compute metering, and it still charges as if each account were 34 bytes. A program can only reference accounts that are already in its own instruction, which is also true of today’s invoke.
The one-line version
SIMD-0670 lets a program forward an account to a CPI with a 4-byte index instead of 72 to 90 bytes of copies, cutting preparation from about 60 compute units per account to about 18, or to a constant 20 for a fixed list, and keeping 254 accounts inside one stack frame.
Pinocchio itself is the library behind p-token, where the same idea of not copying what the runtime already has made the token program several times cheaper.
Sources: SIMD-0670 at commit c9ec366a; Agave #15979; Pinocchio 0.11.2 and solana-instruction-view 2.1.0; solana-sbpf 0.26.0. Measurements are from our own test program and harness.