Back to Blog

Secure the Solana Ecosystem (3) — Program Upgrade

March 27, 2022

0. Review

1. Overview

In the previous blog, we introduced how to implement the cross-program invocation via function invoke and function invoke_signed. In this post, we will introduce another basic topic - Program Upgrade.

2. Deploy an Upgradable Program

By default, all the deployed programs are deployed with the BPFLoaderUpgradeable loader in Solana, which means they can be upgraded. Apart from this, Solana provides the option --final to ensure that the deployed program is immutable. In this case, BPFLoader2 loader will be used to deploy the program. Once deployed, the program can not be upgraded.

The BPFLoaderUpgradeable loader program manages three types of accounts, program accounts, program data accounts, and buffer accounts. In the process of deployment, a buffer account is created at first. After that, the Solana CLI loads the bytecodes of the target program and writes them into the buffer account. Eventually, the bytecodes stored in the buffer account will be copied into the program data account.

Thus, buffer accounts are used by the Upgradeable BPF loader to temporarily store the bytecodes during the deployment process, program data accounts store the real data of a program, and program accounts work as proxy which point to the corresponding program data accounts.

In the following, let’s go through a few samples on how to upgrade your program. All the code used in this post can be find here.

2.1 Code Review (Rectangle_Area)

In this sample contract, we define a struct named Rectangle, which contains three attributes: width, height, and area from line 13 to line 17. We also define an area() function for Rectangle. The function area() is to calculate the area of the rectangle using width and height.

In function process_instruction(), we first use function unpack_u32() to extract the width and the height of the rectangle from the instruction data (line 45 - line 46). In line 51, the account used to store the data of the rectangle is extracted. In line 59, function try_from_slice_unchecked() deserializes the data of the account with the type of struct Rectangle. Next, we assign the data to corresponding fields of the struct and calculate the value of area (line 61 - line 63). The last step is to serialize the data, and write it back to the data account.

The deployed program can be find in the following link.

https://explorer.solana.com/address/84mMqHRTQT6b2vfsD7XRxVKA3XMd7xoEXFdF6pLNw8y?cluster=testnet

2.2 Send Transaction

On the client side, we first send a transaction to create an account for storing data. We set the space size as 1,024 bytes to ensure it is enough.

We then send another transaction to calculate the area of the rectangle and store it into the data account.

The transaction can be found in the following link.

https://explorer.solana.com/tx/4PybjXGRpuPKpak7FAz4BKMbcQCWmway69zAxtTnpFRuTTg7onyxW7agSe6ETx44iAGexbgnBUa8WdzdTTQSawJ3?cluster=testnet

3. Upgrade

Now, we want to add a new function in the program, which is to calculate the perimeter of the rectangle.

After compile the updated project, we upgrade the deployed program on-chain with the following command.

solana program deploy /path/to/program.so --program-id <PROGRAM_ID>

The contract can be upgraded directly and you can view the related transaction below.

https://explorer.solana.com/tx/4Dm9v4zMiijKjQBhatx1D9xbV9PvMLdaonUWLaC2VwzkFvzdgorzbX5vsy4VQ7VxSUmqadftjiDzbyUmXgQchYmk?cluster=testnet

We further send a transaction to the program to verify that it is upgraded successfully. The result shows the function works well

The related transaction can be found below.

https://explorer.solana.com/tx/21c2G7kPVktAtdUFkH3QwGVi7orajRmy5PJo1UxX1mmAMU68eUkNuLzWYJRBaTzwGi5DxeocYjHfpWiU4hcSFtpQ?cluster=testnet

4 Upgrade Again

This time we want to store the calculated perimeter into the data account. In this case, we add one more attribute perimeter into the struct Rectangle (line 18).

Since the struct of the data account is different from the previous one, we need to first read the data from the data account. We deserialize the stored data using the original struct (i.e., OldRectangle). After that, we assign the related attributes to the new struct (i.e., CurrentRectangle) and init the new attribute (i.e., perimeter).

