The blockchain world is constantly evolving, and with it, the need for dynamic smart contracts has become increasingly apparent. Traditional smart contracts are immutable, meaning that once deployed, their code and logic cannot be altered. While this ensures security and trust, it also poses challenges when bugs need fixing or new features are to be added.
This is where upgradable smart contracts come into play, offering flexibility without compromising existing data. In this guide, we’ll explore how to create and upgrade smart contracts on the Conflux eSpace Testnet using Hardhat and OpenZeppelin’s Upgrades Plugin. By the end of this guide, you’ll know how to deploy an upgradable contract and seamlessly update its logic while maintaining stored data.
Why Upgradable Contracts?
The main advantage of upgradable contracts is that they separate logic from data storage. This design allows you to deploy a proxy contract that points to the logic of your smart contract while keeping storage in a consistent state. You can later deploy new versions of the logic contract and have the proxy point to the updated version.
This approach ensures:
- Fixes for bugs or vulnerabilities.
- Addition of new functionalities.
- No disruption or loss of previously stored data.
Getting Started
Before diving into the code, ensure you have the following tools installed:
- Node.js
- Hardhat
- OpenZeppelin Upgrades Plugin
Step 1: Clone the Repository and Install Dependencies
Clone the boilerplate repository for this project and install the necessary packages:
git clone https://github.com/Vikash-8090-Yadav/Conflux-Tutorial
cd upgradableContract
npm install
This repository comes pre-configured with Hardhat and the OpenZeppelin Upgrades plugin.
Step 2: Writing the Initial Smart Contract (Num1.sol)
Let’s create a simple contract to store and retrieve a number.
Create a file named Num1.sol inside the contracts folder:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
contract Num1 {
uint256 public num;
function update (uint256 _num) public {
num = _num;
}
function get()public view returns(uint256){
return num;
}
}
Step 3: Deploying the Initial Contract
Write a deployment script to deploy Num1 using a proxy.
Create a new file scripts/deploy-num1.js:
const hre = require("hardhat");
const {ethers,upgrades} = require("hardhat");
// const fs = require('fs');
async function main() {
const Num1 = await hre.ethers.getContractFactory("Num1")
const num1 = await upgrades.deployProxy(Num1, [12],{
initializer:"update"
});
await num1.waitForDeployment();
console.log("num1 deployed to:", await num1.getAddress())
// fs.writeFileSync('./config.js', `export const marketplaceAddress = "${nftMarketplace.address}"`)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Run the script:
npx hardhat run scripts/deploy-num1.js --network confluxTestnet
Ensure your hardhat.config.js file includes the Conflux eSpace Testnet configuration:
require("@openzeppelin/hardhat-upgrades");
require("@nomicfoundation/hardhat-ethers");
module.exports = {
solidity: "0.8.10",
networks: {
confluxTestnet: {
url: "https://evmtestnet.confluxrpc.com",
chainId: 71,
accounts: ["YOUR_PRIVATE_KEY"],
},
},
};
Step 4: Upgrading the Contract (Num2.sol)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
contract Num2 {
uint256 public num;
function update (uint256 _num) public {
num = _num;
}
function get()public view returns(uint256){
return num;
}
function inc() public{
num = num+1;
}
}
Step 5: Deploying the Upgrade
Create a new file scripts/upgrade-num1.js:
const hre = require("hardhat");
const {ethers,upgrades} = require("hardhat");
// const fs = require('fs');
async function main() {
const Num2 = await hre.ethers.getContractFactory("Num2")
const num1 = await upgrades.upgradeProxy('0xb2873916D01A48D0E7A588F6E2aA411f2B66d5b0',Num2);
console.log("Num1 updated sucesfully!")
// fs.writeFileSync('./config.js', `export const marketplaceAddress = "${nftMarketplace.address}"`)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Run the script:
npx hardhat run scripts/upgrade-num1.js --network confluxTestnet
Step 6: Testing the Upgrade
Use the Hardhat console to interact with your upgraded contract.
Start the Console
Run the following command to start the Hardhat console:
npx hardhat console --network confluxTestnet
Attach to the Upgraded Contract
Attach to your upgraded contract using its deployed proxy address:
const instance = await ethers.getContractAt("Num2", "DEPLOYED_PROXY_ADDRESS");
Call the inc Function and Check the Updated Value
Invoke the inc function to increment the stored number and verify the updated value:
await instance.inc();
console.log(await instance.get()); // Should return the incremented value
Conclusion
Congratulations! You’ve successfully deployed and upgraded a smart contract on the Conflux eSpace Testnet. By using Hardhat and OpenZeppelin, you’ve ensured your contracts are future-proof, allowing seamless updates while retaining existing data.
If you enjoyed this tutorial or have any questions, feel free to share your feedback. Keep building!
GitHub repo: https://github.com/Vikash-8090-Yadav/Conflux-Tutorial/tree/main/upgradableContract