I tried a more complex example without any success.
MyContract.sol
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "./MyBase.sol";
contract MyContract is Initializable, MyBase {
function initialize(string memory baseURI) public initializer {
MyBase.initializeMyBaseV1(
"MyContractCoin",
"MCC",
baseURI
);
}
uint256 private _someValue;
function getSomeValue() public view returns(uint256) {
return _someValue;
}
function setSomeValue(uint256 value) private {
_someValue = value;
}
}
MyBase.sol
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol";
contract MyBase is Initializable, ERC721PresetMinterPauserAutoIdUpgradeSafe {
uint256 private _versions = 0;
bytes32 public constant TEST1_ROLE = keccak256("TEST1_ROLE");
/**
* @dev Grants `DEFAULT_ADMIN_ROLE` and `MINTER_ROLE`to the account that
* deploys the contract.
*
* Token URIs will be autogenerated based on `baseURI` and their token IDs.
* See {ERC721-tokenURI}.
*/
function initializeMyBaseV1(string memory name, string memory symbol, string memory baseURI) public initializer {
super.initialize(name, symbol, baseURI);
_setupRole(TEST1_ROLE, _msgSender());
_versions += 1;
}
/**
* @dev Creates a new token for `to`. Its token ID will be automatically
* assigned (and available on the emitted {Transfer} event), and the token
* URI autogenerated based on the base URI passed at construction.
*
* See {ERC721-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 tokenId) public {
require(hasRole(MINTER_ROLE, _msgSender()), "MyBase: must have minter role to mint");
_mint(to, tokenId);
}
function getVersions() public view returns (uint256) {
return _versions;
}
uint256[50] private ______gap;
// === upgrade to V2 ===
// bytes32 private _TEST2_ROLE;
// function TEST2_ROLE() public view returns (bytes32) {
// return _TEST2_ROLE;
// }
// function initializeMyBaseV2() public initializer {
// _TEST2_ROLE = keccak256("TEST2_ROLE");
// _setupRole(_TEST2_ROLE, _msgSender());
// _versions += 2;
// }
// === update ______gap length before deployment ===
// === upgrade to V3 ===
// bytes32 private _TEST3_ROLE;
// function TEST3_ROLE() public view returns (bytes32) {
// return _TEST3_ROLE;
// }
// function initializeMyBaseV3() public initializer {
// _TEST3_ROLE = keccak256("TEST3_ROLE");
// _setupRole(_TEST3_ROLE, _msgSender());
// _versions += 4;
// }
// === update ______gap length before deployment ===
}
MyBase.sol upgraded
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol";
contract MyBase is Initializable, ERC721PresetMinterPauserAutoIdUpgradeSafe {
uint256 private _versions = 0;
bytes32 public constant TEST1_ROLE = keccak256("TEST1_ROLE");
/**
* @dev Grants `DEFAULT_ADMIN_ROLE` and `MINTER_ROLE`to the account that
* deploys the contract.
*
* Token URIs will be autogenerated based on `baseURI` and their token IDs.
* See {ERC721-tokenURI}.
*/
function initializeMyBaseV1(string memory name, string memory symbol, string memory baseURI) public initializer {
super.initialize(name, symbol, baseURI);
_setupRole(TEST1_ROLE, _msgSender());
_versions += 1;
}
/**
* @dev Creates a new token for `to`. Its token ID will be automatically
* assigned (and available on the emitted {Transfer} event), and the token
* URI autogenerated based on the base URI passed at construction.
*
* See {ERC721-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 tokenId) public {
require(hasRole(MINTER_ROLE, _msgSender()), "MyBase: must have minter role to mint");
_mint(to, tokenId);
}
function getVersions() public view returns (uint256) {
return _versions;
}
uint256[49] private ______gap;
// === upgrade to V2 ===
bytes32 private _TEST2_ROLE;
function TEST2_ROLE() public view returns (bytes32) {
return _TEST2_ROLE;
}
function initializeMyBaseV2() public initializer {
_TEST2_ROLE = keccak256("TEST2_ROLE");
_setupRole(_TEST2_ROLE, _msgSender());
_versions += 2;
}
// === update ______gap length before deployment ===
// === upgrade to V3 ===
// bytes32 private _TEST3_ROLE;
// function TEST3_ROLE() public view returns (bytes32) {
// return _TEST3_ROLE;
// }
// function initializeMyBaseV3() public initializer {
// _TEST3_ROLE = keccak256("TEST3_ROLE");
// _setupRole(_TEST3_ROLE, _msgSender());
// _versions += 4;
// }
// === update ______gap length before deployment ===
}
Upgrade
In this asciinema I started with uint256[49] private ______gap;
so I then decreased the length to 48
instead.
The upgrade process resulted in some transactions to be reverted as seen in the node logs:
Served eth_estimateGas conn=172.17.0.1:51534 reqid=19 t=12.064661ms err="execution reverted"
Served eth_estimateGas conn=172.17.0.1:51550 reqid=20 t=10.75354ms err="execution reverted"
Served eth_estimateGas conn=172.17.0.1:51558 reqid=21 t=9.623944ms err="execution reverted"
Served eth_estimateGas conn=172.17.0.1:51570 reqid=22 t=12.451764ms err="execution reverted"
I’m not sure what exactly goes wrong.