@openzeppelin/upgrades-core 1.29.0 → 1.30.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/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 +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -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 +35 -0
- package/dist/storage/namespace.d.ts.map +1 -0
- package/dist/storage/namespace.js +219 -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/query.d.ts.map +1 -1
- package/dist/validate/query.js +4 -2
- package/dist/validate/query.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 +80 -2
- package/dist/validate/run.js.map +1 -1
- package/package.json +3 -3
- package/src/call-optional-signature.ts +20 -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/upgradeability-assessment.ts +1 -6
- package/src/cli/validate/validations.ts +6 -5
- package/src/impl-address.ts +9 -20
- package/src/index.ts +4 -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 +267 -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/query.ts +4 -2
- package/src/validate/run.ts +108 -2
|
@@ -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
|
}
|
package/src/validate/query.ts
CHANGED
|
@@ -93,9 +93,11 @@ export function unfoldStorageLayout(runData: ValidationRunData, fullContractName
|
|
|
93
93
|
solcVersion,
|
|
94
94
|
storage: c.layout.storage,
|
|
95
95
|
types: c.layout.types,
|
|
96
|
+
namespaces: c.layout.namespaces,
|
|
96
97
|
};
|
|
97
98
|
} else {
|
|
98
|
-
|
|
99
|
+
// Namespaces are pre-flattened
|
|
100
|
+
const layout: StorageLayout = { solcVersion, storage: [], types: {}, namespaces: c.layout.namespaces };
|
|
99
101
|
for (const name of [fullContractName].concat(c.inherit)) {
|
|
100
102
|
layout.storage.unshift(...runData[name].layout.storage);
|
|
101
103
|
Object.assign(layout.types, runData[name].layout.types);
|
|
@@ -192,7 +194,7 @@ export function isUpgradeSafe(data: ValidationData, version: Version): boolean {
|
|
|
192
194
|
|
|
193
195
|
export function inferUUPS(runValidation: ValidationRunData, fullContractName: string): boolean {
|
|
194
196
|
const methods = getAllMethods(runValidation, fullContractName);
|
|
195
|
-
return methods.includes(upgradeToSignature);
|
|
197
|
+
return methods.includes(upgradeToSignature) || methods.includes(upgradeToAndCallSignature);
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
export function inferProxyKind(data: ValidationData, version: Version): ProxyDeployment['kind'] {
|
package/src/validate/run.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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';
|
|
4
5
|
|
|
5
|
-
import { SolcOutput, SolcBytecode } from '../solc-api';
|
|
6
|
+
import { SolcOutput, SolcBytecode, SolcInput } from '../solc-api';
|
|
6
7
|
import { SrcDecoder } from '../src-decoder';
|
|
7
8
|
import { isNullish } from '../utils/is-nullish';
|
|
8
9
|
import { getFunctionSignature } from '../utils/function';
|
|
@@ -12,6 +13,8 @@ import { extractStorageLayout } from '../storage/extract';
|
|
|
12
13
|
import { StorageLayout } from '../storage/layout';
|
|
13
14
|
import { getFullyQualifiedName } from '../utils/contract-name';
|
|
14
15
|
import { getAnnotationArgs as getSupportedAnnotationArgs, getDocumentation } from '../utils/annotations';
|
|
16
|
+
import { getStorageLocationAnnotation, isNamespaceSupported } from '../storage/namespace';
|
|
17
|
+
import { UpgradesError } from '../error';
|
|
15
18
|
|
|
16
19
|
export type ValidationRunData = Record<string, ContractValidation>;
|
|
17
20
|
|
|
@@ -116,7 +119,29 @@ function skipCheck(error: string, node: Node): boolean {
|
|
|
116
119
|
return getAllowed(node, false).includes(error) || getAllowed(node, true).includes(error);
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Runs validations on the given solc output.
|
|
124
|
+
*
|
|
125
|
+
* If `namespacedOutput` is provided, it is used to extract storage layout information for namespaced types.
|
|
126
|
+
* It must be from a compilation with the same sources as `solcInput` and `solcOutput`, but with storage variables
|
|
127
|
+
* injected for each namespaced struct so that the types are available in the storage layout. This can be obtained by
|
|
128
|
+
* calling the `makeNamespacedInput` function from this package to create modified solc input, then compiling
|
|
129
|
+
* that modified solc input to get the namespaced output.
|
|
130
|
+
*
|
|
131
|
+
* @param solcOutput Solc output to validate
|
|
132
|
+
* @param decodeSrc Source decoder for the original source code
|
|
133
|
+
* @param solcVersion The version of solc used to compile the contracts
|
|
134
|
+
* @param solcInput Solc input that the compiler was invoked with
|
|
135
|
+
* @param namespacedOutput Namespaced solc output to extract storage layout information for namespaced types
|
|
136
|
+
* @returns A record of validation results for each fully qualified contract name
|
|
137
|
+
*/
|
|
138
|
+
export function validate(
|
|
139
|
+
solcOutput: SolcOutput,
|
|
140
|
+
decodeSrc: SrcDecoder,
|
|
141
|
+
solcVersion?: string,
|
|
142
|
+
solcInput?: SolcInput,
|
|
143
|
+
namespacedOutput?: SolcOutput,
|
|
144
|
+
): ValidationRunData {
|
|
120
145
|
const validation: ValidationRunData = {};
|
|
121
146
|
const fromId: Record<number, string> = {};
|
|
122
147
|
const inheritIds: Record<string, number[]> = {};
|
|
@@ -128,6 +153,9 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
128
153
|
const selfDestructCache = initOpcodeCache();
|
|
129
154
|
|
|
130
155
|
for (const source in solcOutput.contracts) {
|
|
156
|
+
checkNamespaceSolidityVersion(source, solcVersion, solcInput);
|
|
157
|
+
checkNamespacesOutsideContract(source, solcOutput, decodeSrc);
|
|
158
|
+
|
|
131
159
|
for (const contractName in solcOutput.contracts[source]) {
|
|
132
160
|
const bytecode = solcOutput.contracts[source][contractName].evm.bytecode;
|
|
133
161
|
const version = bytecode.object === '' ? undefined : getVersion(bytecode.object);
|
|
@@ -176,7 +204,9 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
176
204
|
decodeSrc,
|
|
177
205
|
deref,
|
|
178
206
|
solcOutput.contracts[source][contractDef.name].storageLayout,
|
|
207
|
+
getNamespacedCompilationContext(source, contractDef, namespacedOutput),
|
|
179
208
|
);
|
|
209
|
+
|
|
180
210
|
validation[key].methods = [...findAll('FunctionDefinition', contractDef)]
|
|
181
211
|
.filter(fnDef => ['external', 'public'].includes(fnDef.visibility))
|
|
182
212
|
.map(fnDef => getFunctionSignature(fnDef, deref));
|
|
@@ -195,6 +225,82 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
195
225
|
return validation;
|
|
196
226
|
}
|
|
197
227
|
|
|
228
|
+
function checkNamespaceSolidityVersion(source: string, solcVersion?: string, solcInput?: SolcInput) {
|
|
229
|
+
if (solcInput === undefined) {
|
|
230
|
+
// This should only be missing if using an old version of the Hardhat or Truffle plugin.
|
|
231
|
+
// Even without this param, namespace layout checks would still occur if compiled with solc version >= 0.8.20
|
|
232
|
+
debug('Cannot check Solidity version for namespaces because solcInput is undefined');
|
|
233
|
+
} else {
|
|
234
|
+
// Solc versions older than 0.8.20 do not have documentation for structs.
|
|
235
|
+
// Use a regex to check for strings that look like namespace annotations, and if found, check that the compiler version is >= 0.8.20
|
|
236
|
+
const content = solcInput.sources[source].content;
|
|
237
|
+
const hasMatch = content !== undefined && content.match(/@custom:storage-location/);
|
|
238
|
+
if (hasMatch) {
|
|
239
|
+
if (solcVersion === undefined) {
|
|
240
|
+
throw new UpgradesError(
|
|
241
|
+
`${source}: Namespace annotations require Solidity version >= 0.8.20, but no solcVersion parameter was provided`,
|
|
242
|
+
() =>
|
|
243
|
+
`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.`,
|
|
244
|
+
);
|
|
245
|
+
} else if (!isNamespaceSupported(solcVersion)) {
|
|
246
|
+
throw new UpgradesError(
|
|
247
|
+
`${source}: Namespace annotations require Solidity version >= 0.8.20, but ${solcVersion} was used`,
|
|
248
|
+
() =>
|
|
249
|
+
`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.`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function checkNamespacesOutsideContract(source: string, solcOutput: SolcOutput, decodeSrc: SrcDecoder) {
|
|
257
|
+
for (const node of solcOutput.sources[source].ast.nodes) {
|
|
258
|
+
if (isNodeType('StructDefinition', node)) {
|
|
259
|
+
const storageLocation = getStorageLocationAnnotation(node);
|
|
260
|
+
if (storageLocation !== undefined) {
|
|
261
|
+
throw new UpgradesError(
|
|
262
|
+
`${decodeSrc(node)}: Namespace struct ${node.name} is defined outside of a contract`,
|
|
263
|
+
() =>
|
|
264
|
+
`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.`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function getNamespacedCompilationContext(
|
|
272
|
+
source: string,
|
|
273
|
+
contractDef: ContractDefinition,
|
|
274
|
+
namespacedOutput?: SolcOutput,
|
|
275
|
+
) {
|
|
276
|
+
if (namespacedOutput === undefined || contractDef.canonicalName === undefined) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (namespacedOutput.sources[source] === undefined) {
|
|
281
|
+
throw new Error(`Source ${source} not found in namespaced solc output`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const namespacedContractDef = namespacedOutput.sources[source].ast.nodes
|
|
285
|
+
.filter(isNodeType('ContractDefinition'))
|
|
286
|
+
.find(c => c.canonicalName === contractDef.canonicalName);
|
|
287
|
+
|
|
288
|
+
if (namespacedContractDef === undefined) {
|
|
289
|
+
throw new Error(`Contract definition with name ${contractDef.canonicalName} not found in namespaced solc output`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const storageLayout = namespacedOutput.contracts[source][contractDef.name].storageLayout;
|
|
293
|
+
if (storageLayout === undefined) {
|
|
294
|
+
throw new Error(`Storage layout for contract ${contractDef.canonicalName} not found in namespaced solc output`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
contractDef: namespacedContractDef,
|
|
299
|
+
deref: astDereferencer(namespacedOutput),
|
|
300
|
+
storageLayout: storageLayout,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
198
304
|
function* getConstructorErrors(contractDef: ContractDefinition, decodeSrc: SrcDecoder): Generator<ValidationError> {
|
|
199
305
|
for (const fnDef of findAll('FunctionDefinition', contractDef, node => skipCheck('constructor', node))) {
|
|
200
306
|
if (fnDef.kind === 'constructor' && ((fnDef.body?.statements?.length ?? 0) > 0 || fnDef.modifiers.length > 0)) {
|