Lift the CPI caller restriction
- Idea
- Draft
- Review
- Accept
- Implement
- Active
- Withdrawn
Decision brief
Why this proposal matters
Remove the check which forces CPI callers to have the program account of the callee available to them as an instruction account.
Proposal at a glance
What changes
- In CPI a feature gate must switch the search for the program id of the callee from the instruction accounts of the caller to the account list of the transaction.
- Remove the check which forces CPI callers to have the program account of the callee available to them as an instruction account.
Stakeholder map
Who is affected
Builders & client teams Medium impact
Dapp developers who wish to benefit from the lifting of the restriction shall:
Action requirement unknownValidators & operators Impact unknown
No proposal-specific evidence was found for this group.
Action requirement unknownUsers & stakers Medium impact
Transaction building should append the required program accounts, which are not passed as instruction accounts, at the end of the transaction accounts list. How the dapps describe which callee prorgams they require to be present in the transaction is explicitly left unspecified.
Action may be requiredGovernance & ecosystem Impact unknown
No proposal-specific evidence was found for this group.
Action requirement unknownExact source revision
Full proposal document
0616093b2952Summary
Remove the check which forces CPI callers to have the program account of the callee available to them as an instruction account.
Motivation
The restriction is purely historical and not necessary for the protocol or its implementations.
Removing it would improve composability and reduce transaction building complexity because program accounts would no longer be passed all the way from transaction level instructions to the inner most CPI instructions.
Additionally, not passing these program accounts is a significant reduction in data copied during serialization as program accounts of loader v1, v2 and v4 contain the entire 10 MB ELF. This massively reduces the CU cost for nested CPI and disincentivizes programs from accessing program accounts, which could later allow us to remove program loading from transaction loading.
Example transaction
- Accounts:
- Fee payer
- Caller Program
- Callee Program
- Token
- Transaction-level instruction:
- Program Account: Caller Program
- Instruction Accounts:
- Callee Program (this could be removed)
- Token
- CPI:
- Program Account: Callee Program
- Instruction Accounts:
- Token
Currently, in order to be used as a program account for a nested instruction the callee program must be passed as an instruction account to all outer instructions recursively.
New Terminology
None.
Detailed Design
In CPI a feature gate must switch the search for the program id of the callee from the instruction accounts of the caller to the account list of the transaction.
Invoking a program in CPI which was un/re/deployed in the same transaction is prevented by the "delay visibility" feature and thus unproblematic.
Alternatives Considered
None.
Impact
See motivation and security considerations.
Dapp developers who wish to benefit from the lifting of the restriction shall:
- Hard-code the callee address in the CPI call, in case they want a static dispatch
- Use the instruction data, not instruction accounts, to receive the callee address as a parameter, in case they want a dynamic dispatch
Transaction building should append the required program accounts, which are not passed as instruction accounts, at the end of the transaction accounts list. How the dapps describe which callee prorgams they require to be present in the transaction is explicitly left unspecified.
Security Considerations
For a program to be allowed to modify an account it must be the owner and the
instruction account must have been passed in with the is_writable flag set.
Some programs take custody of an accounts ownership and allow control via a
signer authority. In these cases the instruction account must have been passed
in with the is_signer flag set additionally. Both these flags can only be set
in the inner most nested CPI if all parent CPIs did also set them. However,
they can not be controlled at per top-level-instruction. All of these
instructions share the same flags throughout the transaction.
This means the CPI caller restriction has been used as a security feature to prevent a top-level-instruction from calling into a program which has custody of an account that another top-level-instruction required as a signer. Removing this restriction would require a lot more care to be taken in transaction builing to prevent the relaxation from being exploited.
Backwards Compatibility
Programs remain unaffected the way they are, unless they want to profit from this change. In that case they can do one of the following:
Existing programs, which have hard-coded the callee statically and only need it
as any instruction account to satisfy the constraint imposed by the runtime,
can be fed a placeholder like NativeLoader1111111111111111111111111111111, in
order not to shift the indices of the other instruction accounts.
All other existing programs, which dynamically call whatever is passed in a specific instruction account will have to be updated and redeployed to benefit from the lifting of the restriction.
Evidence graph
Related proposals and rollout
One or more sources are unavailable.
Upstream review record
Upstream discussion & review
GitHub review is editorial context, not evidence of on-chain support, voting, or outcome.
PR #163 · merged SIMD-0163: Lift the CPI caller restriction 23 comments and reviews · Jan 19, 2026we could have a new message header value which defines how many of the trailing readonly accounts are invokable Good idea, however outside of this SIMD. This is only about CPI, it does not affect the message accounts.
GitHub ↗I agree. New transaction format needs to be thought through in great detail alongside https://github.com/solana-foundation/solana-improvement-documents/pull/172. I will draft something up in the next few weeks.
GitHub ↗should we specify that non-instruction non-payer accounts must be read-only and enforce this in sanitize? or specify that writable non-instruction non-payer accounts are demoted to read-only? it wouldnt really affect lock contention because the only reason to append a writable account is to be annoying, and they could be just as annoying by taking it as an instruction account, but it would mean we dont need to check if a non-instruction account was invoked to skip it during account saving
GitHub ↗I don't think this SIMD is that much related to account loading. It needs all accounts containing programs to be loaded in the program cache, that's it. And that happens anyway, independent of writeability and later account saving.
GitHub ↗It looks like there's consensus here. If no one objects within the next week, I'll merge this on 25 November.
GitHub ↗We think there is a risk that some programs rely on inspecting instructions to determine whether other instructions might make a CPI call to a particular program. For example, right now is it possible to use the instructions sysvar to inspect other Instructions executing in the same tx, and you can guarantee that an instruction doesn't make a CPI call to a program if that program is not listed on the Instruction. Lifting this restriction (i.e. allowing instructions to call into programs not listed as instruction accounts) would make such a check insufficient.
GitHub ↗We think there is a risk that some programs rely on inspecting instructions to determine whether other instructions might make a CPI call to a particular program. For example, right now is it possible to use the instructions sysvar to inspect other Instructions executing in the same tx, and you can guarantee that an instruction doesn't make a CPI call to a program if that program is not listed on the Instruction. Lifting this restriction (i.e. allowing instructions to call into programs not listed as instruction accounts) would make such a check insufficient. Related to this, the changes proposed in this SIMD expands the behaviour of what instructions can access in unpredictable ways, since
GitHub ↗I had thought that this feature only restricts at the level of top-level instruction, meaning that each program in a top-level instruction could CPI into other programs declared within the top-level instruction, but I was told this isn't the case. While I think it's OK to lift the CPI caller restriction at the level of each top-level instruction, if we lift it at the transaction level, there are some dangerous possibilities, as Febo mentioned. Currently, it's possible to see dangers by looking at top-level instructions. For example, if you see an instruction take in the system program and your wallet as a signer, then you know that program could drain your wallet. With this change, however,
GitHub ↗Provenance
Evidence & technical details
Rollout or chain data, source revisions, freshness and integrity. 1
Provenance
Evidence & technical details
Rollout or chain data, source revisions, freshness and integrity.SIMD
Deployment Status
HcW8ZjBezYYgvcbxNJwqv1t484Y2556qJsfNDWvJGZRHExact source revision
Sources & integrity
- Proposal document pinned_commit_blob
0616093b2952ed6de52c4a27d66aadd11d48d4f9 - simd-document document · current · Sep 11, 2026
0616093b2952ed6de52c4a27d66aadd11d48d4f9
One or more sources are unavailable.
simd.watch community discussion · SIMD-0163
Powered by Giscus · Sign in with GitHub to comment
Community comments load when this section approaches the viewport.