@openzeppelin/upgrades-core 1.28.0 → 1.30.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/call-optional-signature.d.ts +3 -0
- package/dist/call-optional-signature.d.ts.map +1 -0
- package/dist/call-optional-signature.js +24 -0
- package/dist/call-optional-signature.js.map +1 -0
- package/dist/cli/validate/build-info-file.d.ts +4 -0
- package/dist/cli/validate/build-info-file.d.ts.map +1 -1
- package/dist/cli/validate/build-info-file.js +5 -2
- package/dist/cli/validate/build-info-file.js.map +1 -1
- package/dist/cli/validate/build-info-file.test.js +4 -1
- package/dist/cli/validate/build-info-file.test.js.map +1 -1
- package/dist/cli/validate/upgradeability-assessment.js +2 -6
- package/dist/cli/validate/upgradeability-assessment.js.map +1 -1
- package/dist/cli/validate/validations.d.ts.map +1 -1
- package/dist/cli/validate/validations.js +5 -4
- package/dist/cli/validate/validations.js.map +1 -1
- package/dist/impl-address.d.ts.map +1 -1
- package/dist/impl-address.js +9 -16
- package/dist/impl-address.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/levenshtein.d.ts +6 -0
- package/dist/levenshtein.d.ts.map +1 -1
- package/dist/levenshtein.js.map +1 -1
- package/dist/solc-api.d.ts +14 -0
- package/dist/solc-api.d.ts.map +1 -1
- package/dist/standalone.d.ts +4 -1
- package/dist/standalone.d.ts.map +1 -1
- package/dist/standalone.js +6 -3
- package/dist/standalone.js.map +1 -1
- package/dist/standalone.test.js +17 -0
- package/dist/standalone.test.js.map +1 -1
- package/dist/storage/compare.d.ts +2 -1
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/compare.js +25 -2
- 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 +48 -21
- package/dist/storage/extract.js.map +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +15 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/layout.d.ts +1 -0
- package/dist/storage/layout.d.ts.map +1 -1
- package/dist/storage/layout.js.map +1 -1
- package/dist/storage/namespace.d.ts +34 -0
- package/dist/storage/namespace.d.ts.map +1 -0
- package/dist/storage/namespace.js +191 -0
- package/dist/storage/namespace.js.map +1 -0
- package/dist/storage/report.js +6 -0
- package/dist/storage/report.js.map +1 -1
- package/dist/storage-namespaced-conflicts-layout.test.d.ts +2 -0
- package/dist/storage-namespaced-conflicts-layout.test.d.ts.map +1 -0
- package/dist/storage-namespaced-conflicts-layout.test.js +69 -0
- package/dist/storage-namespaced-conflicts-layout.test.js.map +1 -0
- package/dist/storage-namespaced-conflicts.test.d.ts +2 -0
- package/dist/storage-namespaced-conflicts.test.d.ts.map +1 -0
- package/dist/storage-namespaced-conflicts.test.js +45 -0
- package/dist/storage-namespaced-conflicts.test.js.map +1 -0
- package/dist/storage-namespaced-layout.test.d.ts +2 -0
- package/dist/storage-namespaced-layout.test.d.ts.map +1 -0
- package/dist/storage-namespaced-layout.test.js +59 -0
- package/dist/storage-namespaced-layout.test.js.map +1 -0
- package/dist/storage-namespaced-outside-contract.test.d.ts +2 -0
- package/dist/storage-namespaced-outside-contract.test.d.ts.map +1 -0
- package/dist/storage-namespaced-outside-contract.test.js +22 -0
- package/dist/storage-namespaced-outside-contract.test.js.map +1 -0
- package/dist/storage-namespaced-solc-versions.test.d.ts +2 -0
- package/dist/storage-namespaced-solc-versions.test.d.ts.map +1 -0
- package/dist/storage-namespaced-solc-versions.test.js +42 -0
- package/dist/storage-namespaced-solc-versions.test.js.map +1 -0
- package/dist/storage-namespaced-udvt.test.d.ts +2 -0
- package/dist/storage-namespaced-udvt.test.d.ts.map +1 -0
- package/dist/storage-namespaced-udvt.test.js +130 -0
- package/dist/storage-namespaced-udvt.test.js.map +1 -0
- package/dist/storage-namespaced.test.d.ts +2 -0
- package/dist/storage-namespaced.test.d.ts.map +1 -0
- package/dist/storage-namespaced.test.js +132 -0
- package/dist/storage-namespaced.test.js.map +1 -0
- package/dist/upgrade-interface-version.d.ts +3 -0
- package/dist/upgrade-interface-version.d.ts.map +1 -0
- package/dist/upgrade-interface-version.js +25 -0
- package/dist/upgrade-interface-version.js.map +1 -0
- package/dist/upgrade-interface-version.test.d.ts +2 -0
- package/dist/upgrade-interface-version.test.d.ts.map +1 -0
- package/dist/upgrade-interface-version.test.js +32 -0
- package/dist/upgrade-interface-version.test.js.map +1 -0
- package/dist/utils/annotations.d.ts +4 -0
- package/dist/utils/annotations.d.ts.map +1 -1
- package/dist/utils/annotations.js +9 -1
- package/dist/utils/annotations.js.map +1 -1
- package/dist/utils/make-namespaced.d.ts +20 -0
- package/dist/utils/make-namespaced.d.ts.map +1 -0
- package/dist/utils/make-namespaced.js +131 -0
- package/dist/utils/make-namespaced.js.map +1 -0
- package/dist/utils/make-namespaced.test.d.ts +2 -0
- package/dist/utils/make-namespaced.test.d.ts.map +1 -0
- package/dist/utils/make-namespaced.test.js +59 -0
- package/dist/utils/make-namespaced.test.js.map +1 -0
- package/dist/utils/stabilize-layout.d.ts +11 -0
- package/dist/utils/stabilize-layout.d.ts.map +1 -1
- package/dist/utils/stabilize-layout.js +5 -0
- package/dist/utils/stabilize-layout.js.map +1 -1
- package/dist/validate/overrides.js +1 -1
- package/dist/validate/overrides.js.map +1 -1
- package/dist/validate/query.d.ts.map +1 -1
- package/dist/validate/query.js +7 -3
- package/dist/validate/query.js.map +1 -1
- package/dist/validate/report.js +2 -2
- package/dist/validate/report.js.map +1 -1
- package/dist/validate/run.d.ts +18 -2
- package/dist/validate/run.d.ts.map +1 -1
- package/dist/validate/run.js +104 -2
- package/dist/validate/run.js.map +1 -1
- package/dist/validate.test.js +3 -0
- package/dist/validate.test.js.map +1 -1
- package/package.json +3 -3
- package/src/call-optional-signature.ts +20 -0
- package/src/cli/cli.test.ts.md +4 -4
- package/src/cli/cli.test.ts.snap +0 -0
- package/src/cli/validate/build-info-file.test.ts +4 -1
- package/src/cli/validate/build-info-file.ts +12 -2
- package/src/cli/validate/project-report.test.ts.md +4 -4
- package/src/cli/validate/project-report.test.ts.snap +0 -0
- package/src/cli/validate/upgradeability-assessment.ts +1 -6
- package/src/cli/validate/validate-upgrade-safety.test.ts.md +2 -2
- package/src/cli/validate/validate-upgrade-safety.test.ts.snap +0 -0
- package/src/cli/validate/validations.test.ts.md +2 -2
- package/src/cli/validate/validations.test.ts.snap +0 -0
- package/src/cli/validate/validations.ts +6 -5
- package/src/impl-address.ts +9 -20
- package/src/index.ts +3 -0
- package/src/levenshtein.ts +7 -0
- package/src/solc-api.ts +14 -0
- package/src/standalone.test.ts +26 -0
- package/src/standalone.ts +6 -2
- package/src/storage/compare.ts +34 -2
- package/src/storage/extract.ts +68 -20
- package/src/storage/index.ts +22 -1
- package/src/storage/layout.ts +1 -0
- package/src/storage/namespace.ts +262 -0
- package/src/storage/report.ts +8 -0
- package/src/storage-0.8.test.ts.md +4 -0
- package/src/storage-0.8.test.ts.snap +0 -0
- package/src/storage-memory-0.5.test.ts.md +2 -0
- package/src/storage-memory-0.5.test.ts.snap +0 -0
- package/src/storage-namespaced-conflicts-layout.test.ts +96 -0
- package/src/storage-namespaced-conflicts-layout.test.ts.md +53 -0
- package/src/storage-namespaced-conflicts-layout.test.ts.snap +0 -0
- package/src/storage-namespaced-conflicts.test.ts +54 -0
- package/src/storage-namespaced-conflicts.test.ts.md +53 -0
- package/src/storage-namespaced-conflicts.test.ts.snap +0 -0
- package/src/storage-namespaced-layout.test.ts +77 -0
- package/src/storage-namespaced-layout.test.ts.md +67 -0
- package/src/storage-namespaced-layout.test.ts.snap +0 -0
- package/src/storage-namespaced-outside-contract.test.ts +24 -0
- package/src/storage-namespaced-solc-versions.test.ts +59 -0
- package/src/storage-namespaced-udvt.test.ts +160 -0
- package/src/storage-namespaced.test.ts +146 -0
- package/src/storage-namespaced.test.ts.md +89 -0
- package/src/storage-namespaced.test.ts.snap +0 -0
- package/src/storage.test.ts.md +2 -0
- package/src/storage.test.ts.snap +0 -0
- package/src/upgrade-interface-version.test.ts +37 -0
- package/src/upgrade-interface-version.ts +27 -0
- package/src/utils/annotations.ts +8 -0
- package/src/utils/make-namespaced.test.ts +74 -0
- package/src/utils/make-namespaced.test.ts.md +154 -0
- package/src/utils/make-namespaced.test.ts.snap +0 -0
- package/src/utils/make-namespaced.ts +154 -0
- package/src/utils/stabilize-layout.ts +5 -0
- package/src/validate/overrides.ts +1 -1
- package/src/validate/query.ts +9 -3
- package/src/validate/report.ts +3 -2
- package/src/validate/run.ts +109 -2
- package/src/validate.test.ts +3 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Snapshot report for `src/utils/make-namespaced.test.ts`
|
|
2
|
+
|
|
3
|
+
The actual snapshot is saved in `make-namespaced.test.ts.snap`.
|
|
4
|
+
|
|
5
|
+
Generated by [AVA](https://avajs.dev).
|
|
6
|
+
|
|
7
|
+
## make namespaced input
|
|
8
|
+
|
|
9
|
+
> Snapshot 1
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
language: 'Solidity',
|
|
13
|
+
settings: {
|
|
14
|
+
optimizer: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
runs: 200,
|
|
17
|
+
},
|
|
18
|
+
outputSelection: {
|
|
19
|
+
'*': {
|
|
20
|
+
'': [
|
|
21
|
+
'ast',
|
|
22
|
+
],
|
|
23
|
+
'*': [
|
|
24
|
+
'storageLayout',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
sources: {
|
|
30
|
+
'contracts/test/NamespacedToModify.sol': {
|
|
31
|
+
content: `// SPDX-License-Identifier: MIT␊
|
|
32
|
+
pragma solidity ^0.8.20;␊
|
|
33
|
+
␊
|
|
34
|
+
contract Example {␊
|
|
35
|
+
/// @custom:storage-location erc7201:example.main␊
|
|
36
|
+
struct MainStorage {␊
|
|
37
|
+
uint256 x;␊
|
|
38
|
+
uint256 y;␊
|
|
39
|
+
} MainStorage $MainStorage_random;␊
|
|
40
|
+
␊
|
|
41
|
+
␊
|
|
42
|
+
␊
|
|
43
|
+
␊
|
|
44
|
+
␊
|
|
45
|
+
␊
|
|
46
|
+
␊
|
|
47
|
+
␊
|
|
48
|
+
␊
|
|
49
|
+
struct MyStruct { uint b; }␊
|
|
50
|
+
␊
|
|
51
|
+
// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));␊
|
|
52
|
+
␊
|
|
53
|
+
␊
|
|
54
|
+
␊
|
|
55
|
+
␊
|
|
56
|
+
␊
|
|
57
|
+
␊
|
|
58
|
+
/// @notice standlone natspec␊
|
|
59
|
+
␊
|
|
60
|
+
␊
|
|
61
|
+
␊
|
|
62
|
+
␊
|
|
63
|
+
// standalone doc␊
|
|
64
|
+
␊
|
|
65
|
+
/**␊
|
|
66
|
+
* standlone doc block␊
|
|
67
|
+
*/␊
|
|
68
|
+
␊
|
|
69
|
+
␊
|
|
70
|
+
␊
|
|
71
|
+
␊
|
|
72
|
+
␊
|
|
73
|
+
␊
|
|
74
|
+
}␊
|
|
75
|
+
␊
|
|
76
|
+
contract HasFunction {␊
|
|
77
|
+
␊
|
|
78
|
+
␊
|
|
79
|
+
}␊
|
|
80
|
+
␊
|
|
81
|
+
contract UsingFunction is HasFunction {␊
|
|
82
|
+
␊
|
|
83
|
+
}␊
|
|
84
|
+
␊
|
|
85
|
+
uint256 constant FreeFunctionUsingSelector = 0;␊
|
|
86
|
+
␊
|
|
87
|
+
uint256 constant CONSTANT_USING_SELECTOR = 0;␊
|
|
88
|
+
␊
|
|
89
|
+
library Lib {␊
|
|
90
|
+
␊
|
|
91
|
+
␊
|
|
92
|
+
␊
|
|
93
|
+
}␊
|
|
94
|
+
␊
|
|
95
|
+
contract Consumer {␊
|
|
96
|
+
␊
|
|
97
|
+
␊
|
|
98
|
+
␊
|
|
99
|
+
␊
|
|
100
|
+
␊
|
|
101
|
+
}␊
|
|
102
|
+
␊
|
|
103
|
+
uint256 constant plusTwo = 0;␊
|
|
104
|
+
␊
|
|
105
|
+
␊
|
|
106
|
+
␊
|
|
107
|
+
contract UsingForDirectives {␊
|
|
108
|
+
␊
|
|
109
|
+
␊
|
|
110
|
+
␊
|
|
111
|
+
}`,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
## make namespaced input - solc 0.7
|
|
117
|
+
|
|
118
|
+
> Snapshot 1
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
language: 'Solidity',
|
|
122
|
+
settings: {
|
|
123
|
+
optimizer: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
runs: 200,
|
|
126
|
+
},
|
|
127
|
+
outputSelection: {
|
|
128
|
+
'*': {
|
|
129
|
+
'': [
|
|
130
|
+
'ast',
|
|
131
|
+
],
|
|
132
|
+
'*': [
|
|
133
|
+
'storageLayout',
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
sources: {
|
|
139
|
+
'contracts/test/NamespacedToModify07.sol': {
|
|
140
|
+
content: `// SPDX-License-Identifier: MIT␊
|
|
141
|
+
pragma solidity 0.7.6;␊
|
|
142
|
+
␊
|
|
143
|
+
contract HasFunction {␊
|
|
144
|
+
␊
|
|
145
|
+
␊
|
|
146
|
+
}␊
|
|
147
|
+
␊
|
|
148
|
+
contract UsingFunction is HasFunction {␊
|
|
149
|
+
␊
|
|
150
|
+
}␊
|
|
151
|
+
`,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { isNodeType } from 'solidity-ast/utils';
|
|
2
|
+
import { Node } from 'solidity-ast/node';
|
|
3
|
+
import { SolcInput, SolcOutput } from '../solc-api';
|
|
4
|
+
import { getStorageLocationAnnotation } from '../storage/namespace';
|
|
5
|
+
import { assert } from './assert';
|
|
6
|
+
|
|
7
|
+
const OUTPUT_SELECTION = {
|
|
8
|
+
'*': {
|
|
9
|
+
'*': ['storageLayout'],
|
|
10
|
+
'': ['ast'],
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Makes a modified version of the solc input to add state variables in each contract for namespaced struct definitions,
|
|
16
|
+
* so that the compiler will generate their types in the storage layout.
|
|
17
|
+
*
|
|
18
|
+
* This makes the following modifications to the input:
|
|
19
|
+
* - Adds a state variable for each namespaced struct definition
|
|
20
|
+
* - Deletes all contracts' functions since they are not needed for storage layout
|
|
21
|
+
* - Deletes all contracts' modifiers, variables, and parent constructor invocations to avoid compilation errors due to deleted functions and constructors
|
|
22
|
+
* - Deletes all using for directives (at file level and in contracts) since they may reference deleted functions
|
|
23
|
+
* - Converts all free functions and constants (at file level) to dummy variables (do not delete them since they might be imported by other files)
|
|
24
|
+
*
|
|
25
|
+
* Also sets the outputSelection to only include storageLayout and ast, since the other outputs are not needed.
|
|
26
|
+
*
|
|
27
|
+
* @param input The original solc input.
|
|
28
|
+
* @param output The original solc output.
|
|
29
|
+
* @returns The modified solc input with storage layout that includes namespaced type information.
|
|
30
|
+
*/
|
|
31
|
+
export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcInput {
|
|
32
|
+
const modifiedSources: Record<string, { content?: string }> = {};
|
|
33
|
+
|
|
34
|
+
for (const [sourcePath] of Object.entries(input.sources)) {
|
|
35
|
+
const source = input.sources[sourcePath];
|
|
36
|
+
|
|
37
|
+
if (source.content === undefined) {
|
|
38
|
+
modifiedSources[sourcePath] = source;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const orig = Buffer.from(source.content, 'utf8');
|
|
43
|
+
|
|
44
|
+
const modifications: Modification[] = [];
|
|
45
|
+
|
|
46
|
+
for (const node of output.sources[sourcePath].ast.nodes) {
|
|
47
|
+
if (isNodeType('ContractDefinition', node)) {
|
|
48
|
+
const contractDef = node;
|
|
49
|
+
|
|
50
|
+
// Remove any calls to parent constructors from the inheritance list
|
|
51
|
+
const inherits = contractDef.baseContracts;
|
|
52
|
+
for (const inherit of inherits) {
|
|
53
|
+
if (Array.isArray(inherit.arguments)) {
|
|
54
|
+
assert(inherit.baseName.name !== undefined);
|
|
55
|
+
modifications.push(makeReplace(inherit, orig, inherit.baseName.name));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const contractNodes = contractDef.nodes;
|
|
60
|
+
for (const contractNode of contractNodes) {
|
|
61
|
+
if (
|
|
62
|
+
isNodeType('FunctionDefinition', contractNode) ||
|
|
63
|
+
isNodeType('ModifierDefinition', contractNode) ||
|
|
64
|
+
isNodeType('VariableDeclaration', contractNode)
|
|
65
|
+
) {
|
|
66
|
+
if (contractNode.documentation) {
|
|
67
|
+
modifications.push(makeDelete(contractNode.documentation, orig));
|
|
68
|
+
}
|
|
69
|
+
modifications.push(makeDelete(contractNode, orig));
|
|
70
|
+
} else if (isNodeType('UsingForDirective', contractNode)) {
|
|
71
|
+
modifications.push(makeDelete(contractNode, orig));
|
|
72
|
+
} else if (isNodeType('StructDefinition', contractNode)) {
|
|
73
|
+
const storageLocation = getStorageLocationAnnotation(contractNode);
|
|
74
|
+
if (storageLocation !== undefined) {
|
|
75
|
+
const structName = contractNode.name;
|
|
76
|
+
const variableName = `$${structName}_${(Math.random() * 1e6).toFixed(0)}`;
|
|
77
|
+
const insertText = ` ${structName} ${variableName};`;
|
|
78
|
+
|
|
79
|
+
modifications.push(makeInsertAfter(contractNode, insertText));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else if (isNodeType('FunctionDefinition', node) || isNodeType('VariableDeclaration', node)) {
|
|
84
|
+
if (node.documentation) {
|
|
85
|
+
modifications.push(makeDelete(node.documentation, orig));
|
|
86
|
+
}
|
|
87
|
+
// Replace with a dummy variable of arbitrary type
|
|
88
|
+
const name = node.name;
|
|
89
|
+
const insertText = `uint256 constant ${name} = 0;`;
|
|
90
|
+
modifications.push(makeReplace(node, orig, insertText));
|
|
91
|
+
} else if (isNodeType('UsingForDirective', node)) {
|
|
92
|
+
modifications.push(makeDelete(node, orig));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
modifiedSources[sourcePath] = { ...source, content: getModifiedSource(orig, modifications) };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { ...input, sources: modifiedSources, settings: { ...input.settings, outputSelection: OUTPUT_SELECTION } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface Modification {
|
|
103
|
+
start: number;
|
|
104
|
+
end: number;
|
|
105
|
+
text?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getPositions(node: Node) {
|
|
109
|
+
const [start, length] = node.src.split(':').map(Number);
|
|
110
|
+
const end = start + length;
|
|
111
|
+
return { start, end };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function makeReplace(node: Node, orig: Buffer, text: string): Modification {
|
|
115
|
+
// Replace is a delete and insert
|
|
116
|
+
const { start, end } = makeDelete(node, orig);
|
|
117
|
+
return { start, end, text };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function makeInsertAfter(node: Node, text: string): Modification {
|
|
121
|
+
const { end } = getPositions(node);
|
|
122
|
+
return { start: end, end, text };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function makeDelete(node: Node, orig: Buffer): Modification {
|
|
126
|
+
const positions = getPositions(node);
|
|
127
|
+
let end = positions.end;
|
|
128
|
+
// If the next character is a semicolon for variables, skip over it
|
|
129
|
+
if (isNodeType('VariableDeclaration', node) && end + 1 < orig.length && orig.toString('utf8', end, end + 1) === ';') {
|
|
130
|
+
end += 1;
|
|
131
|
+
}
|
|
132
|
+
return { start: positions.start, end };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getModifiedSource(orig: Buffer, modifications: Modification[]): string {
|
|
136
|
+
let result = '';
|
|
137
|
+
let copyFromIndex = 0;
|
|
138
|
+
|
|
139
|
+
for (const modification of modifications) {
|
|
140
|
+
assert(modification.start >= copyFromIndex);
|
|
141
|
+
result += orig.toString('utf8', copyFromIndex, modification.start);
|
|
142
|
+
|
|
143
|
+
if (modification.text !== undefined) {
|
|
144
|
+
result += modification.text;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
copyFromIndex = modification.end;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
assert(copyFromIndex <= orig.length);
|
|
151
|
+
result += orig.toString('utf8', copyFromIndex);
|
|
152
|
+
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
@@ -13,5 +13,10 @@ export function stabilizeStorageLayout(layout: StorageLayout) {
|
|
|
13
13
|
: item.members.map(m => ({ ...m, type: stabilizeTypeIdentifier(m.type) })));
|
|
14
14
|
return [stabilizeTypeIdentifier(type), { ...item, members }];
|
|
15
15
|
}),
|
|
16
|
+
namespaces: layout.namespaces
|
|
17
|
+
? Object.entries(layout.namespaces).map(([ns, items]) => {
|
|
18
|
+
return [ns, items.map(item => ({ ...item, type: stabilizeTypeIdentifier(item.type) }))];
|
|
19
|
+
})
|
|
20
|
+
: undefined,
|
|
16
21
|
};
|
|
17
22
|
}
|
|
@@ -73,7 +73,7 @@ export const ValidationErrorUnsafeMessages: Record<ValidationError['kind'], stri
|
|
|
73
73
|
selfdestruct: [`You are using the \`unsafeAllow.selfdestruct\` flag.`],
|
|
74
74
|
'missing-public-upgradeto': [
|
|
75
75
|
`You are using the \`unsafeAllow.missing-public-upgradeto\` flag with uups proxy.`,
|
|
76
|
-
`Not having a public upgradeTo function in your implementation can break upgradeability.`,
|
|
76
|
+
`Not having a public upgradeTo or upgradeToAndCall function in your implementation can break upgradeability.`,
|
|
77
77
|
`Some implementation might check that onchain, and cause the upgrade to revert.`,
|
|
78
78
|
],
|
|
79
79
|
};
|
package/src/validate/query.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ValidationData, normalizeValidationData } from './data';
|
|
|
8
8
|
import { ProxyDeployment } from '../manifest';
|
|
9
9
|
|
|
10
10
|
const upgradeToSignature = 'upgradeTo(address)';
|
|
11
|
+
const upgradeToAndCallSignature = 'upgradeToAndCall(address,bytes)';
|
|
11
12
|
|
|
12
13
|
export function assertUpgradeSafe(data: ValidationData, version: Version, opts: ValidationOptions): void {
|
|
13
14
|
const dataV3 = normalizeValidationData(data);
|
|
@@ -92,9 +93,11 @@ export function unfoldStorageLayout(runData: ValidationRunData, fullContractName
|
|
|
92
93
|
solcVersion,
|
|
93
94
|
storage: c.layout.storage,
|
|
94
95
|
types: c.layout.types,
|
|
96
|
+
namespaces: c.layout.namespaces,
|
|
95
97
|
};
|
|
96
98
|
} else {
|
|
97
|
-
|
|
99
|
+
// Namespaces are pre-flattened
|
|
100
|
+
const layout: StorageLayout = { solcVersion, storage: [], types: {}, namespaces: c.layout.namespaces };
|
|
98
101
|
for (const name of [fullContractName].concat(c.inherit)) {
|
|
99
102
|
layout.storage.unshift(...runData[name].layout.storage);
|
|
100
103
|
Object.assign(layout.types, runData[name].layout.types);
|
|
@@ -147,7 +150,10 @@ export function getErrors(data: ValidationData, version: Version, opts: Validati
|
|
|
147
150
|
|
|
148
151
|
const selfAndInheritedMethods = getAllMethods(runValidation, fullContractName);
|
|
149
152
|
|
|
150
|
-
if (
|
|
153
|
+
if (
|
|
154
|
+
!selfAndInheritedMethods.includes(upgradeToSignature) &&
|
|
155
|
+
!selfAndInheritedMethods.includes(upgradeToAndCallSignature)
|
|
156
|
+
) {
|
|
151
157
|
errors.push({
|
|
152
158
|
src: c.src,
|
|
153
159
|
kind: 'missing-public-upgradeto',
|
|
@@ -188,7 +194,7 @@ export function isUpgradeSafe(data: ValidationData, version: Version): boolean {
|
|
|
188
194
|
|
|
189
195
|
export function inferUUPS(runValidation: ValidationRunData, fullContractName: string): boolean {
|
|
190
196
|
const methods = getAllMethods(runValidation, fullContractName);
|
|
191
|
-
return methods.includes(upgradeToSignature);
|
|
197
|
+
return methods.includes(upgradeToSignature) || methods.includes(upgradeToAndCallSignature);
|
|
192
198
|
}
|
|
193
199
|
|
|
194
200
|
export function inferProxyKind(data: ValidationData, version: Version): ProxyDeployment['kind'] {
|
package/src/validate/report.ts
CHANGED
|
@@ -57,9 +57,10 @@ const errorInfo: ErrorDescriptions<ValidationError> = {
|
|
|
57
57
|
hint: () => `Update your dependency and run again`,
|
|
58
58
|
},
|
|
59
59
|
'missing-public-upgradeto': {
|
|
60
|
-
msg: () =>
|
|
60
|
+
msg: () =>
|
|
61
|
+
`Implementation is missing a public \`upgradeTo(address)\` or \`upgradeToAndCall(address,bytes)\` function`,
|
|
61
62
|
hint: () =>
|
|
62
|
-
`Inherit UUPSUpgradeable to include
|
|
63
|
+
`Inherit UUPSUpgradeable to include one or both of these functions in your contract\n` +
|
|
63
64
|
` @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol`,
|
|
64
65
|
link: 'https://zpl.in/upgrades/error-008',
|
|
65
66
|
},
|
package/src/validate/run.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Node } from 'solidity-ast/node';
|
|
2
2
|
import { isNodeType, findAll, ASTDereferencer, astDereferencer } from 'solidity-ast/utils';
|
|
3
3
|
import type { ContractDefinition, FunctionDefinition } from 'solidity-ast';
|
|
4
|
+
import debug from '../utils/debug';
|
|
5
|
+
import * as versions from 'compare-versions';
|
|
4
6
|
|
|
5
|
-
import { SolcOutput, SolcBytecode } from '../solc-api';
|
|
7
|
+
import { SolcOutput, SolcBytecode, SolcInput } from '../solc-api';
|
|
6
8
|
import { SrcDecoder } from '../src-decoder';
|
|
7
9
|
import { isNullish } from '../utils/is-nullish';
|
|
8
10
|
import { getFunctionSignature } from '../utils/function';
|
|
@@ -12,6 +14,8 @@ import { extractStorageLayout } from '../storage/extract';
|
|
|
12
14
|
import { StorageLayout } from '../storage/layout';
|
|
13
15
|
import { getFullyQualifiedName } from '../utils/contract-name';
|
|
14
16
|
import { getAnnotationArgs as getSupportedAnnotationArgs, getDocumentation } from '../utils/annotations';
|
|
17
|
+
import { getStorageLocationAnnotation } from '../storage/namespace';
|
|
18
|
+
import { UpgradesError } from '../error';
|
|
15
19
|
|
|
16
20
|
export type ValidationRunData = Record<string, ContractValidation>;
|
|
17
21
|
|
|
@@ -116,7 +120,29 @@ function skipCheck(error: string, node: Node): boolean {
|
|
|
116
120
|
return getAllowed(node, false).includes(error) || getAllowed(node, true).includes(error);
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Runs validations on the given solc output.
|
|
125
|
+
*
|
|
126
|
+
* If `namespacedOutput` is provided, it is used to extract storage layout information for namespaced types.
|
|
127
|
+
* It must be from a compilation with the same sources as `solcInput` and `solcOutput`, but with storage variables
|
|
128
|
+
* injected for each namespaced struct so that the types are available in the storage layout. This can be obtained by
|
|
129
|
+
* calling the `makeNamespacedInput` function from this package to create modified solc input, then compiling
|
|
130
|
+
* that modified solc input to get the namespaced output.
|
|
131
|
+
*
|
|
132
|
+
* @param solcOutput Solc output to validate
|
|
133
|
+
* @param decodeSrc Source decoder for the original source code
|
|
134
|
+
* @param solcVersion The version of solc used to compile the contracts
|
|
135
|
+
* @param solcInput Solc input that the compiler was invoked with
|
|
136
|
+
* @param namespacedOutput Namespaced solc output to extract storage layout information for namespaced types
|
|
137
|
+
* @returns A record of validation results for each fully qualified contract name
|
|
138
|
+
*/
|
|
139
|
+
export function validate(
|
|
140
|
+
solcOutput: SolcOutput,
|
|
141
|
+
decodeSrc: SrcDecoder,
|
|
142
|
+
solcVersion?: string,
|
|
143
|
+
solcInput?: SolcInput,
|
|
144
|
+
namespacedOutput?: SolcOutput,
|
|
145
|
+
): ValidationRunData {
|
|
120
146
|
const validation: ValidationRunData = {};
|
|
121
147
|
const fromId: Record<number, string> = {};
|
|
122
148
|
const inheritIds: Record<string, number[]> = {};
|
|
@@ -128,6 +154,9 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
128
154
|
const selfDestructCache = initOpcodeCache();
|
|
129
155
|
|
|
130
156
|
for (const source in solcOutput.contracts) {
|
|
157
|
+
checkNamespaceSolidityVersion(source, solcVersion, solcInput);
|
|
158
|
+
checkNamespacesOutsideContract(source, solcOutput, decodeSrc);
|
|
159
|
+
|
|
131
160
|
for (const contractName in solcOutput.contracts[source]) {
|
|
132
161
|
const bytecode = solcOutput.contracts[source][contractName].evm.bytecode;
|
|
133
162
|
const version = bytecode.object === '' ? undefined : getVersion(bytecode.object);
|
|
@@ -176,7 +205,9 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
176
205
|
decodeSrc,
|
|
177
206
|
deref,
|
|
178
207
|
solcOutput.contracts[source][contractDef.name].storageLayout,
|
|
208
|
+
getNamespacedCompilationContext(source, contractDef, namespacedOutput),
|
|
179
209
|
);
|
|
210
|
+
|
|
180
211
|
validation[key].methods = [...findAll('FunctionDefinition', contractDef)]
|
|
181
212
|
.filter(fnDef => ['external', 'public'].includes(fnDef.visibility))
|
|
182
213
|
.map(fnDef => getFunctionSignature(fnDef, deref));
|
|
@@ -195,6 +226,82 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
195
226
|
return validation;
|
|
196
227
|
}
|
|
197
228
|
|
|
229
|
+
function checkNamespaceSolidityVersion(source: string, solcVersion?: string, solcInput?: SolcInput) {
|
|
230
|
+
if (solcInput === undefined) {
|
|
231
|
+
// This should only be missing if using an old version of the Hardhat or Truffle plugin.
|
|
232
|
+
// Even without this param, namespace layout checks would still occur if compiled with solc version >= 0.8.20
|
|
233
|
+
debug('Cannot check Solidity version for namespaces because solcInput is undefined');
|
|
234
|
+
} else {
|
|
235
|
+
// Solc versions older than 0.8.20 do not have documentation for structs.
|
|
236
|
+
// Use a regex to check for strings that look like namespace annotations, and if found, check that the compiler version is >= 0.8.20
|
|
237
|
+
const content = solcInput.sources[source].content;
|
|
238
|
+
const hasMatch = content !== undefined && content.match(/@custom:storage-location/);
|
|
239
|
+
if (hasMatch) {
|
|
240
|
+
if (solcVersion === undefined) {
|
|
241
|
+
throw new UpgradesError(
|
|
242
|
+
`${source}: Namespace annotations require Solidity version >= 0.8.20, but no solcVersion parameter was provided`,
|
|
243
|
+
() =>
|
|
244
|
+
`Structs with the @custom:storage-location annotation can only be used with Solidity version 0.8.20 or higher. Pass the solcVersion parameter to the validate function, or remove the annotation if the struct is not used for namespaced storage.`,
|
|
245
|
+
);
|
|
246
|
+
} else if (versions.compare(solcVersion, '0.8.20', '<')) {
|
|
247
|
+
throw new UpgradesError(
|
|
248
|
+
`${source}: Namespace annotations require Solidity version >= 0.8.20, but ${solcVersion} was used`,
|
|
249
|
+
() =>
|
|
250
|
+
`Structs with the @custom:storage-location annotation can only be used with Solidity version 0.8.20 or higher. Use a newer version of Solidity, or remove the annotation if the struct is not used for namespaced storage.`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function checkNamespacesOutsideContract(source: string, solcOutput: SolcOutput, decodeSrc: SrcDecoder) {
|
|
258
|
+
for (const node of solcOutput.sources[source].ast.nodes) {
|
|
259
|
+
if (isNodeType('StructDefinition', node)) {
|
|
260
|
+
const storageLocation = getStorageLocationAnnotation(node);
|
|
261
|
+
if (storageLocation !== undefined) {
|
|
262
|
+
throw new UpgradesError(
|
|
263
|
+
`${decodeSrc(node)}: Namespace struct ${node.name} is defined outside of a contract`,
|
|
264
|
+
() =>
|
|
265
|
+
`Structs with the @custom:storage-location annotation must be defined within a contract. Move the struct definition into a contract, or remove the annotation if the struct is not used for namespaced storage.`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getNamespacedCompilationContext(
|
|
273
|
+
source: string,
|
|
274
|
+
contractDef: ContractDefinition,
|
|
275
|
+
namespacedOutput?: SolcOutput,
|
|
276
|
+
) {
|
|
277
|
+
if (namespacedOutput === undefined || contractDef.canonicalName === undefined) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (namespacedOutput.sources[source] === undefined) {
|
|
282
|
+
throw new Error(`Source ${source} not found in namespaced solc output`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const namespacedContractDef = namespacedOutput.sources[source].ast.nodes
|
|
286
|
+
.filter(isNodeType('ContractDefinition'))
|
|
287
|
+
.find(c => c.canonicalName === contractDef.canonicalName);
|
|
288
|
+
|
|
289
|
+
if (namespacedContractDef === undefined) {
|
|
290
|
+
throw new Error(`Contract definition with name ${contractDef.canonicalName} not found in namespaced solc output`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const storageLayout = namespacedOutput.contracts[source][contractDef.name].storageLayout;
|
|
294
|
+
if (storageLayout === undefined) {
|
|
295
|
+
throw new Error(`Storage layout for contract ${contractDef.canonicalName} not found in namespaced solc output`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
contractDef: namespacedContractDef,
|
|
300
|
+
deref: astDereferencer(namespacedOutput),
|
|
301
|
+
storageLayout: storageLayout,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
198
305
|
function* getConstructorErrors(contractDef: ContractDefinition, decodeSrc: SrcDecoder): Generator<ValidationError> {
|
|
199
306
|
for (const fnDef of findAll('FunctionDefinition', contractDef, node => skipCheck('constructor', node))) {
|
|
200
307
|
if (fnDef.kind === 'constructor' && ((fnDef.body?.statements?.length ?? 0) > 0 || fnDef.modifiers.length > 0)) {
|
package/src/validate.test.ts
CHANGED
|
@@ -99,7 +99,10 @@ testOverride('UsesExplicitSafeExternalLibrary', 'transparent', { unsafeAllow: ['
|
|
|
99
99
|
testValid('HasEmptyConstructor', 'uups', false);
|
|
100
100
|
testValid('HasInternalUpgradeToFunction', 'uups', false);
|
|
101
101
|
testValid('HasUpgradeToFunction', 'uups', true);
|
|
102
|
+
testValid('HasInternalUpgradeToAndCallFunction', 'uups', false);
|
|
103
|
+
testValid('HasUpgradeToAndCallFunction', 'uups', true);
|
|
102
104
|
testValid('ParentHasUpgradeToFunction', 'uups', true);
|
|
105
|
+
testValid('ParentHasUpgradeToAndCallFunction', 'uups', true);
|
|
103
106
|
testValid('ChildOfProxiable', 'uups', true);
|
|
104
107
|
|
|
105
108
|
testValid('HasNonEmptyConstructorNatspec1', 'transparent', true);
|