openzeppelin-upgrades icon indicating copy to clipboard operation
openzeppelin-upgrades copied to clipboard

hardhat-upgrades: Failed to compile a library that contains storage variable when using hardhat-upgrades plugin

Open bluele opened this issue 1 year ago • 2 comments

When I try to compile a Hardhat project that contains a Solidity library, I get an unexpected error. The library seems to be valid solidity code, but the compile fails when the @openzeppelin/hardhat-upgrades plugin is enabled.

Steps to Reproduce

  1. Install the hardhat(v2.22.9) and @openzeppelin/hardhat-upgrades(v3.2.1) plugin.
  2. Include the following solidity library and hardhat config in the project:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

library Storage {
    // keccak256(abi.encode(uint256(keccak256("library.storageerror")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 internal constant STORAGE_LOCATION =
        0x55d0c499112f0d58436b580033c012e14f7cc466c2e114bde6231acb85553000;

    /// @custom:storage-location erc7201:library.storageerror
    struct MainStorage {
        uint256 a;
    }

    function layout() internal pure returns (MainStorage storage $) {
        assembly {
            $.slot := STORAGE_LOCATION
        }
    }
}
require("@openzeppelin/hardhat-upgrades");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.24",
};
  1. npx hardhat compile

Error message

$ npx hardhat compile
An unexpected error occurred:

Error: Failed to compile modified contracts for namespaced storage:

TypeError: Library cannot have non-constant state variables
  --> contracts/Storage.sol:12:7:
   |
12 |     } MainStorage $MainStorage_751401;
   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^



Please report this at https://zpl.in/upgrades/report. If possible, include the source code for the contracts mentioned in the errors above.

bluele avatar Aug 23 '24 07:08 bluele

Thanks for reporting. This appears to be an issue in the Hardhat Upgrades plugin, and we will look into resolving this.

However, note that your example appears to be incorrect usage of the ERC-7201 namespaced layout pattern. Specifically, ERC-7201 specifies:

Structs with this annotation found outside of contracts are not considered to be namespaces for any contract in the source code.

, that is, namespaces should not be defined in libraries, they should only be defined in contracts.

ericglau avatar Aug 23 '24 20:08 ericglau

@ericglau Thank you! I overlooked that detail.

In my project, I have already defined the eip-7201 storage within a contract instead of using the library pattern for other reasons. If you decide that this case should not be supported, please feel free to close this issue.

bluele avatar Aug 26 '24 02:08 bluele