Error when creating upgradeable contracts extending OpenZeppelin Contracts Ethereum Package

At @abcoathup’s request, I’m posting this publicly in case it is helpful to future generations.

I’m working hard on understanding the new upgradable paradigm after getting pretty far with the “regular” stuff.

Confusion has set in about “writing your own” upgradable contract, however.

My stand-alone “regular” version of my contract works really well, with all its extensions and added data, and I’m looking to recreate it as fully-upgradable, custom extended functions and all.

The docs seem to offer conflicting information as to what the import statement should be.

@openzeppelin/contracts-ethereum-package/README.md:

import '@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol';
import '@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol';

versus https://docs.openzeppelin.com/cli/2.8/dependencies#writing-the-exchange-contract

import '@openzeppelin/upgrades/contracts/Initializable.sol';
import '@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol';

Unfortunately, neither work with the provided example contract code from the README.md (included at the end of this post):

[02:06:35] [eviljordan@WAT ../xxxContract]$ npx oz deploy
✖ Compiling contracts with solc 0.6.7 (commit.b8d736ae)
Compilation errors: 
contracts/xxxContract.sol:6:34: DeclarationError: Identifier not found or not unique.
contract MyNFT is Initializable, ERC721PresetMinterPauserAutoId {
                                 ^----------------------------^

contracts/xxxContract.sol:7:32: DeclarationError: Undeclared identifier. Did you mean "initialize"?
  function initialize() public initializer {
                               ^---------^
[02:07:01] [eviljordan@WAT ../xxxContract]$ npx oz deploy
✖ Compiling contracts with solc 0.6.7 (commit.b8d736ae)
Compilation errors: 
contracts/xxxContract.sol:4:1: DeclarationError: Identifier already declared.
import '@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol';
^-----------------------------------------------------------------------------------------------------^
@openzeppelin/upgrades/contracts/Initializable.sol:16:1: The previous declaration is here:
contract Initializable {
^ (Relevant source part starts here and spans across multiple lines).

This is the solidity code straight from the README.md documentation:

import '@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol';
import '@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol';

contract MyNFT is Initializable, ERC721PresetMinterPauserAutoId {
  function initialize() public initializer {
    ERC721PresetMinterPauserAutoId.initialize("name", "SYM");
  }
}
1 Like

I got this working (I think it’s all kosher, but I could be very, very wrong…), but it’s different than the docs would suggest. This works:

import '@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol';

contract MyNFT is ERC721PresetMinterPauserAutoIdUpgradeSafe {
    function initialize() public initializer {
        ERC721PresetMinterPauserAutoIdUpgradeSafe.initialize("name", "SYM", 'http://url');
    }
}

There are two major differences:

  1. The use of ERC721PresetMinterPauserAutoIdUpgradeSafe instead of ERC721PresetMinterPauserAutoId (the contract filename).
  2. The removal of the import statement for the Initializer contract (it’s already included as a dependency of ERC721PresetMinterPauserAutoId.sol.
1 Like

Hi @EvilJordan,

OpenZeppelin Contracts Ethereum Package v3.0 announcement includes the following note:

All contracts have an UpgradeSafe suffix to avoid confusion with their counterparts in OpenZeppelin Contracts. For example, ERC20 becomes ERC20UpgradeSafe .

To inherit from a contract in OpenZeppelin Contracts Ethereum Package we need to add the suffix UpgradeSafe

This is shown in the example in the Extending Contracts section of the README:

(I assume you were looking at the tagged version which I updated last week. Apologies for any confusion: https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/v3.0.0/README.md#extending-contracts)

pragma solidity ^0.6.2;

import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/presets/ERC721PresetMinterPauserAutoId.sol";


contract MyNFT is Initializable, ERC721PresetMinterPauserAutoIdUpgradeSafe {
    function initialize() public initializer {
        ERC721PresetMinterPauserAutoIdUpgradeSafe.initialize(
           "MyNFT",
           "MYN",
           "https://example.com/token/"
       );
   }
}

When I was updating the documentation last week I hadn’t realized that OpenZeppelin Contracts Ethereum Package had an implementation of Initializable, so I will need to update references to that.

[Update] Best practice is to explicityly import and inherit from Initializable In fact as you pointed out, in the example in the README we don’t need to import nor inherit from Initializable as it is already imported in the preset contract.

I have created PR https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/pull/88 to update the README to remove explicit import and inheritance of Initializable.

[Update] I closed the PR as it is best practice to explicitly import Initializable