@openzeppelin/upgrades-core 1.45.0 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/validate/validate-upgrade-safety.test.js +18 -0
- package/dist/cli/validate/validate-upgrade-safety.test.js.map +1 -1
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/compare.js +3 -3
- package/dist/storage/compare.js.map +1 -1
- package/dist/storage/extract.d.ts +15 -3
- package/dist/storage/extract.d.ts.map +1 -1
- package/dist/storage/extract.js +68 -22
- package/dist/storage/extract.js.map +1 -1
- package/dist/storage/namespace.d.ts.map +1 -1
- package/dist/storage/namespace.js +26 -13
- package/dist/storage/namespace.js.map +1 -1
- package/dist/storage/report-rename-retype.test.js +30 -0
- package/dist/storage/report-rename-retype.test.js.map +1 -1
- package/dist/storage/report.js +9 -1
- package/dist/storage/report.js.map +1 -1
- package/dist/storage-erc7201-builtin.test.d.ts +2 -0
- package/dist/storage-erc7201-builtin.test.d.ts.map +1 -0
- package/dist/storage-erc7201-builtin.test.js +105 -0
- package/dist/storage-erc7201-builtin.test.js.map +1 -0
- package/dist/storage-namespaced-renamed-retyped.test.d.ts +2 -0
- package/dist/storage-namespaced-renamed-retyped.test.d.ts.map +1 -0
- package/dist/storage-namespaced-renamed-retyped.test.js +278 -0
- package/dist/storage-namespaced-renamed-retyped.test.js.map +1 -0
- package/dist/storage-struct-member-renamed-retyped.test.d.ts +2 -0
- package/dist/storage-struct-member-renamed-retyped.test.d.ts.map +1 -0
- package/dist/storage-struct-member-renamed-retyped.test.js +256 -0
- package/dist/storage-struct-member-renamed-retyped.test.js.map +1 -0
- package/dist/utils/annotations.d.ts +1 -0
- package/dist/utils/annotations.d.ts.map +1 -1
- package/dist/utils/annotations.js +4 -1
- package/dist/utils/annotations.js.map +1 -1
- package/dist/utils/erc7201-builtin.d.ts +24 -0
- package/dist/utils/erc7201-builtin.d.ts.map +1 -0
- package/dist/utils/erc7201-builtin.js +83 -0
- package/dist/utils/erc7201-builtin.js.map +1 -0
- package/dist/utils/erc7201.d.ts.map +1 -1
- package/dist/utils/erc7201.js +5 -3
- package/dist/utils/erc7201.js.map +1 -1
- package/dist/utils/erc7201.test.js +10 -0
- package/dist/utils/erc7201.test.js.map +1 -1
- package/dist/utils/integer-literals.d.ts +19 -6
- package/dist/utils/integer-literals.d.ts.map +1 -1
- package/dist/utils/integer-literals.js +28 -10
- package/dist/utils/integer-literals.js.map +1 -1
- package/dist/utils/integer-literals.test.js +12 -0
- package/dist/utils/integer-literals.test.js.map +1 -1
- package/dist/utils/struct-member-docs.d.ts +52 -0
- package/dist/utils/struct-member-docs.d.ts.map +1 -0
- package/dist/utils/struct-member-docs.js +264 -0
- package/dist/utils/struct-member-docs.js.map +1 -0
- package/dist/utils/struct-member-docs.test.d.ts +2 -0
- package/dist/utils/struct-member-docs.test.d.ts.map +1 -0
- package/dist/utils/struct-member-docs.test.js +320 -0
- package/dist/utils/struct-member-docs.test.js.map +1 -0
- package/dist/validate/data.d.ts +1 -1
- package/dist/validate/data.d.ts.map +1 -1
- package/dist/validate/data.js +1 -1
- package/dist/validate/run.d.ts +1 -1
- package/dist/validate/run.d.ts.map +1 -1
- package/dist/validate/run.js +22 -2
- package/dist/validate/run.js.map +1 -1
- package/package.json +5 -5
- package/src/cli/validate/validate-upgrade-safety.test.ts +35 -0
- package/src/cli/validate/validate-upgrade-safety.test.ts.md +10 -0
- package/src/cli/validate/validate-upgrade-safety.test.ts.snap +0 -0
- package/src/storage/compare.ts +3 -4
- package/src/storage/extract.ts +83 -30
- package/src/storage/namespace.ts +31 -14
- package/src/storage/report-rename-retype.test.ts +41 -0
- package/src/storage/report.ts +9 -1
- package/src/storage-erc7201-builtin.test.ts +127 -0
- package/src/storage-namespaced-renamed-retyped.test.ts +338 -0
- package/src/storage-namespaced-renamed-retyped.test.ts.md +183 -0
- package/src/storage-namespaced-renamed-retyped.test.ts.snap +0 -0
- package/src/storage-struct-member-renamed-retyped.test.ts +299 -0
- package/src/storage-struct-member-renamed-retyped.test.ts.md +260 -0
- package/src/storage-struct-member-renamed-retyped.test.ts.snap +0 -0
- package/src/utils/annotations.ts +4 -4
- package/src/utils/erc7201-builtin.ts +91 -0
- package/src/utils/erc7201.test.ts +12 -0
- package/src/utils/erc7201.ts +6 -4
- package/src/utils/integer-literals.test.ts +15 -1
- package/src/utils/integer-literals.ts +29 -10
- package/src/utils/struct-member-docs.test.ts +351 -0
- package/src/utils/struct-member-docs.ts +293 -0
- package/src/validate/data.ts +2 -2
- package/src/validate/run.ts +23 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import _test, { ExecutionContext, TestFn } from 'ava';
|
|
2
|
+
import { artifacts, run } from 'hardhat';
|
|
3
|
+
import {
|
|
4
|
+
TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD,
|
|
5
|
+
TASK_COMPILE_SOLIDITY_RUN_SOLC,
|
|
6
|
+
TASK_COMPILE_SOLIDITY_RUN_SOLCJS,
|
|
7
|
+
} from 'hardhat/builtin-tasks/task-names';
|
|
8
|
+
import { SolcBuild } from 'hardhat/types/builtin-tasks';
|
|
9
|
+
|
|
10
|
+
import { SolcInput, SolcOutput } from './solc-api';
|
|
11
|
+
import { validate, ValidationRunData } from './validate';
|
|
12
|
+
import { withValidationDefaults } from './validate/overrides';
|
|
13
|
+
import { solcInputOutputDecoder } from './src-decoder';
|
|
14
|
+
import { makeNamespacedInput, trySanitizeNatSpec } from './utils/make-namespaced';
|
|
15
|
+
import { getStorageUpgradeErrors, getStorageUpgradeReport } from './storage';
|
|
16
|
+
import { StorageLayout, isStructMembers } from './storage/layout';
|
|
17
|
+
import { stabilizeStorageLayout } from './utils/stabilize-layout';
|
|
18
|
+
|
|
19
|
+
const source = 'contracts/test/NamespacedRenamedRetyped.sol';
|
|
20
|
+
const crossFileSource = 'contracts/test/NamespacedCrossFile.sol';
|
|
21
|
+
|
|
22
|
+
interface Context {
|
|
23
|
+
input: SolcInput;
|
|
24
|
+
output: SolcOutput;
|
|
25
|
+
namespacedOutput: SolcOutput;
|
|
26
|
+
solcVersion: string;
|
|
27
|
+
validation: ValidationRunData;
|
|
28
|
+
// Validation without the namespaced compilation, as with the build info files of the CLI, so that namespace members
|
|
29
|
+
// have no slots
|
|
30
|
+
validationWithoutSlots: ValidationRunData;
|
|
31
|
+
// Validation of the fixture whose namespace has a member of a struct type from another source file
|
|
32
|
+
crossFile: { output: SolcOutput; validation: ValidationRunData };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const test = _test as TestFn<Context>;
|
|
36
|
+
|
|
37
|
+
test.before(async t => {
|
|
38
|
+
const { input, output, solcVersion } = await getBuildInfo(`${source}:NamespacedRenamedRetypedV1`);
|
|
39
|
+
const namespacedOutput = await compileNamespaced(input, output, solcVersion);
|
|
40
|
+
const decodeSrc = solcInputOutputDecoder(input, output);
|
|
41
|
+
t.context.input = input;
|
|
42
|
+
t.context.output = output;
|
|
43
|
+
t.context.namespacedOutput = namespacedOutput;
|
|
44
|
+
t.context.solcVersion = solcVersion;
|
|
45
|
+
t.context.validation = validate(output, decodeSrc, solcVersion, input, namespacedOutput);
|
|
46
|
+
t.context.validationWithoutSlots = validate(output, decodeSrc, solcVersion, input);
|
|
47
|
+
|
|
48
|
+
const crossFile = await getBuildInfo(`${crossFileSource}:NamespacedCrossFileV1`);
|
|
49
|
+
t.context.crossFile = {
|
|
50
|
+
output: crossFile.output,
|
|
51
|
+
validation: await validateWithNamespacedOutput(crossFile.input, crossFile.output, crossFile.solcVersion),
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
async function getBuildInfo(contract: string) {
|
|
56
|
+
const buildInfo = await artifacts.getBuildInfo(contract);
|
|
57
|
+
if (buildInfo === undefined) {
|
|
58
|
+
throw new Error(`Build info not found for ${contract}`);
|
|
59
|
+
}
|
|
60
|
+
return buildInfo;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compiles the namespaced input for the given compilation, following the same steps as the Hardhat plugin.
|
|
65
|
+
*/
|
|
66
|
+
async function compileNamespaced(input: SolcInput, output: SolcOutput, solcVersion: string) {
|
|
67
|
+
const namespacedInput = await trySanitizeNatSpec(makeNamespacedInput(input, output, solcVersion), solcVersion);
|
|
68
|
+
return hardhatCompile(namespacedInput, solcVersion);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validates with a namespaced compilation.
|
|
73
|
+
*/
|
|
74
|
+
async function validateWithNamespacedOutput(input: SolcInput, output: SolcOutput, solcVersion: string) {
|
|
75
|
+
const namespacedOutput = await compileNamespaced(input, output, solcVersion);
|
|
76
|
+
return validate(output, solcInputOutputDecoder(input, output), solcVersion, input, namespacedOutput);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Compiles the fixture again from the given source content and validates with a namespaced compilation.
|
|
81
|
+
*/
|
|
82
|
+
async function compileAndValidate(input: SolcInput, content: string, solcVersion: string) {
|
|
83
|
+
const newInput = { ...input, sources: { [source]: { content } } };
|
|
84
|
+
const output = await hardhatCompile(newInput, solcVersion);
|
|
85
|
+
return validateWithNamespacedOutput(newInput, output, solcVersion);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function hardhatCompile(input: SolcInput, solcVersion: string): Promise<SolcOutput> {
|
|
89
|
+
const solcBuild: SolcBuild = await run(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, {
|
|
90
|
+
quiet: true,
|
|
91
|
+
solcVersion,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const output: SolcOutput = solcBuild.isSolcJs
|
|
95
|
+
? await run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { input, solcJsPath: solcBuild.compilerPath })
|
|
96
|
+
: await run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { input, solcPath: solcBuild.compilerPath });
|
|
97
|
+
|
|
98
|
+
const errors = output.errors?.filter(e => e.severity === 'error');
|
|
99
|
+
if (errors !== undefined && errors.length > 0) {
|
|
100
|
+
throw new Error(errors.map(e => e.formattedMessage).join('\n'));
|
|
101
|
+
}
|
|
102
|
+
return output;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getLayout(validation: ValidationRunData, contract: string): StorageLayout {
|
|
106
|
+
return validation[`${source}:${contract}`].layout;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function assertAnnotationsApplied(t: ExecutionContext<Context>, validation: ValidationRunData) {
|
|
110
|
+
const v1 = getLayout(validation, 'NamespacedRenamedRetypedV1');
|
|
111
|
+
const v2 = getLayout(validation, 'NamespacedRenamedRetypedV2');
|
|
112
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
113
|
+
assertExactMap(t, getLayout(validation, 'NamespacedExactMap'));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function assertExactMap(t: ExecutionContext<Context>, layout: StorageLayout) {
|
|
117
|
+
const items = layout.namespaces?.['erc7201:example.exact'];
|
|
118
|
+
if (items === undefined) {
|
|
119
|
+
throw new Error('Namespace not found');
|
|
120
|
+
}
|
|
121
|
+
t.is(items.length, 6);
|
|
122
|
+
for (const item of items) {
|
|
123
|
+
t.is(item.renamedFrom, `old_${item.label}`);
|
|
124
|
+
t.is(item.retypedFrom, undefined);
|
|
125
|
+
// Slots come from the namespaced compilation
|
|
126
|
+
t.not(item.slot, undefined);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// The struct used inside the namespace is looked up through the namespaced compilation
|
|
130
|
+
const members = Object.values(layout.types).find(type => type.label === 'struct NamespacedExactMap.Inner')?.members;
|
|
131
|
+
if (members === undefined || !isStructMembers(members)) {
|
|
132
|
+
throw new Error('Struct members not found');
|
|
133
|
+
}
|
|
134
|
+
t.is(members.length, 3);
|
|
135
|
+
for (const member of members) {
|
|
136
|
+
t.is(member.renamedFrom, `old_${member.label}`);
|
|
137
|
+
t.is(member.retypedFrom, undefined);
|
|
138
|
+
t.not(member.slot, undefined);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
test('layout', t => {
|
|
143
|
+
t.snapshot(stabilizeStorageLayout(getLayout(t.context.validation, 'NamespacedRenamedRetypedV2')));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('upgrade ok', t => {
|
|
147
|
+
assertAnnotationsApplied(t, t.context.validation);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('inherited namespace upgrade ok', t => {
|
|
151
|
+
const v1 = getLayout(t.context.validation, 'NamespacedRenamedRetypedInheritedV1');
|
|
152
|
+
const v2 = getLayout(t.context.validation, 'NamespacedRenamedRetypedInheritedV2');
|
|
153
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('wrong retype', t => {
|
|
157
|
+
const v1 = getLayout(t.context.validation, 'NamespacedBadV1');
|
|
158
|
+
const v2 = getLayout(t.context.validation, 'NamespacedWrongRetypeV2');
|
|
159
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
160
|
+
length: 1,
|
|
161
|
+
0: { kind: 'typechange', original: { label: 'x' }, updated: { label: 'x', retypedFrom: 'address' } },
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('annotation on the wrong member', t => {
|
|
166
|
+
// The annotation makes `x` match the original `y` with a different type, and `x` and `z` are left unmatched
|
|
167
|
+
const v1 = getLayout(t.context.validation, 'NamespacedBadV1');
|
|
168
|
+
const v2 = getLayout(t.context.validation, 'NamespacedWrongMemberV2');
|
|
169
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
170
|
+
length: 3,
|
|
171
|
+
0: { kind: 'delete', original: { label: 'x' } },
|
|
172
|
+
1: { kind: 'typechange', original: { label: 'y' }, updated: { label: 'x', renamedFrom: 'y' } },
|
|
173
|
+
2: { kind: 'insert', updated: { label: 'z' } },
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('stale rename annotation', t => {
|
|
178
|
+
const v1 = getLayout(t.context.validation, 'NamespacedBadV1');
|
|
179
|
+
const v2 = getLayout(t.context.validation, 'NamespacedStaleRenameV2');
|
|
180
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
181
|
+
length: 1,
|
|
182
|
+
0: { kind: 'rename', original: { label: 'y' }, updated: { label: 'y', renamedFrom: 'q' } },
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('unannotated rename and retype', t => {
|
|
187
|
+
const v1 = getLayout(t.context.validation, 'NamespacedBadV1');
|
|
188
|
+
const v2 = getLayout(t.context.validation, 'NamespacedUnannotatedV2');
|
|
189
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
190
|
+
length: 2,
|
|
191
|
+
0: { kind: 'typechange', original: { label: 'x' }, updated: { label: 'x' } },
|
|
192
|
+
1: { kind: 'rename', original: { label: 'y' }, updated: { label: 'z' } },
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('namespaced output with different source ids', t => {
|
|
197
|
+
const { input, output, namespacedOutput, solcVersion } = t.context;
|
|
198
|
+
const sources = Object.fromEntries(
|
|
199
|
+
Object.entries(namespacedOutput.sources).map(([path, s]) => [path, { ...s, id: s.id + 1 }]),
|
|
200
|
+
);
|
|
201
|
+
t.throws(
|
|
202
|
+
() => validate(output, solcInputOutputDecoder(input, output), solcVersion, input, { ...namespacedOutput, sources }),
|
|
203
|
+
{ message: /Source ids differ between the solc output and the namespaced solc output/ },
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('two annotated namespaces in one contract', t => {
|
|
208
|
+
const v1 = getLayout(t.context.validation, 'NamespacedTwoV1');
|
|
209
|
+
const v2 = getLayout(t.context.validation, 'NamespacedTwoV2');
|
|
210
|
+
t.like(v2.namespaces, {
|
|
211
|
+
'erc7201:example.first': {
|
|
212
|
+
length: 2,
|
|
213
|
+
0: { label: 'x', retypedFrom: 'bytes32' },
|
|
214
|
+
1: { label: 'y', renamedFrom: 'oldY' },
|
|
215
|
+
},
|
|
216
|
+
'erc7201:example.second': {
|
|
217
|
+
length: 2,
|
|
218
|
+
0: { label: 'f', retypedFrom: 'uint8' },
|
|
219
|
+
1: { label: 'g', renamedFrom: 'oldG' },
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('kept retype annotation', t => {
|
|
226
|
+
const v1 = getLayout(t.context.validation, 'NamespacedKeptRetypeV1');
|
|
227
|
+
const v2 = getLayout(t.context.validation, 'NamespacedKeptRetypeV2');
|
|
228
|
+
t.like(v2.namespaces, { 'erc7201:example.main': { length: 1, 0: { label: 'x', retypedFrom: 'uint8', slot: '0' } } });
|
|
229
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('kept retype annotation without layout info', t => {
|
|
233
|
+
const v1 = getLayout(t.context.validationWithoutSlots, 'NamespacedKeptRetypeV1');
|
|
234
|
+
const v2 = getLayout(t.context.validationWithoutSlots, 'NamespacedKeptRetypeV2');
|
|
235
|
+
t.like(v2.namespaces, {
|
|
236
|
+
'erc7201:example.main': { length: 1, 0: { label: 'x', retypedFrom: 'uint8', slot: undefined } },
|
|
237
|
+
});
|
|
238
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('retype without layout info', t => {
|
|
242
|
+
// A retype that is not known to be compatible is uncertain without slots
|
|
243
|
+
const v1 = getLayout(t.context.validationWithoutSlots, 'NamespacedBadV1');
|
|
244
|
+
const v2 = getLayout(t.context.validationWithoutSlots, 'NamespacedRetypeV2');
|
|
245
|
+
const report = getStorageUpgradeReport(v1, v2, withValidationDefaults({}));
|
|
246
|
+
t.false(report.ok);
|
|
247
|
+
t.true(
|
|
248
|
+
report.explain().includes('Layout could have changed for `x` (bytes32 -> struct TransientSlot.TBytes32)'),
|
|
249
|
+
report.explain(),
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('wrong retype without layout info', t => {
|
|
254
|
+
const v1 = getLayout(t.context.validationWithoutSlots, 'NamespacedBadV1');
|
|
255
|
+
const v2 = getLayout(t.context.validationWithoutSlots, 'NamespacedWrongRetypeV2');
|
|
256
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
257
|
+
length: 1,
|
|
258
|
+
0: { kind: 'typechange', original: { label: 'x' }, updated: { label: 'x', retypedFrom: 'address' } },
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('retype with layout change', t => {
|
|
263
|
+
// A retype annotation that names the original type does not allow the member's size to change
|
|
264
|
+
const v1 = getLayout(t.context.validation, 'NamespacedRetypeLayoutChangeV1');
|
|
265
|
+
const v2 = getLayout(t.context.validation, 'NamespacedRetypeLayoutChangeV2');
|
|
266
|
+
t.like(getStorageUpgradeErrors(v1, v2), {
|
|
267
|
+
length: 1,
|
|
268
|
+
0: {
|
|
269
|
+
kind: 'layoutchange',
|
|
270
|
+
original: { label: 'y' },
|
|
271
|
+
updated: { label: 'y', retypedFrom: 'uint256' },
|
|
272
|
+
change: { bytes: { from: '32', to: '16' } },
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('struct from another source file', t => {
|
|
278
|
+
// The annotations on the members of the struct are read from the source file that defines it
|
|
279
|
+
const { output, validation } = t.context.crossFile;
|
|
280
|
+
t.not(output.sources['contracts/test/NamespacedCrossFileStruct.sol'].id, 0);
|
|
281
|
+
const v1 = validation[`${crossFileSource}:NamespacedCrossFileV1`].layout;
|
|
282
|
+
const v2 = validation[`${crossFileSource}:NamespacedCrossFileV2`].layout;
|
|
283
|
+
t.like(v2.namespaces, { 'erc7201:example.main': { length: 1, 0: { label: 'data', slot: '0' } } });
|
|
284
|
+
const members = Object.values(v2.types).find(type => type.label === 'struct CrossFileV2.Data')?.members;
|
|
285
|
+
if (members === undefined || !isStructMembers(members)) {
|
|
286
|
+
throw new Error('Struct members not found');
|
|
287
|
+
}
|
|
288
|
+
t.like(members, {
|
|
289
|
+
length: 2,
|
|
290
|
+
0: { label: 'a', retypedFrom: 'bytes32', slot: '0' },
|
|
291
|
+
1: { label: 'c', renamedFrom: 'b', slot: '1' },
|
|
292
|
+
});
|
|
293
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Validates the fixture compiled again with the given line endings.
|
|
298
|
+
*/
|
|
299
|
+
async function validateWithLineEndings(t: ExecutionContext<Context>, lineEnding: string) {
|
|
300
|
+
const content = t.context.input.sources[source].content;
|
|
301
|
+
if (content === undefined) {
|
|
302
|
+
throw new Error('Unexpected fixture content');
|
|
303
|
+
}
|
|
304
|
+
return compileAndValidate(t.context.input, content.replace(/\r\n|\r|\n/g, lineEnding), t.context.solcVersion);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
test('CRLF line endings', async t => {
|
|
308
|
+
assertAnnotationsApplied(t, await validateWithLineEndings(t, '\r\n'));
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('CR line endings', async t => {
|
|
312
|
+
assertAnnotationsApplied(t, await validateWithLineEndings(t, '\r'));
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('solc 0.8.20', async t => {
|
|
316
|
+
const content = t.context.input.sources[source].content;
|
|
317
|
+
if (content === undefined) {
|
|
318
|
+
throw new Error('Unexpected fixture content');
|
|
319
|
+
}
|
|
320
|
+
const validation = await compileAndValidate(t.context.input, content, '0.8.20');
|
|
321
|
+
assertAnnotationsApplied(t, validation);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test('stale rename annotation report', t => {
|
|
325
|
+
const v1 = getLayout(t.context.validation, 'NamespacedBadV1');
|
|
326
|
+
const v2 = getLayout(t.context.validation, 'NamespacedStaleRenameV2');
|
|
327
|
+
const report = getStorageUpgradeReport(v1, v2, withValidationDefaults({}));
|
|
328
|
+
t.false(report.ok);
|
|
329
|
+
const items = v2.namespaces?.['erc7201:example.main'];
|
|
330
|
+
if (items === undefined) {
|
|
331
|
+
throw new Error('Namespace not found');
|
|
332
|
+
}
|
|
333
|
+
t.is(
|
|
334
|
+
report.explain(),
|
|
335
|
+
`${items[1].src}: \`y\` has a '@custom:oz-renamed-from q' annotation, but it was not renamed\n` +
|
|
336
|
+
' > If the annotation is left over from an earlier upgrade, remove it.',
|
|
337
|
+
);
|
|
338
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Snapshot report for `src/storage-namespaced-renamed-retyped.test.ts`
|
|
2
|
+
|
|
3
|
+
The actual snapshot is saved in `storage-namespaced-renamed-retyped.test.ts.snap`.
|
|
4
|
+
|
|
5
|
+
Generated by [AVA](https://avajs.dev).
|
|
6
|
+
|
|
7
|
+
## layout
|
|
8
|
+
|
|
9
|
+
> Snapshot 1
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
baseSlot: undefined,
|
|
13
|
+
namespaces: [
|
|
14
|
+
[
|
|
15
|
+
'erc7201:example.main',
|
|
16
|
+
[
|
|
17
|
+
{
|
|
18
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
19
|
+
label: 'x',
|
|
20
|
+
offset: 0,
|
|
21
|
+
retypedFrom: 'bytes32',
|
|
22
|
+
slot: '0',
|
|
23
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:40',
|
|
24
|
+
type: 't_struct(TBytes32)storage',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
28
|
+
label: 'z',
|
|
29
|
+
offset: 0,
|
|
30
|
+
renamedFrom: 'y',
|
|
31
|
+
slot: '1',
|
|
32
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:46',
|
|
33
|
+
type: 't_uint256',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
37
|
+
label: 'f',
|
|
38
|
+
offset: 0,
|
|
39
|
+
retypedFrom: 'uint8',
|
|
40
|
+
slot: '2',
|
|
41
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:48',
|
|
42
|
+
type: 't_bool',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
46
|
+
label: 'inner',
|
|
47
|
+
offset: 0,
|
|
48
|
+
slot: '3',
|
|
49
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:49',
|
|
50
|
+
type: 't_struct(Inner)storage',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
54
|
+
label: 'c',
|
|
55
|
+
offset: 0,
|
|
56
|
+
slot: '5',
|
|
57
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:50',
|
|
58
|
+
type: 't_uint256',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
contract: 'NamespacedRenamedRetypedV2',
|
|
62
|
+
label: 'd',
|
|
63
|
+
offset: 0,
|
|
64
|
+
renamedFrom: 'oldD',
|
|
65
|
+
slot: '6',
|
|
66
|
+
src: 'contracts/test/NamespacedRenamedRetyped.sol:51',
|
|
67
|
+
type: 't_uint256',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
],
|
|
71
|
+
],
|
|
72
|
+
storage: [],
|
|
73
|
+
types: [
|
|
74
|
+
[
|
|
75
|
+
't_bool',
|
|
76
|
+
{
|
|
77
|
+
label: 'bool',
|
|
78
|
+
members: undefined,
|
|
79
|
+
numberOfBytes: '1',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
't_bytes32',
|
|
84
|
+
{
|
|
85
|
+
label: 'bytes32',
|
|
86
|
+
members: undefined,
|
|
87
|
+
numberOfBytes: '32',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
't_struct(Inner)storage',
|
|
92
|
+
{
|
|
93
|
+
label: 'struct NamespacedRenamedRetypedV2.Inner',
|
|
94
|
+
members: [
|
|
95
|
+
{
|
|
96
|
+
label: 'g',
|
|
97
|
+
offset: 0,
|
|
98
|
+
renamedFrom: 'a',
|
|
99
|
+
retypedFrom: 'uint8',
|
|
100
|
+
slot: '0',
|
|
101
|
+
type: 't_bool',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
label: 'bb',
|
|
105
|
+
offset: 0,
|
|
106
|
+
renamedFrom: 'b',
|
|
107
|
+
slot: '1',
|
|
108
|
+
type: 't_bytes32',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
numberOfBytes: '64',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
't_struct(MainStorage)storage',
|
|
116
|
+
{
|
|
117
|
+
label: 'struct NamespacedRenamedRetypedV2.MainStorage',
|
|
118
|
+
members: [
|
|
119
|
+
{
|
|
120
|
+
label: 'x',
|
|
121
|
+
offset: 0,
|
|
122
|
+
slot: '0',
|
|
123
|
+
type: 't_struct(TBytes32)storage',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
label: 'z',
|
|
127
|
+
offset: 0,
|
|
128
|
+
slot: '1',
|
|
129
|
+
type: 't_uint256',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: 'f',
|
|
133
|
+
offset: 0,
|
|
134
|
+
slot: '2',
|
|
135
|
+
type: 't_bool',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: 'inner',
|
|
139
|
+
offset: 0,
|
|
140
|
+
slot: '3',
|
|
141
|
+
type: 't_struct(Inner)storage',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
label: 'c',
|
|
145
|
+
offset: 0,
|
|
146
|
+
slot: '5',
|
|
147
|
+
type: 't_uint256',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
label: 'd',
|
|
151
|
+
offset: 0,
|
|
152
|
+
slot: '6',
|
|
153
|
+
type: 't_uint256',
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
numberOfBytes: '224',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
't_struct(TBytes32)storage',
|
|
161
|
+
{
|
|
162
|
+
label: 'struct TransientSlot.TBytes32',
|
|
163
|
+
members: [
|
|
164
|
+
{
|
|
165
|
+
label: '_placeholder',
|
|
166
|
+
offset: 0,
|
|
167
|
+
slot: '0',
|
|
168
|
+
type: 't_bytes32',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
numberOfBytes: '32',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
[
|
|
175
|
+
't_uint256',
|
|
176
|
+
{
|
|
177
|
+
label: 'uint256',
|
|
178
|
+
members: undefined,
|
|
179
|
+
numberOfBytes: '32',
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
],
|
|
183
|
+
}
|
|
Binary file
|