Thank you. This is an example of my contracts @ericglau
I have a hardhat project. both contracts V1 and V2 are under ./contracts . The script is under ./scripts. And I'm using ganache app with seed phrase
1. Debug logs
me@me-MacBook-Pro example % npx hardhat run scripts/deploy_and_upgrade.ts --network ganache
Compiled 29 Solidity files successfully (evm target: paris).
Deploy Example V1 Contract to network ganache
@openzeppelin:upgrades:core manifest file: .openzeppelin/unknown-1337.json fallback file: .openzeppelin/unknown-1337.json +0ms
@openzeppelin:upgrades:core manifest file: .openzeppelin/unknown-1337.json fallback file: .openzeppelin/unknown-1337.json +8ms
@openzeppelin:upgrades:core fetching deployment of implementation d5edb5b0853a77ace3eb2cd2d1e49b9e29d6290d742707bbde85752d60948679 +0ms
@openzeppelin:upgrades:core initiated deployment transaction hash: 0xe13cb86c4265facc86f4db97df43563dc82c48df61c284600450daa0dee297b6 merge: false +43ms
@openzeppelin:upgrades:core polling timeout 60000 polling interval 5000 +0ms
@openzeppelin:upgrades:core verifying deployment tx mined 0xe13cb86c4265facc86f4db97df43563dc82c48df61c284600450daa0dee297b6 +1ms
@openzeppelin:upgrades:core succeeded verifying deployment tx mined 0xe13cb86c4265facc86f4db97df43563dc82c48df61c284600450daa0dee297b6 +1ms
@openzeppelin:upgrades:core verifying code in target address 0x93bf5a364BcCc6e5c6F0273cf3Cc219c467e80fe +0ms
@openzeppelin:upgrades:core code in target address found 0x93bf5a364BcCc6e5c6F0273cf3Cc219c467e80fe +1ms
The proxy of ExampleV1 Contract deployed to address 0xf8eeE0E663E0dc20072C97BB48d65fd5564bCC77
Preparing upgrade to ExampleV2
@openzeppelin:upgrades:core manifest file: .openzeppelin/unknown-1337.json fallback file: .openzeppelin/unknown-1337.json +56ms
@openzeppelin:upgrades:core manifest file: .openzeppelin/unknown-1337.json fallback file: .openzeppelin/unknown-1337.json +2ms
@openzeppelin:upgrades:core manifest file: .openzeppelin/unknown-1337.json fallback file: .openzeppelin/unknown-1337.json +4ms
@openzeppelin:upgrades:core fetching deployment of implementation 50cb387b9d03711341dd3f4777c8a775185df05eb1688e96a75239792a07937e +0ms
@openzeppelin:upgrades:core initiated deployment transaction hash: 0x359c7330967e40da82b4d02ef06e400396a425608be711df091fc0a39573872c merge: false +23ms
@openzeppelin:upgrades:core polling timeout 60000 polling interval 5000 +1ms
@openzeppelin:upgrades:core verifying deployment tx mined 0x359c7330967e40da82b4d02ef06e400396a425608be711df091fc0a39573872c +0ms
@openzeppelin:upgrades:core succeeded verifying deployment tx mined 0x359c7330967e40da82b4d02ef06e400396a425608be711df091fc0a39573872c +1ms
@openzeppelin:upgrades:core verifying code in target address 0x9BE75FB171c494D2B57BB61387B448631f0a3953 +0ms
@openzeppelin:upgrades:core code in target address found 0x9BE75FB171c494D2B57BB61387B448631f0a3953 +2ms
ExampleV2 implementation contract address 0x9BE75FB171c494D2B57BB61387B448631f0a3953
2. Network file unknown-1337.json
{
"manifestVersion": "3.2",
"proxies": [
{
"address": "0xf8eeE0E663E0dc20072C97BB48d65fd5564bCC77",
"txHash": "0x135c666c252501483574aa704a8da274f891be4738af23eefb5cf1870777192a",
"kind": "uups"
}
],
"impls": {
"d5edb5b0853a77ace3eb2cd2d1e49b9e29d6290d742707bbde85752d60948679": {
"address": "0x93bf5a364BcCc6e5c6F0273cf3Cc219c467e80fe",
"txHash": "0xe13cb86c4265facc86f4db97df43563dc82c48df61c284600450daa0dee297b6",
"layout": {
"solcVersion": "0.8.20",
"storage": [
{
"label": "value",
"offset": 0,
"slot": "0",
"type": "t_uint256",
"contract": "ExampleV1",
"src": "contracts/ExampleV1.sol:9"
}
],
"types": {
"t_address": {
"label": "address",
"numberOfBytes": "20"
},
"t_bool": {
"label": "bool",
"numberOfBytes": "1"
},
"t_bytes32": {
"label": "bytes32",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_bool)": {
"label": "mapping(address => bool)",
"numberOfBytes": "32"
},
"t_mapping(t_bytes32,t_struct(RoleData)25_storage)": {
"label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)",
"numberOfBytes": "32"
},
"t_struct(AccessControlStorage)32_storage": {
"label": "struct AccessControlUpgradeable.AccessControlStorage",
"members": [
{
"label": "_roles",
"type": "t_mapping(t_bytes32,t_struct(RoleData)25_storage)",
"offset": 0,
"slot": "0"
}
],
"numberOfBytes": "32"
},
"t_struct(InitializableStorage)47_storage": {
"label": "struct Initializable.InitializableStorage",
"members": [
{
"label": "_initialized",
"type": "t_uint64",
"offset": 0,
"slot": "0"
},
{
"label": "_initializing",
"type": "t_bool",
"offset": 8,
"slot": "0"
}
],
"numberOfBytes": "32"
},
"t_struct(RoleData)25_storage": {
"label": "struct AccessControlUpgradeable.RoleData",
"members": [
{
"label": "hasRole",
"type": "t_mapping(t_address,t_bool)",
"offset": 0,
"slot": "0"
},
{
"label": "adminRole",
"type": "t_bytes32",
"offset": 0,
"slot": "1"
}
],
"numberOfBytes": "64"
},
"t_uint64": {
"label": "uint64",
"numberOfBytes": "8"
},
"t_uint256": {
"label": "uint256",
"numberOfBytes": "32"
}
},
"namespaces": {
"erc7201:openzeppelin.storage.AccessControl": [
{
"contract": "AccessControlUpgradeable",
"label": "_roles",
"type": "t_mapping(t_bytes32,t_struct(RoleData)25_storage)",
"src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61",
"offset": 0,
"slot": "0"
}
],
"erc7201:openzeppelin.storage.Initializable": [
{
"contract": "Initializable",
"label": "_initialized",
"type": "t_uint64",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69",
"offset": 0,
"slot": "0"
},
{
"contract": "Initializable",
"label": "_initializing",
"type": "t_bool",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73",
"offset": 8,
"slot": "0"
}
]
}
}
},
"50cb387b9d03711341dd3f4777c8a775185df05eb1688e96a75239792a07937e": {
"address": "0x9BE75FB171c494D2B57BB61387B448631f0a3953",
"txHash": "0x359c7330967e40da82b4d02ef06e400396a425608be711df091fc0a39573872c",
"layout": {
"solcVersion": "0.8.20",
"storage": [
{
"label": "value",
"offset": 0,
"slot": "0",
"type": "t_uint256",
"contract": "ExampleV2",
"src": "contracts/ExampleV2.sol:8"
}
],
"types": {
"t_address": {
"label": "address",
"numberOfBytes": "20"
},
"t_bool": {
"label": "bool",
"numberOfBytes": "1"
},
"t_bytes32": {
"label": "bytes32",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_bool)": {
"label": "mapping(address => bool)",
"numberOfBytes": "32"
},
"t_mapping(t_bytes32,t_struct(RoleData)25_storage)": {
"label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)",
"numberOfBytes": "32"
},
"t_struct(AccessControlStorage)32_storage": {
"label": "struct AccessControlUpgradeable.AccessControlStorage",
"members": [
{
"label": "_roles",
"type": "t_mapping(t_bytes32,t_struct(RoleData)25_storage)",
"offset": 0,
"slot": "0"
}
],
"numberOfBytes": "32"
},
"t_struct(InitializableStorage)47_storage": {
"label": "struct Initializable.InitializableStorage",
"members": [
{
"label": "_initialized",
"type": "t_uint64",
"offset": 0,
"slot": "0"
},
{
"label": "_initializing",
"type": "t_bool",
"offset": 8,
"slot": "0"
}
],
"numberOfBytes": "32"
},
"t_struct(RoleData)25_storage": {
"label": "struct AccessControlUpgradeable.RoleData",
"members": [
{
"label": "hasRole",
"type": "t_mapping(t_address,t_bool)",
"offset": 0,
"slot": "0"
},
{
"label": "adminRole",
"type": "t_bytes32",
"offset": 0,
"slot": "1"
}
],
"numberOfBytes": "64"
},
"t_uint64": {
"label": "uint64",
"numberOfBytes": "8"
},
"t_uint256": {
"label": "uint256",
"numberOfBytes": "32"
}
},
"namespaces": {
"erc7201:openzeppelin.storage.AccessControl": [
{
"contract": "AccessControlUpgradeable",
"label": "_roles",
"type": "t_mapping(t_bytes32,t_struct(RoleData)25_storage)",
"src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61",
"offset": 0,
"slot": "0"
}
],
"erc7201:openzeppelin.storage.Initializable": [
{
"contract": "Initializable",
"label": "_initialized",
"type": "t_uint64",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69",
"offset": 0,
"slot": "0"
},
{
"contract": "Initializable",
"label": "_initializing",
"type": "t_bool",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73",
"offset": 8,
"slot": "0"
}
]
}
}
}
}
}
3. Hardhat config file
import "@nomicfoundation/hardhat-toolbox"
import "@openzeppelin/hardhat-upgrades"
import * as dotenv from "dotenv"
import { HardhatUserConfig } from "hardhat/config"
import "solidity-coverage"
dotenv.config()
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
networks: {
hardhat: {
blockGasLimit: 15000000,
accounts: {
mnemonic: "above north wish cable copper subject patch cage cause rent unfold twice",
count: 20
}
},
ganache: {
url: "http://127.0.0.1:7545",
blockGasLimit: 15000000,
accounts: {
mnemonic: "turn illness antique boost state tribe rebuild pave ivory mammal seat violin"
}
}
},
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
}
}
export default config
3. Script(s) deploy_and_upgrade.ts
const hre = require("hardhat")
import { ethers, upgrades } from "hardhat"
async function main() {
console.log(`Deploy Example V1 Contract to network ${hre.network.name}`)
const ExampleV1 = await ethers.getContractFactory("ExampleV1")
const exampleV1Instance = await upgrades.deployProxy(
ExampleV1, [ 2 ], { kind: "uups" }
)
await exampleV1Instance.waitForDeployment()
console.log(
`The proxy of ExampleV1 Contract deployed to address ${await exampleV1Instance.getAddress()}`
)
// Set the address of the proxy contrat
const proxyAddress = await exampleV1Instance.getAddress()
const ExampleV2 = await ethers.getContractFactory("ExampleV2")
console.log("Preparing upgrade to ExampleV2")
const exampleV2Address = await upgrades.prepareUpgrade(proxyAddress, ExampleV2)
console.log("ExampleV2 implementation contract address " + exampleV2Address)
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
4. Command that was run
Ganache running in the background using this compromised seed phrase "turn illness antique boost state tribe rebuild pave ivory mammal seat violin"
npx hardhat run scripts/deploy_and_upgrade.ts --network ganache
6. Installed versions of packages
npm list
├── @nomicfoundation/hardhat-ethers@3.0.4
├── @nomicfoundation/hardhat-toolbox@3.0.0
├── @openzeppelin/contracts-upgradeable@5.0.0
├── @openzeppelin/contracts@5.0.0
├── @openzeppelin/hardhat-upgrades@2.4.1
├── dotenv@16.3.1
├── ethereumjs-util@7.1.5
├── ethers@6.8.1
├── hardhat-contract-sizer@2.10.0
├── hardhat-gas-reporter@1.0.9
├── hardhat-log-remover@2.0.2
├── hardhat-solpp@1.0.1
├── hardhat@2.19.0
├── node-gyp@9.4.0
├── prettier-plugin-solidity@1.1.3
├── prettier@3.1.0
├── solhint@3.6.2
├── solidity-coverage@0.8.5
└── winston@3.11.0
npm list @openzeppelin/upgrades-core
└─┬ @openzeppelin/hardhat-upgrades@2.4.1
└── @openzeppelin/upgrades-core@1.31.1
The same result for OpenZeppelin version 4.9.0
7. Implementation contract(s)
ExampleV1
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
contract ExampleV1 is Initializable, UUPSUpgradeable, AccessControlUpgradeable {
uint256 public value;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(uint256 _value) public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
value = _value;
}
function _authorizeUpgrade(address newContractAddress) internal view override {
/** Anyone */
}
function setValue(uint256 _value) public {
value = _value;
}
function getValue() public view returns (uint256) {
return value;
}
}
ExampleV2 - the implementation updated contract with no Initializable
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
contract ExampleV2 is UUPSUpgradeable, AccessControlUpgradeable {
uint256 public value;
function _authorizeUpgrade(address newContractAddress) internal view override {
/** Anyone */
}
// Modified setValue method
function setValue(uint256 _newValue) public {
value = _newValue + 10;
}
// New increment method
function increment() public {
value = value + 1;
}
function getValue() public view returns (uint256) {
return value;
}
}