Skip to content

Commit 6663fb8

Browse files
committed
fix immutable udvts
1 parent 80dca76 commit 6663fb8

File tree

7 files changed

+56
-7
lines changed

7 files changed

+56
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.3.20 (2023-02-11)
4+
5+
- Fix support for immutable variables of user defined value types.
6+
37
## 0.3.19 (2022-09-24)
48

59
- Add license header to WithInit.sol.

contracts/TransformAddGap-0.8.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8;
3+
4+
type ShortString is bytes32;
5+
6+
contract Foo {
7+
ShortString immutable s = ShortString.wrap(0);
8+
}

hardhat.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ internalTask(TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT, async (args, hre, runSupe
99

1010
module.exports = {
1111
solidity: {
12-
compilers: [{ version: '0.6.7' }, { version: '0.8.2' }],
12+
compilers: [{ version: '0.6.7' }, { version: '0.8.8' }],
1313
},
1414
};

src/transform-0.8.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,9 @@ test('custom contract size', t => {
6767
t.context.transform.apply(addStorageGaps);
6868
t.snapshot(t.context.transform.results()[file]);
6969
});
70+
71+
test('add storage gaps', t => {
72+
const file = 'contracts/TransformAddGap-0.8.sol';
73+
t.context.transform.apply(addStorageGaps);
74+
t.snapshot(t.context.transform.results()[file]);
75+
});

src/transform-0.8.test.ts.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,24 @@ Generated by [AVA](https://avajs.dev).
221221
uint256[114] private __gap;␊
222222
}␊
223223
`
224+
225+
## add storage gaps
226+
227+
> Snapshot 1
228+
229+
`// SPDX-License-Identifier: UNLICENSED␊
230+
pragma solidity ^0.8;␊
231+
232+
type ShortString is bytes32;␊
233+
234+
contract Foo {␊
235+
ShortString immutable s = ShortString.wrap(0);␊
236+
237+
/**␊
238+
* @dev This empty reserved space is put in place to allow future versions to add new␊
239+
* variables without shifting down storage in the inheritance chain.␊
240+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps␊
241+
*/␊
242+
uint256[49] private __gap;␊
243+
}␊
244+
`

src/transform-0.8.test.ts.snap

72 Bytes
Binary file not shown.

src/transformations/add-storage-gaps.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { TransformerTools } from '../transform';
1010
import { extractNatspec } from '../utils/extractNatspec';
1111
import { decodeTypeIdentifier } from '../utils/type-id';
1212
import { parseTypeId } from '../utils/parse-type-id';
13+
import { ASTResolver } from '../ast-resolver';
1314

1415
// By default, make the contract a total of 50 slots (storage + gap)
1516
const DEFAULT_SLOT_COUNT = 50;
1617

1718
export function* addStorageGaps(
1819
sourceUnit: SourceUnit,
19-
{ getLayout }: TransformerTools,
20+
{ getLayout, resolver }: TransformerTools,
2021
): Generator<Transformation> {
2122
for (const contract of findAll('ContractDefinition', sourceUnit)) {
2223
if (contract.contractKind === 'contract') {
@@ -27,7 +28,7 @@ export function* addStorageGaps(
2728
}
2829
}
2930

30-
const gapSize = targetSlots - getContractSlotCount(contract, getLayout(contract));
31+
const gapSize = targetSlots - getContractSlotCount(contract, getLayout(contract), resolver);
3132

3233
if (gapSize <= 0) {
3334
throw new Error(
@@ -71,8 +72,9 @@ function isStorageVariable(varDecl: VariableDeclaration): boolean {
7172
}
7273
}
7374

74-
function getNumberOfBytesOfValueType(typeId: string): number {
75-
const details = parseTypeId(typeId).head.match(/^t_(?<base>[a-z]+)(?<size>\d+)?/);
75+
function getNumberOfBytesOfValueType(typeId: string, resolver: ASTResolver): number {
76+
const { head, tail } = parseTypeId(typeId);
77+
const details = head.match(/^t_(?<base>[a-zA-Z]+)(?<size>\d+)?/);
7678
switch (details?.groups?.base) {
7779
case 'bool':
7880
case 'byte':
@@ -86,12 +88,20 @@ function getNumberOfBytesOfValueType(typeId: string): number {
8688
case 'int':
8789
case 'uint':
8890
return parseInt(details.groups.size, 10) / 8;
91+
case 'userDefinedValueType':
92+
const definition = resolver.resolveNode('UserDefinedValueTypeDefinition', Number(tail));
93+
const underlying = definition.underlyingType.typeDescriptions.typeIdentifier;
94+
if (underlying) {
95+
return getNumberOfBytesOfValueType(underlying, resolver);
96+
} else {
97+
throw new Error(`Unsupported value type: ${typeId}`);
98+
}
8999
default:
90100
throw new Error(`Unsupported value type: ${typeId}`);
91101
}
92102
}
93103

94-
function getContractSlotCount(contractNode: ContractDefinition, layout: StorageLayout): number {
104+
function getContractSlotCount(contractNode: ContractDefinition, layout: StorageLayout, resolver: ASTResolver): number {
95105
// This tracks both slot and offset:
96106
// - slot = Math.floor(contractSizeInBytes / 32)
97107
// - offset = contractSizeInBytes % 32
@@ -109,7 +119,7 @@ function getContractSlotCount(contractNode: ContractDefinition, layout: StorageL
109119
const size =
110120
layout.types && layout.types[typeIdentifier]
111121
? parseInt(layout.types[typeIdentifier]?.numberOfBytes ?? '')
112-
: getNumberOfBytesOfValueType(typeIdentifier);
122+
: getNumberOfBytesOfValueType(typeIdentifier, resolver);
113123

114124
// used space in the current slot
115125
const offset = contractSizeInBytes % 32;

0 commit comments

Comments
 (0)