- A program can hold another program’s upgrade authority through a PDA and upgrade it with a cross-program invocation (CPI) to the upgradeable loader. Multisig and governance programs use this to put upgrades behind their own approval rules.
- Through CPI, a program can call only the loader’s
Upgrade,SetAuthority,SetAuthorityChecked, andCloseinstructions. Writing a buffer, deploying a new program, and extending a program stay top-level instructions that your wallet sends. ExtendProgramneeds no authority signature, so your wallet can grow a PDA-controlled program before a larger build goes in. Each extension adds at least 10,240 bytes.- A buffer handed to the PDA can only be closed by the PDA. Give the upgrading program a close instruction, or the buffer’s rent stays locked.
- Once SIMD-0500 activates, the buffer has to hold an sBPFv3 build, whoever signs the upgrade. See Solana: Rebuild your programs for sBPFv3.
Main article
Every program deployed with the upgradeable loader,BPFLoaderUpgradeab1e11111111111111111111111, has an upgrade authority: the one address that can replace its code. The authority does not have to be a wallet. Set it to a program derived address (PDA), and only the program that derives the PDA can sign for it, which it does with invoke_signed. That program decides when an upgrade goes through.
This guide builds an upgrader program that holds a PDA authority, hands a second program to it, and upgrades that program to a larger build through CPI. The examples use Devnet through a Chainstack endpoint.
To follow along, you need a Solana node endpoint and the Agave 4.3.0 toolchain. See Solana tooling and Install the toolchain.
What a program can call on the loader
The runtime filters CPIs to the upgradeable loader by instruction:
A rejected CPI fails the calling program before the loader runs:
Write the upgrader program
The upgrader derives its authority PDA from the seedupgrade_authority and the admin’s address. Each admin gets a separate PDA, and the upgrader signs with it only when that admin signs the transaction. It has three instructions:
0— upgrade the target program from a buffer that the PDA owns.1— hand the upgrade authority back to the admin wallet.2— close a buffer that the PDA owns and return its lamports to the admin.
u32. The instruction builders in solana-loader-v3-interface need that crate’s wincode feature, which takes this program from 17 KB to about 50 KB and raises the rent to deploy it about threefold.
Build it for sBPFv3:
Write the program to upgrade
The target is a minimal program in a crate namedgreeter. Version 1 logs a fixed message:
src/lib.rs
cargo build-sbf --arch v3 and keep the version 2 source for later.
Deploy both programs
Deploy the upgrader and version 1 of the target:Hand the upgrade authority to the PDA
Derive the PDA for your wallet:Prepare the new build
Build version 2 and upload it to a buffer, then give the buffer to the same PDA. The loader requires the buffer and the program to have the same authority:Extend the program when the build grows
A program’s ProgramData account holds its current build and nothing more: after the version 1 deployment,solana program show reports a Data Length of 9,152 bytes, the size of that .so file. Version 2 is 14,592 bytes. An upgrade to a larger build fails with ProgramData account not large enough until you extend the program.
ExtendProgram cannot go through CPI, and it does not need the upgrade authority to sign, so your wallet sends it. Every extension adds at least 10,240 bytes, the minimum set by SIMD-0431:
Program was deployed in this block already, so wait for the next slot before you upgrade.
Upgrade through the upgrader
This client sends the upgrader’s instructions with Solana Kit. It derives the authority PDA and the target’s ProgramData address, then passes the accounts each instruction reads:upgrade.mjs
call.mjs script from Call the program now runs version 2:
Close buffers that the PDA owns
A buffer that you give to the PDA and never use for an upgrade still holds rent. Your wallet cannot close it, andsolana program close fails with Buffer account authority Some(<PDA>) does not match Some(<wallet>). Close it through the upgrader instead:
Hand the authority back
To return the target to direct wallet control, call the hand-back instruction. It usesSetAuthorityChecked, which requires the new authority to sign. The admin signs the transaction, so the authority cannot go to an address nobody controls:
Errors
Before you use this in production
- Gate the upgrade instruction. The example accepts any upgrade the admin signs, while a production upgrader checks multisig approvals, a timelock, or a governance vote before it calls
Upgrade. - Check what the buffer holds. The upgrader installs whatever bytes the buffer contains, so compare the buffer’s hash with your verified build, for example with
solana-verify get-buffer-hash. - Protect the upgrader itself. Whoever controls the upgrader’s own upgrade authority can replace its rules, so put that authority behind the same controls or make the upgrader immutable.
Additional resources
- Solana: Rebuild your programs for sBPFv3
- Solana: Program derived addresses and cross-program invocations
- SIMD-0431: minimum extend program size
- Agave CPI loader filter —
check_authorized_programin Agave 4.3.0