This repository demonstrates how to work with UUPS (Universal Upgradeable Proxy Standard) upgradeable contracts using Hardhat.
It includes:
- Deploying & Upgrading UUPS Proxies
- Working with Upgradeable Smart Contracts
- Two different approaches:
1οΈβ£ Single-task deployment & upgrade
2οΈβ£ Separate deployment & upgrade tasks
Ensure you have the following installed:
- Node.js v18+ (
β οΈ Avoid Node.js 20+ due to Hardhat compatibility issues). - Yarn or npm
- Hardhat
- =nil; blockchain RPC endpoint
Clone the repository and install the required dependencies:
git clone https://github.com/gitshreevatsa/HH-Upgradeable-plugin.git
cd HH-upgradeable-plugin
npm install
Before running deployment, you must compile the contracts:
npx hardhat compile
This step ensures all contracts are compiled and their ABI & Bytecode are generated.
Create a .env
file in the project root and add the following:
NIL_RPC_ENDPOINT=<your_nil_rpc_endpoint>
NIL=0x0001111111111111111111111111111111111110
If you want to upgrade contracts using the 2-task function process, store your private key & smart account address in .env
along with the above variables:
PRIVATE_KEY=<your_wallet_private_key>
SMART_ACCOUNT_ADDRESS=<your_smart_account_address>
This allows the system to use the same smart account for both deploying & upgrading.
β Deploys the initial implementation contract.
β Deploys a proxy contract pointing to the implementation.
β Initializes the contract (e.g., storing a number).
β Verification
- Fetch the proxy's implementation address using
getImplementation()
. - Check the stored value using
getValue()
.
β Deploys a new implementation contract (V2).
β Upgrades the proxy to use V2 instead of V1.
β Optionally calls reinitializeV2()
if required.
β Verification
- Ensure the proxy now points to the new implementation using
getImplementation()
. - Check if
getValue()
still returns the correct value (state persistence). - Test new functions in V2 (e.g.,
deposit()
).
/contracts
βββ UUPSUpgradeableExample.sol # V1 Implementation contract
βββ UUPSUpgradeableExampleV2.sol # V2 Implementation contract
βββ MyERC1967Proxy.sol # UUPS Proxy contract
/tasks
βββ test-uups-pattern.ts # Deploy & upgrade in one task
βββ test-modular.ts # Separate deploy & upgrade tasks
/utils
βββ deployAndUpgrade.ts # Functions to handle proxy deployment and upgrade logic
This method deploys & upgrades in a single Hardhat task.
npx hardhat test-uups-pattern
β Best for: Quick testing of upgradeability.
This method splits deployment & upgrading into two tasks.
npx hardhat deploy-uups-proxy
β Deploys implementation & proxy.
β Stores the proxy address in proxyAddress.txt
.
npx hardhat upgrade-uups-proxy --proxy <PROXY_ADDRESS>
β Deploys new implementation contract.
β Upgrades the proxy to use the new implementation.
To verify the smart account used, run:
npx hardhat smart-account
By default, this repository includes example contracts:
UUPSUpgradeableExample.sol
(V1)UUPSUpgradeableExampleV2.sol
(V2)
If you are working on your own upgradeable contract, replace these files with your own implementation.
π Make sure your contracts inherit from UUPSUpgradeable.sol
.
Modify tasks/test-uups-pattern.ts
or tasks/test-modular.ts
to import your custom contracts instead of the default ones:
const UUPSExample = require("../artifacts/contracts/UUPSUpgradeableExample.sol/UUPSUpgradeableExample.json");
const UUPSV2Example = require("../artifacts/contracts/UUPSUpgradeableExampleV2.sol/UUPSUpgradeableExampleV2.json");
Your contract may require different parameters during initialization or reinitialization.
π Update the initialization variables inside the deployment script:
const initializeArgs = ["myCustomParam1", 1234]; // Update with your contract's parameters
π Update the reinitialization arguments inside the upgrade script:
const reinitializeArgs = ["updatedParam", 5678]; // Modify as per your new implementation
If your V2 implementation does not require reinitialization, leave this as an empty array:
const reinitializeArgs = [];
Once the changes are made, follow the same deployment & upgrade steps outlined above.
After upgrading to V2, check:
1οΈβ£ State Persistence β getValue()
should return the same stored value as V1.
2οΈβ£ New Logic β You should be able to call new functions (e.g., deposit()
).
3οΈβ£ Implementation Address Change β getImplementation()
should show a new address.
β If all checks pass, the upgrade was successful! π
Try increasing feeCredit
inside deployAndUpgrade.ts
.
If you see "Node.js version not supported"
, downgrade to Node.js v18:
nvm install 18
nvm use 18
- π EIP-1967: Proxy Storage Slots
- π OpenZeppelin UUPS Proxies
- π Upgradeable Smart Contracts Guide
π§ This project is currently under maintenance. π§
There will be more upgrades with respect to flow, documentation, and additional features for a smoother experience.
Stay tuned for updates! π
For any issues or suggestions, feel free to open an issue or contribute.