Finally, we calculate the value of perimeter and assign it back to the update_account before serializing it into the account_data.

The transaction for upgrading can be found below.

https://explorer.solana.com/tx/5J3oKxZXtCi755gD7pMnVh48AFvmeVzRLPgJiyNn8JFeCKx1xpAfJtsi34zjyYKJmnMk8LtC3bcwfFSP7H2gtj5o?cluster=testnet

After upgrading, we can check the data stored in the account with the command solana account <DataAccount>. You can find the attributes are stored in the first 32 bytes. The whole upgrading process is finished.

5. Conclusion

In this article, we introduce how a program can be upgraded in Solana. We use different examples to illustrate the detail process. Stay tuned and more articles for Solana will be posted.


About BlockSec

BlockSec is a pioneering blockchain security company established in 2021 by a group of globally distinguished security experts. The company is committed to enhancing security and usability for the emerging Web3 world in order to facilitate its mass adoption. To this end, BlockSec provides smart contract and EVM chain security auditing services, the Phalcon platform for security development and blocking threats proactively, the MetaSleuth platform for fund tracking and investigation, and MetaSuites extension for web3 builders surfing efficiently in the crypto world.

To date, the company has served over 300 esteemed clients such as MetaMask, Uniswap Foundation, Compound, Forta, and PancakeSwap, and received tens of millions of US dollars in two rounds of financing from preeminent investors, including Matrix Partners, Vitalbridge Capital, and Fenbushi Capital.

Official website: https://blocksec.com/

Official Twitter account: https://twitter.com/BlockSecTeam

Sign up for the latest updates
Newsletter - April 2026
Security Insights

Newsletter - April 2026

In April 2026, the DeFi ecosystem experienced three major security incidents. KelpDAO lost ~$290M due to an insecure 1-of-1 DVN bridge configuration exploited via RPC infrastructure compromise, Drift Protocol suffered ~$285M from a multisig governance takeover leveraging Solana's durable nonce mechanism, and Rhea Finance incurred ~$18.4M following a business logic flaw in its margin-trading module that allowed circular swap path manipulatio

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly
Security Insights

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly

This BlockSec weekly security report covers eight attack incidents detected between April 20 and April 26, 2026, across Ethereum, Avalanche, Sui, Base, HyperLiquid, and MegaETH, with total estimated losses of approximately $7.04M. The highlighted incident is the $1.3M GiddyDefi exploit, where the attacker did not break any cryptography or use a flash loan but simply replayed an existing on-chain EIP-712 signature with the unsigned `aggregator` and `fromToken` fields swapped out for a malicious contract, demonstrating how partial signature coverage turns any historical signature into a generic permit. Other incidents include a $3.5M Volo Vault operator key compromise on Sui, a $1.5M Purrlend privileged-role takeover, a $413K SingularityFinance oracle misconfiguration, a $142.7K Scallop cross-pool index injection, a $72.35K Kipseli Router decimal mismatch, a $50.7K REVLoans (Juicebox) accounting pollution, and a $64K Custom Rebalancer arbitrary-call exploit.

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis
Security Insights

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis

This BlockSec deep-dive analyzes the KelpDAO $290M rsETH cross-chain bridge exploit (April 18, 2026), attributed to the Lazarus Group, tracing a causal chain across three layers: how a single-point DVN dependency enabled the attack, how DeFi composability cascaded the damage through Aave V3 lending markets to freeze WETH liquidity exceeding $6.7B across Ethereum, Arbitrum, Base, Mantle, and Linea, and how the crisis forced decentralized governance to exercise centralized emergency powers. The article examines three parameters that shaped the cascade's severity (LTV, pool depth, and cross-chain deployment count) and provides an exclusive technical breakdown of Arbitrum Security Council's forced state transition, an atomic contract upgrade that moved 30,766 ETH without the holder's signature.