Rename Struct property

Hi guys,

I am trying to update a contract that contains a struct that has a property that has been renamed. This renaming was not superficial because it represents a critical business logic that needs to say something about what it does.

I am using the natspec doc /// @custom:oz-renamed-from <previous name> to tell the storage verifier about the change in this way:

struct Project {
    uint256 id;
    uint256 maintenancePercentage;
    uint256 initialProjectValue;
    uint256 currentProjectValue;
    uint256 swapFactor;
    uint256 pWattsSupply;
    uint256 usdDepreciated;
    uint256 originatorFee;
    ProjectState state;
    Signatures signatures;
    address addr;
    address adminAddr;
    address installerAddr;
    /// @custom:oz-renamed-from operator
    address originator;
    address stableAddr;
}

But the following error still throws.

Error: New storage layout is incompatible

contracts/ProjectsManager.sol:40: Upgraded `projects` to an incompatible type
  - In mapping(address => struct Project)
    - Bad upgrade to struct Project
  - In struct Project
    - Renamed `operator` to `originator`

Did you know how can I solve this without use the unsafeAllowRenames directive?

This is a limitation of Solidity where Natspec annotations within a struct are not exposed. See https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/802 which describes some possible workarounds.

1 Like

This workaround works if the strut is not inside a contract, but in a file called Types.sol, which contains a lot of struct?

The workaround provided in Replace unused variable in struct with others - #5 by ericglau refers to a struct within a contract.

If your struct is outside of a contract, you can just specify the original struct (without a contract prefix) in the retyped annotation, e.g.:

import {S1} from "./Types.sol";

contract MyContractV2 {
  /// @custom:oz-retyped-from S1
  S2 s;
}

If the struct type has the same name in both versions (e.g. if both the original and updated struct type are called S), you can do this:

import {S} from "./Types.sol";

contract MyContractV2 {
  /// @custom:oz-retyped-from S
  S s;
}

This looks strange, but the retyped annotation causes the type change to be ignored from the storage comparison. Therefore, as mentioned in the linked posts, you should manually verify that your structs are compatible.