@openzeppelin/upgrades-core 1.13.0 → 1.14.1
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/storage/compare.d.ts +15 -4
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/compare.js +46 -4
- package/dist/storage/compare.js.map +1 -1
- package/dist/storage/extract.d.ts.map +1 -1
- package/dist/storage/extract.js +39 -19
- package/dist/storage/extract.js.map +1 -1
- package/dist/storage/layout.d.ts +14 -0
- package/dist/storage/layout.d.ts.map +1 -1
- package/dist/storage/layout.js +6 -2
- package/dist/storage/layout.js.map +1 -1
- package/dist/storage/report-rename-retype.test.d.ts +2 -0
- package/dist/storage/report-rename-retype.test.d.ts.map +1 -0
- package/dist/storage/report-rename-retype.test.js +93 -0
- package/dist/storage/report-rename-retype.test.js.map +1 -0
- package/dist/storage/report.d.ts.map +1 -1
- package/dist/storage/report.js +32 -2
- package/dist/storage/report.js.map +1 -1
- package/dist/storage/report.test.js +6 -0
- package/dist/storage/report.test.js.map +1 -1
- package/dist/storage.test.js +10 -2
- package/dist/storage.test.js.map +1 -1
- package/dist/utils/execall.d.ts +2 -0
- package/dist/utils/execall.d.ts.map +1 -0
- package/dist/utils/execall.js +17 -0
- package/dist/utils/execall.js.map +1 -0
- package/package.json +2 -2
- package/src/storage/compare.ts +57 -8
- package/src/storage/extract.ts +42 -29
- package/src/storage/layout.ts +17 -2
- package/src/storage/report-rename-retype.test.ts +106 -0
- package/src/storage/report-rename-retype.test.ts.md +44 -0
- package/src/storage/report-rename-retype.test.ts.snap +0 -0
- package/src/storage/report.test.ts +7 -0
- package/src/storage/report.test.ts.md +11 -0
- package/src/storage/report.test.ts.snap +0 -0
- package/src/storage/report.ts +39 -4
- package/src/storage.test.ts +12 -2
- package/src/storage.test.ts.md +348 -109
- package/src/storage.test.ts.snap +0 -0
- package/src/utils/execall.ts +11 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Snapshot report for `src/storage/report-rename-retype.test.ts`
|
|
2
|
+
|
|
3
|
+
The actual snapshot is saved in `report-rename-retype.test.ts.snap`.
|
|
4
|
+
|
|
5
|
+
Generated by [AVA](https://avajs.dev).
|
|
6
|
+
|
|
7
|
+
## succesful rename
|
|
8
|
+
|
|
9
|
+
> Snapshot 1
|
|
10
|
+
|
|
11
|
+
''
|
|
12
|
+
|
|
13
|
+
## succesful retype
|
|
14
|
+
|
|
15
|
+
> Snapshot 1
|
|
16
|
+
|
|
17
|
+
''
|
|
18
|
+
|
|
19
|
+
## wrongly reported retype
|
|
20
|
+
|
|
21
|
+
> Snapshot 1
|
|
22
|
+
|
|
23
|
+
`file.sol:1: Upgraded \`x\` to an incompatible type␊
|
|
24
|
+
- Bad upgrade from bool to uint8`
|
|
25
|
+
|
|
26
|
+
## rightly reported retype but incompatible new type
|
|
27
|
+
|
|
28
|
+
> Snapshot 1
|
|
29
|
+
|
|
30
|
+
`file.sol:1: Layout changed for \`x\` (bool -> bytes32)␊
|
|
31
|
+
- Number of bytes changed from 1 to 32`
|
|
32
|
+
|
|
33
|
+
## confusing bad retype
|
|
34
|
+
|
|
35
|
+
> Snapshot 1
|
|
36
|
+
|
|
37
|
+
`file.sol:1: Upgraded \`x\` to an incompatible type␊
|
|
38
|
+
- Bad upgrade from bool to uint8`
|
|
39
|
+
|
|
40
|
+
## non-hardcoded retype
|
|
41
|
+
|
|
42
|
+
> Snapshot 1
|
|
43
|
+
|
|
44
|
+
''
|
|
Binary file
|
|
@@ -59,6 +59,13 @@ test('obvious mismatch', t => {
|
|
|
59
59
|
t.snapshot(report.explain());
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
test('function visibility change', t => {
|
|
63
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_FunctionType_Visibility_V1');
|
|
64
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_FunctionType_Visibility_V2_Bad');
|
|
65
|
+
const report = getReport(v1, v2);
|
|
66
|
+
t.snapshot(report.explain());
|
|
67
|
+
});
|
|
68
|
+
|
|
62
69
|
test('array', t => {
|
|
63
70
|
const v1 = t.context.extractStorageLayout('StorageUpgrade_Array_V1');
|
|
64
71
|
const v2 = t.context.extractStorageLayout('StorageUpgrade_Array_V2_Bad');
|
|
@@ -90,6 +90,17 @@ Generated by [AVA](https://avajs.dev).
|
|
|
90
90
|
file.sol:1: Upgraded \`a1\` to an incompatible type␊
|
|
91
91
|
- Bad upgrade from uint256[] to mapping(uint256 => uint256)`
|
|
92
92
|
|
|
93
|
+
## function visibility change
|
|
94
|
+
|
|
95
|
+
> Snapshot 1
|
|
96
|
+
|
|
97
|
+
`file.sol:1: Upgraded \`s1\` to an incompatible type␊
|
|
98
|
+
- Bad upgrade from struct StorageUpgrade_FunctionType_Visibility_V1.S to struct StorageUpgrade_FunctionType_Visibility_V2_Bad.S␊
|
|
99
|
+
- In struct StorageUpgrade_FunctionType_Visibility_V2_Bad.S␊
|
|
100
|
+
- Upgraded \`m\` to an incompatible type␊
|
|
101
|
+
- Bad upgrade from function () to function () external␊
|
|
102
|
+
Different visibility`
|
|
103
|
+
|
|
93
104
|
## array
|
|
94
105
|
|
|
95
106
|
> Snapshot 1
|
|
Binary file
|
package/src/storage/report.ts
CHANGED
|
@@ -2,7 +2,7 @@ import _chalk from 'chalk';
|
|
|
2
2
|
|
|
3
3
|
import type { BasicOperation } from '../levenshtein';
|
|
4
4
|
import type { ParsedTypeDetailed } from './layout';
|
|
5
|
-
import type { StorageOperation, StorageItem, StorageField, TypeChange, EnumOperation } from './compare';
|
|
5
|
+
import type { StorageOperation, StorageItem, StorageField, TypeChange, EnumOperation, LayoutChange } from './compare';
|
|
6
6
|
import { itemize, itemizeWith } from '../utils/itemize';
|
|
7
7
|
import { indent } from '../utils/indent';
|
|
8
8
|
import { assert } from '../utils/assert';
|
|
@@ -22,8 +22,12 @@ export class LayoutCompatibilityReport {
|
|
|
22
22
|
const chalk = new _chalk.Instance({ level: color && _chalk.supportsColor ? _chalk.supportsColor.level : 0 });
|
|
23
23
|
const res = [];
|
|
24
24
|
|
|
25
|
-
for (const op of this.ops) {
|
|
25
|
+
for (const [i, op] of this.ops.entries()) {
|
|
26
26
|
const src = 'updated' in op ? op.updated.src : op.original.contract;
|
|
27
|
+
// Only print layoutchange if it's the first op, otherwise we assume it will be explained by previous ops.
|
|
28
|
+
if (op.kind === 'layoutchange' && i !== 0) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
27
31
|
res.push(
|
|
28
32
|
chalk.bold(src) + ':' + indent(explainStorageOperation(op, { kind: 'layout', allowAppend: true }), 2, 1),
|
|
29
33
|
);
|
|
@@ -52,13 +56,20 @@ function explainStorageOperation(op: StorageOperation<StorageField>, ctx: Storag
|
|
|
52
56
|
: [];
|
|
53
57
|
return `Upgraded ${label(op.updated)} to an incompatible type\n` + itemize(basic, ...details);
|
|
54
58
|
}
|
|
55
|
-
|
|
56
59
|
case 'rename':
|
|
57
60
|
return `Renamed ${label(op.original)} to ${label(op.updated)}`;
|
|
58
61
|
|
|
59
62
|
case 'replace':
|
|
60
63
|
return `Replaced ${label(op.original)} with ${label(op.updated)} of incompatible type`;
|
|
61
64
|
|
|
65
|
+
case 'layoutchange': {
|
|
66
|
+
return (
|
|
67
|
+
`Layout ${op.change.uncertain ? 'could have changed' : 'changed'} for ${label(op.updated)} ` +
|
|
68
|
+
`(${op.original.type.item.label} -> ${op.updated.type.item.label})\n` +
|
|
69
|
+
describeLayoutTransition(op.change)
|
|
70
|
+
).trimEnd();
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
default: {
|
|
63
74
|
const title = explainBasicOperation(op, t => t.label);
|
|
64
75
|
const hints = [];
|
|
@@ -90,6 +101,9 @@ function explainStorageOperation(op: StorageOperation<StorageField>, ctx: Storag
|
|
|
90
101
|
|
|
91
102
|
function explainTypeChange(ch: TypeChange): string {
|
|
92
103
|
switch (ch.kind) {
|
|
104
|
+
case 'visibility change':
|
|
105
|
+
return `Bad upgrade ${describeTransition(ch.original, ch.updated)}\nDifferent visibility`;
|
|
106
|
+
|
|
93
107
|
case 'obvious mismatch':
|
|
94
108
|
case 'struct members':
|
|
95
109
|
case 'enum members':
|
|
@@ -172,7 +186,16 @@ function explainTypeChangeDetails(ch: TypeChange): string | undefined {
|
|
|
172
186
|
const { allowAppend } = ch;
|
|
173
187
|
return (
|
|
174
188
|
`In ${ch.updated.item.label}\n` +
|
|
175
|
-
itemize(
|
|
189
|
+
itemize(
|
|
190
|
+
...ch.ops.flatMap((op, i) => {
|
|
191
|
+
if (op.kind === 'layoutchange' && i !== 0) {
|
|
192
|
+
// Only print layoutchange if it's the first op, otherwise we assume it will be explained by previous ops.
|
|
193
|
+
return [];
|
|
194
|
+
} else {
|
|
195
|
+
return [explainStorageOperation(op, { kind: 'struct', allowAppend })];
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
)
|
|
176
199
|
);
|
|
177
200
|
}
|
|
178
201
|
|
|
@@ -215,6 +238,18 @@ function describeTransition(original: ParsedTypeDetailed, updated: ParsedTypeDet
|
|
|
215
238
|
}
|
|
216
239
|
}
|
|
217
240
|
|
|
241
|
+
function describeLayoutTransition(change: LayoutChange): string {
|
|
242
|
+
const res = [];
|
|
243
|
+
for (const k of ['slot', 'offset', 'bytes'] as const) {
|
|
244
|
+
const ch = change[k];
|
|
245
|
+
if (ch) {
|
|
246
|
+
const label = (k === 'bytes' ? 'number of bytes' : k).replace(/^./, c => c.toUpperCase());
|
|
247
|
+
res.push(`${label} changed from ${ch.from} to ${ch.to}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return itemize(...res);
|
|
251
|
+
}
|
|
252
|
+
|
|
218
253
|
function label(variable: { label: string }): string {
|
|
219
254
|
return '`' + variable.label + '`';
|
|
220
255
|
}
|
package/src/storage.test.ts
CHANGED
|
@@ -23,11 +23,15 @@ test.before(async t => {
|
|
|
23
23
|
}
|
|
24
24
|
const solcOutput: SolcOutput = buildInfo.output;
|
|
25
25
|
const contracts: Record<string, ContractDefinition> = {};
|
|
26
|
+
const storageLayouts: Record<string, StorageLayout> = {};
|
|
26
27
|
for (const def of findAll('ContractDefinition', solcOutput.sources['contracts/test/Storage.sol'].ast)) {
|
|
27
28
|
contracts[def.name] = def;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
30
|
+
storageLayouts[def.name] = solcOutput.contracts['contracts/test/Storage.sol'][def.name].storageLayout!;
|
|
28
31
|
}
|
|
29
32
|
const deref = astDereferencer(solcOutput);
|
|
30
|
-
t.context.extractStorageLayout = name =>
|
|
33
|
+
t.context.extractStorageLayout = name =>
|
|
34
|
+
extractStorageLayout(contracts[name], dummyDecodeSrc, deref, storageLayouts[name]);
|
|
31
35
|
});
|
|
32
36
|
|
|
33
37
|
const dummyDecodeSrc = () => 'file.sol:1';
|
|
@@ -175,7 +179,7 @@ test('storage upgrade with structs', t => {
|
|
|
175
179
|
change: {
|
|
176
180
|
kind: 'struct members',
|
|
177
181
|
ops: {
|
|
178
|
-
length:
|
|
182
|
+
length: 2,
|
|
179
183
|
0: { kind: 'delete' },
|
|
180
184
|
},
|
|
181
185
|
},
|
|
@@ -464,6 +468,12 @@ test('storage upgrade with enum key in mapping', t => {
|
|
|
464
468
|
});
|
|
465
469
|
});
|
|
466
470
|
|
|
471
|
+
test('storage upgrade with embedded enum inside struct type', t => {
|
|
472
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_StructEnum_V2');
|
|
473
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_StructEnum_V2');
|
|
474
|
+
t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
|
|
475
|
+
});
|
|
476
|
+
|
|
467
477
|
function stabilizeStorageLayout(layout: StorageLayout) {
|
|
468
478
|
return {
|
|
469
479
|
storage: layout.storage.map(s => ({ ...s, type: stabilizeTypeIdentifier(s.type) })),
|