Skip to main content
Smart contracts are immutable: once deployed, the code cannot be changed, only the state. That is a problem when you need to fix a bug, add a feature, or meet a new requirement. An upgradable contract puts the logic behind a proxy, so you can replace the logic while the address and the state stay the same. This guide shows you how to build, deploy, and upgrade such a contract with OpenZeppelin’s Upgrades plugin and Hardhat.

Prerequisites

  • You have deployed a contract with Hardhat before.
  • You can read JavaScript.

Instructions

Building an Upgradable Smart Contract

Deployment goes through OpenZeppelin’s deployProxy.

1. Install upgrades plugin

In your Hardhat project, install the OpenZeppelin package that holds the upgrade logic:
Then require it in hardhat.config.js:

2. Create the contract

Place this SimpleStorage.sol in the contracts/ directory of your project:

3. Write the deployment script

The deployment script goes in scripts/, not in ignition/modules/. Create scripts/upgrade_storage.js with the following code:

4. Deploy the contract

Upgradable contracts are deployed with npx hardhat run, not with ignition deploy:
The script prints the proxy address. Save it, that is the address you keep using.

5. Interact with the contract

Open the Hardhat console:
Then check the deployment. Replace 0xYourProxyAddress with the proxy address printed by your deployment script:
The stored value is 10, so the proxy was initialized correctly.

Upgrade previously deployed contract

Now add a feature to the deployed contract: incrementing the stored value.

1. Create a new contract

Create a second Solidity contract in contracts/ called SimpleStorageV2:
The contract is the same as the first one, with a different name and the added increment function.

2. Upgrade previously deployed contract

OpenZeppelin’s upgradeProxy points the existing proxy at the new logic. Create scripts/upgrade_storagev2.js:

3. Run the upgrade

Run the upgrade script against the same network:
The proxy now runs the new logic, at the same address and with the same state.

4. Interact with the contract

Back in the Hardhat console, increment() is callable. Use the same deployed proxy address in place of 0xYourProxyAddress:
The contract kept its address and its stored value across the upgrade, and the value only changed when increment() was called. For how proxies do this, see the OpenZeppelin documentation.