diff --git a/packages/core/contracts/test/RenamedRetyped.sol b/packages/core/contracts/test/RenamedRetyped.sol index 402454a0a..96f4a05a6 100644 --- a/packages/core/contracts/test/RenamedRetyped.sol +++ b/packages/core/contracts/test/RenamedRetyped.sol @@ -61,4 +61,79 @@ contract LayoutChangeV2 { /// @custom:oz-retyped-from bool uint8 b; -} \ No newline at end of file +} + +contract RenameStructV1 { + struct History { + Checkpoint[] _checkpoints; + } + + struct Checkpoint { + uint32 _blockNumber; + uint224 _value; + } + + History history; +} + +contract RenameStructV2a { + struct Trace224 { + Checkpoint224[] _checkpoints; + } + + struct Checkpoint224 { + uint32 _blockNumber; + uint224 _value; + } + + Trace224 history; +} + +contract RenameStructV2b { + struct Trace224 { + Checkpoint224[] _checkpoints; + } + + struct Checkpoint224 { + uint32 _key; + uint224 _value; + } + + /// @custom:oz-retyped-from RenameStructV1.History + Trace224 history; +} + +contract InnerRetypeV1 { + struct ProposalCore { + BlockNumber key; + bool flag; + } + + struct BlockNumber { + uint32 blockNumber; + } + + ProposalCore core; +} + +contract InnerRetypeV2Good { + struct ProposalCore { + uint256 blockNumber; + bool flag; + } + + /// @custom:oz-retyped-from InnerRetypeV1.ProposalCore + ProposalCore core; +} + +contract InnerRetypeV2Bad { + struct ProposalCore { + uint16 a; + uint224 b; + uint16 c; + bool flag; + } + + /// @custom:oz-retyped-from InnerRetypeV1.ProposalCore + ProposalCore core; +} diff --git a/packages/core/src/storage/report-rename-retype.test.ts b/packages/core/src/storage/report-rename-retype.test.ts index 143cd3e09..844befc6c 100644 --- a/packages/core/src/storage/report-rename-retype.test.ts +++ b/packages/core/src/storage/report-rename-retype.test.ts @@ -28,6 +28,12 @@ const testContracts = [ 'contracts/test/RenamedRetyped.sol:NonHardcodedRetypeV2', 'contracts/test/RenamedRetyped.sol:LayoutChangeV1', 'contracts/test/RenamedRetyped.sol:LayoutChangeV2', + 'contracts/test/RenamedRetyped.sol:RenameStructV1', + 'contracts/test/RenamedRetyped.sol:RenameStructV2a', + 'contracts/test/RenamedRetyped.sol:RenameStructV2b', + 'contracts/test/RenamedRetyped.sol:InnerRetypeV1', + 'contracts/test/RenamedRetyped.sol:InnerRetypeV2Good', + 'contracts/test/RenamedRetyped.sol:InnerRetypeV2Bad', ]; test.before(async t => { @@ -131,3 +137,24 @@ test('retype with layout change', t => { t.false(report.ok); t.snapshot(report.explain()); }); + +test('storage upgrade with struct renaming/replacing', t => { + const v1 = t.context.extractStorageLayout('RenameStructV1'); + const v2a = t.context.extractStorageLayout('RenameStructV2a'); + const v2b = t.context.extractStorageLayout('RenameStructV2b'); + + t.deepEqual(getStorageUpgradeErrors(v1, v2a), []); + t.deepEqual(getStorageUpgradeErrors(v1, v2b), []); +}); + +test('storage upgrade with internal struct changes', t => { + const v1 = t.context.extractStorageLayout('InnerRetypeV1'); + const v2Good = t.context.extractStorageLayout('InnerRetypeV2Good'); + const v2Bad = t.context.extractStorageLayout('InnerRetypeV2Bad'); + + t.deepEqual(getStorageUpgradeErrors(v1, v2Good), []); + + t.like(getStorageUpgradeErrors(v1, v2Bad), { + length: 1, + }); +});