Hello Everyone,
First of all, Thanks to @frangio for creating this amazing video about upgradeable smart-contracts. If anyone is looking for a best resource to know about upgreadable smart-contracts then please go checkout the video.
Back to question now, so I'm trying below code to understand the upgradeable SC. I was able to test contract Box
successfully but when I'm trying to test contract BoxV2
then it is giving me the error.
My questions are : -
-
Is it possible to add a new state variable
uint d
and assigned a value to it just like I did incontract BoxV2
? If not then what's wrong here? -
In the error it is mentioned that
Move the assignment to the initializer
. How am I suppose to do it becausefunction initialize
only execute once and I can not overide it incontract BoxV2
?
OR
Does it mean that I can't inheritcontract Box
instead I have to put the entire code ofcontract Box
inBoxv2.sol
file with additional function(functions I want to add) and also initialize the variabled
insidefunction initialize
Error
Box (proxy)
value of result is: 13
✓ This is a negative case for function initialize to check if func initialize works then it wil return a value which is not equal to 3
value of result is: 13
✓ Func initialize works and it wil return a value which is equal to 13
BoxV2 (upgrade-proxy)
1) "before all" hook for "returns 35 as a result"
2 passing (1s)
1 failing
1) BoxV2 (upgrade-proxy)
"before all" hook for "returns 35 as a result":
Error: Contract `BoxV2` is not upgrade safe
contracts/Box.sol:43: Variable `d` is assigned an initial value
Move the assignment to the initializer
https://zpl.in/upgrades/error-004
at Object.assertUpgradeSafe (node_modules/@openzeppelin/upgrades-core/src/validate/query.ts:17:11)
at Object.deployImpl (node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy-impl.ts:31:3)
at Proxy.upgradeProxy (node_modules/@openzeppelin/hardhat-upgrades/src/upgrade-proxy.ts:36:22)
at Context.<anonymous> (test/boxv2-proxy-test.js:17:13)
Code to reproduce
// SPDX-License-Identifier: Unlicenced
pragma solidity ^0.8.4;
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract Box is Initializable, PausableUpgradeable, OwnableUpgradeable {
uint c;
function initialize() initializer public {
__Pausable_init();
__Ownable_init();
c = 10;
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function A(uint a, uint b) public view whenNotPaused returns (uint) {
return a + b + c;
}
}
// Box version v2
contract BoxV2 is Box {
uint d=20;
function B(uint e) public view whenNotPaused returns(uint) {
uint x = A(1,3) + d + e;
return x;
}
}
code for testing Box contract
// test/Box.proxy.js
// Load dependencies
const { expect } = require('chai');
let Box;
let box;
// Start test block
describe('Box (proxy)', function () {
before(async function () {
Box = await ethers.getContractFactory("Box");
// initialize only run once
box = await upgrades.deployProxy(Box, [], {initializer: 'initialize'});
//console.log("box is: ", box)
});
// Test case
it('This is a negative case for function initialize to check if func initialize works then it wil return a value which is not equal to 3', async function () {
// Test if the returned value is the same one
// Note that we need to use strings to compare the 256 bit integers
const result = await box.A(1,2);
//await result.wait();
console.log(" value of result is: ", result.toString());
expect(result.toString()).not.equal('3');
});
it('Func initialize works and it wil return a value which is equal to 13', async function () {
// Test if the returned value is the same one
// Note that we need to use strings to compare the 256 bit integers
const result = await box.A(1,2);
console.log(" value of result is: ", result.toString());
expect(result.toString()).to.equal('13');
});
});
code for testing contract BoxV2
// test/BoxV2.proxy.js
// Load dependencies
const { expect } = require('chai');
let Box;
let BoxV2;
let box;
let boxV2;
// Start test block
describe('BoxV2 (upgrade-proxy)', function () {
before(async function () {
Box = await ethers.getContractFactory("Box");
BoxV2 = await ethers.getContractFactory("BoxV2");
box = await upgrades.deployProxy(Box, [], {initializer: 'initialize'});
boxV2 = await upgrades.upgradeProxy(box.address, BoxV2);
});
// Test case
it('returns 35 as a result when successful', async function () {
const result = await boxV2.B(1);
console.log("result is: ", result.toString());
expect((result).toString()).to.equal('35');
});
});
Environment
Hardhat
Please help.