So. We can’t add new variables to persist the created in the second one, in the third, and 4th level.
Because if i added storage in the second, and the address is from the first. In the third upgrade i lost the variables crated of the second level… at least… i can use many proxys from the first contract based in functions to access like ethernal storage concept.
Excelent. I suppose that i need to think diferent now… my questions is because in one exampe, says with Zeppelin Cli, you can persist storage from second level and first level in third level. I think this is a mistake.
You can definitely upgrade multiple times, as long as you don’t change the order or type of state variables. You can add new state variables in each upgrade.
In Upgrades Plugins, we just use the original contract to get the address of the proxy contract. This doesn’t limit how many times you can upgrade.
I recommend trying it out yourself with a simple contract.
I try several times, but i have an error when i tried to call the function that i created in V2 contract… in v4 (real v3, but i skip the number, sorry)
Also it happened if i write wrong the name of the function…
I created the following example upgrading Box -> BoxV2 -> BoxV3
Box.sol
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract Box {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
function version() public pure virtual returns (string memory) {
return "v1.0";
}
}
BoxV2.sol
// contracts/BoxV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Box.sol";
contract BoxV2 is Box {
function version() public pure virtual override returns (string memory) {
return "v2.0";
}
}
BoxV3.sol
// contracts/BoxV3.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Box.sol";
contract BoxV3 is Box {
function version() public pure virtual override returns (string memory) {
return "v3.0";
}
}
I tried the code, but i have some doubts… Let me take your example and modify a little… and ask for some question please…
BoxV1.sol
// contracts/BoxV1.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;
contract BoxV1 {
uint256 private value;
PetCHG[] pets;
struct PetCHG {
uint64 birthTime;
string dna;
}
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
function version() public pure virtual returns (string memory) {
return "v1.0";
}
function doMint(string memory _dna) public virtual {
PetCHG memory _pet = PetCHG({ dna: _dna, birthTime: uint64(block.timestamp)});
pets.push(_pet);
}
function getPetID( uint _petId ) public view returns(string memory dna){
PetCHG memory _pet = pets[_petId];
dna = _pet.dna;
}
}
BoxV2.sol
1.- Here i create a new variable… but in BoxV3 i can’t call this one, i need to extend also from BoxV2? and if yes, how deploy file needs to be?
2.- I comment a structure, i know that is not fully supported, but that is possible to add variables inside an struct?
// contracts/BoxV2.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;
import "./BoxV1.sol";
contract BoxV2 is BoxV1 {
uint256 private valueV2;
/*
struct PetCHG {
uint64 birthTime;
string dna;
}
function doMint(string memory _dna) public virtual override {
string memory myDna = string(abi.encodePacked(_dna, " chong"));
PetCHG memory _pet = PetCHG({ dna: myDna , birthTime: uint64(block.timestamp)});
pets.push(_pet);
}*/
function version() public pure virtual override returns (string memory) {
return "v2.0";
}
function storeV2(uint256 newValue) public {
valueV2 = newValue;
emit ValueChanged(newValue);
}
function retrieveV2() public view returns (uint256) {
return valueV2;
}
}
BoxV3.sol
// contracts/BoxV3.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;
import "./BoxV1.sol";
contract BoxV3 is BoxV1 {
function doMint(string memory _dna) public virtual override {
string memory myDna = string(abi.encodePacked(_dna, "chong"));
PetCHG memory _pet = PetCHG({ dna: myDna , birthTime: uint64(block.timestamp)});
pets.push(_pet);
}
function version() public pure virtual override returns (string memory) {
return "v3.0";
}
}
also, since first execution of my struct en BoxV1.doMint() i obtain a several console log of the next:
eth_getBlockByNumber
eth_getBlockByNumber
i read that is because in the struct doesn’t create a valid variable, but it’s only two… or it is because it’s not supported?
It depends how the struct is being used, whether it is a state variable or being used inside a mapping.
If you have a contract state variable of the struct type, then you most likely won’t be able to add any fields (unless the variable is the last one on your contract).
If the struct is contained in a mapping, then you probably should be able to add fields, since structs will be stored sparsely in storage.
I am not sure about how arrays are stored, so I assume you won't be able to add any fields to your struct.
Though you should do automated testing for upgrade safety to ensure that state is maintained across upgrades.
I am not sure why that is. I was just using Truffle Develop.
I'll replicate all with inheritance, it's work thanks a lot.
Last Question about your comment:
And according to the next page writing-upgradeable if i only deploy BoxV1 twice, the second time i deployed, i only need leave the variables, remove all new functions, and user override or write new functions...
I'm happy from this, thanks for all your help.
whatever the answer is, i think we could close as solution.
It is not upgrade safe to add fields to a struct being used in an array.
Implementation contracts can be stand alone or they can inherit from previous versions of the implementation contract. It is up to you for what works for your use case.
I tend to use stand alone. I only used inheritance as an example here.