@openzeppelin/upgrades-core 1.20.3 → 1.20.5
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/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.dbg.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.dbg.json +1 -1
- package/artifacts/build-info.json +17 -11
- package/dist/manifest-storage-layout.test.js +6 -6
- package/dist/manifest-storage-layout.test.js.map +1 -1
- package/dist/standalone.test.js +6 -0
- package/dist/standalone.test.js.map +1 -1
- package/dist/storage/compare.d.ts +2 -0
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/compare.js +29 -4
- package/dist/storage/compare.js.map +1 -1
- package/dist/storage/report-retype-from-contract.test.d.ts +2 -0
- package/dist/storage/report-retype-from-contract.test.d.ts.map +1 -0
- package/dist/storage/report-retype-from-contract.test.js +148 -0
- package/dist/storage/report-retype-from-contract.test.js.map +1 -0
- package/dist/storage-memory-0.5.test.d.ts +2 -0
- package/dist/storage-memory-0.5.test.d.ts.map +1 -0
- package/dist/storage-memory-0.5.test.js +74 -0
- package/dist/storage-memory-0.5.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/query.d.ts +16 -1
- package/dist/validate/query.d.ts.map +1 -1
- package/dist/validate/query.js +49 -22
- package/dist/validate/query.js.map +1 -1
- package/dist/validate/run.d.ts.map +1 -1
- package/dist/validate/run.js +17 -13
- package/dist/validate/run.js.map +1 -1
- package/dist/validate.test.js +8 -0
- package/dist/validate.test.js.map +1 -1
- package/package.json +2 -2
- package/src/manifest-storage-layout.test.ts +8 -6
- package/src/standalone.test.ts +11 -0
- package/src/storage/compare.ts +35 -3
- package/src/storage/report-retype-from-contract.test.ts +169 -0
- package/src/storage-memory-0.5.test.ts +89 -0
- package/src/storage-memory-0.5.test.ts.md +175 -0
- package/src/storage-memory-0.5.test.ts.snap +0 -0
- package/src/validate/data.ts +2 -2
- package/src/validate/query.ts +51 -22
- package/src/validate/run.ts +19 -13
- package/src/validate.test.ts +13 -0
package/src/validate/query.ts
CHANGED
|
@@ -11,54 +11,81 @@ const upgradeToSignature = 'upgradeTo(address)';
|
|
|
11
11
|
|
|
12
12
|
export function assertUpgradeSafe(data: ValidationData, version: Version, opts: ValidationOptions): void {
|
|
13
13
|
const dataV3 = normalizeValidationData(data);
|
|
14
|
-
const [
|
|
14
|
+
const [fullContractName] = getContractNameAndRunValidation(dataV3, version);
|
|
15
15
|
|
|
16
16
|
const errors = getErrors(dataV3, version, opts);
|
|
17
17
|
|
|
18
18
|
if (errors.length > 0) {
|
|
19
|
-
throw new ValidationErrors(
|
|
19
|
+
throw new ValidationErrors(fullContractName, errors);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Gets the contract version object from the given validation run data and contract name (either fully qualified or simple contract name).
|
|
25
|
+
*
|
|
26
|
+
* @param runData The validation run data
|
|
27
|
+
* @param contractName Fully qualified or simple contract name
|
|
28
|
+
* @returns contract version object
|
|
29
|
+
* @throws {Error} if the given contract name is not found or is ambiguous
|
|
30
|
+
*/
|
|
23
31
|
export function getContractVersion(runData: ValidationRunData, contractName: string): Version {
|
|
24
|
-
|
|
32
|
+
let version = undefined;
|
|
33
|
+
if (contractName.includes(':')) {
|
|
34
|
+
version = runData[contractName].version;
|
|
35
|
+
} else {
|
|
36
|
+
const foundNames = Object.keys(runData).filter(element => element.endsWith(`:${contractName}`));
|
|
37
|
+
if (foundNames.length > 1) {
|
|
38
|
+
throw new Error(`Contract ${contractName} is ambiguous. Use one of the following:\n${foundNames.join('\n')}`);
|
|
39
|
+
} else if (foundNames.length === 1) {
|
|
40
|
+
version = runData[foundNames[0]].version;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
if (version === undefined) {
|
|
26
45
|
throw new Error(`Contract ${contractName} is abstract`);
|
|
27
46
|
}
|
|
47
|
+
|
|
28
48
|
return version;
|
|
29
49
|
}
|
|
30
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Gets the fully qualified contract name and validation run data.
|
|
53
|
+
*
|
|
54
|
+
* @param data The validation data
|
|
55
|
+
* @param version The contract Version
|
|
56
|
+
* @returns fully qualified contract name and validation run data
|
|
57
|
+
*/
|
|
31
58
|
export function getContractNameAndRunValidation(data: ValidationData, version: Version): [string, ValidationRunData] {
|
|
32
59
|
const dataV3 = normalizeValidationData(data);
|
|
33
60
|
|
|
34
61
|
let runValidation;
|
|
35
|
-
let
|
|
62
|
+
let fullContractName;
|
|
36
63
|
|
|
37
64
|
for (const validation of dataV3.log) {
|
|
38
|
-
|
|
65
|
+
fullContractName = Object.keys(validation).find(
|
|
39
66
|
name => validation[name].version?.withMetadata === version.withMetadata,
|
|
40
67
|
);
|
|
41
|
-
if (
|
|
68
|
+
if (fullContractName !== undefined) {
|
|
42
69
|
runValidation = validation;
|
|
43
70
|
break;
|
|
44
71
|
}
|
|
45
72
|
}
|
|
46
73
|
|
|
47
|
-
if (
|
|
74
|
+
if (fullContractName === undefined || runValidation === undefined) {
|
|
48
75
|
throw new Error('The requested contract was not found. Make sure the source code is available for compilation');
|
|
49
76
|
}
|
|
50
77
|
|
|
51
|
-
return [
|
|
78
|
+
return [fullContractName, runValidation];
|
|
52
79
|
}
|
|
53
80
|
|
|
54
81
|
export function getStorageLayout(data: ValidationData, version: Version): StorageLayout {
|
|
55
82
|
const dataV3 = normalizeValidationData(data);
|
|
56
|
-
const [
|
|
57
|
-
return unfoldStorageLayout(runValidation,
|
|
83
|
+
const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
|
|
84
|
+
return unfoldStorageLayout(runValidation, fullContractName);
|
|
58
85
|
}
|
|
59
86
|
|
|
60
|
-
export function unfoldStorageLayout(runData: ValidationRunData,
|
|
61
|
-
const c = runData[
|
|
87
|
+
export function unfoldStorageLayout(runData: ValidationRunData, fullContractName: string): StorageLayout {
|
|
88
|
+
const c = runData[fullContractName];
|
|
62
89
|
const { solcVersion } = c;
|
|
63
90
|
if (c.layout.flat) {
|
|
64
91
|
return {
|
|
@@ -68,7 +95,7 @@ export function unfoldStorageLayout(runData: ValidationRunData, contractName: st
|
|
|
68
95
|
};
|
|
69
96
|
} else {
|
|
70
97
|
const layout: StorageLayout = { solcVersion, storage: [], types: {} };
|
|
71
|
-
for (const name of [
|
|
98
|
+
for (const name of [fullContractName].concat(c.inherit)) {
|
|
72
99
|
layout.storage.unshift(...runData[name].layout.storage);
|
|
73
100
|
Object.assign(layout.types, runData[name].layout.types);
|
|
74
101
|
}
|
|
@@ -113,12 +140,14 @@ export function getUnlinkedBytecode(data: ValidationData, bytecode: string): str
|
|
|
113
140
|
|
|
114
141
|
export function getErrors(data: ValidationData, version: Version, opts: ValidationOptions = {}): ValidationError[] {
|
|
115
142
|
const dataV3 = normalizeValidationData(data);
|
|
116
|
-
const [
|
|
117
|
-
const c = runValidation[
|
|
143
|
+
const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
|
|
144
|
+
const c = runValidation[fullContractName];
|
|
118
145
|
|
|
119
|
-
const errors = getUsedContractsAndLibraries(
|
|
146
|
+
const errors = getUsedContractsAndLibraries(fullContractName, runValidation).flatMap(
|
|
147
|
+
name => runValidation[name].errors,
|
|
148
|
+
);
|
|
120
149
|
|
|
121
|
-
const selfAndInheritedMethods = getAllMethods(runValidation,
|
|
150
|
+
const selfAndInheritedMethods = getAllMethods(runValidation, fullContractName);
|
|
122
151
|
|
|
123
152
|
if (!selfAndInheritedMethods.includes(upgradeToSignature)) {
|
|
124
153
|
errors.push({
|
|
@@ -127,11 +156,11 @@ export function getErrors(data: ValidationData, version: Version, opts: Validati
|
|
|
127
156
|
});
|
|
128
157
|
}
|
|
129
158
|
|
|
130
|
-
return processExceptions(
|
|
159
|
+
return processExceptions(fullContractName, errors, opts);
|
|
131
160
|
}
|
|
132
161
|
|
|
133
|
-
function getAllMethods(runValidation: ValidationRunData,
|
|
134
|
-
const c = runValidation[
|
|
162
|
+
function getAllMethods(runValidation: ValidationRunData, fullContractName: string): string[] {
|
|
163
|
+
const c = runValidation[fullContractName];
|
|
135
164
|
return c.methods.concat(...c.inherit.map(name => runValidation[name].methods));
|
|
136
165
|
}
|
|
137
166
|
|
|
@@ -155,8 +184,8 @@ export function isUpgradeSafe(data: ValidationData, version: Version): boolean {
|
|
|
155
184
|
|
|
156
185
|
export function inferProxyKind(data: ValidationData, version: Version): ProxyDeployment['kind'] {
|
|
157
186
|
const dataV3 = normalizeValidationData(data);
|
|
158
|
-
const [
|
|
159
|
-
const methods = getAllMethods(runValidation,
|
|
187
|
+
const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
|
|
188
|
+
const methods = getAllMethods(runValidation, fullContractName);
|
|
160
189
|
if (methods.includes(upgradeToSignature)) {
|
|
161
190
|
return 'uups';
|
|
162
191
|
} else {
|
package/src/validate/run.ts
CHANGED
|
@@ -114,6 +114,10 @@ function skipCheck(error: string, node: Node): boolean {
|
|
|
114
114
|
return getAllowed(node).includes(error);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function getFullyQualifiedName(source: string, contractName: string) {
|
|
118
|
+
return `${source}:${contractName}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
117
121
|
export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVersion?: string): ValidationRunData {
|
|
118
122
|
const validation: ValidationRunData = {};
|
|
119
123
|
const fromId: Record<number, string> = {};
|
|
@@ -128,7 +132,7 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
128
132
|
const version = bytecode.object === '' ? undefined : getVersion(bytecode.object);
|
|
129
133
|
const linkReferences = extractLinkReferences(bytecode);
|
|
130
134
|
|
|
131
|
-
validation[contractName] = {
|
|
135
|
+
validation[getFullyQualifiedName(source, contractName)] = {
|
|
132
136
|
src: contractName,
|
|
133
137
|
version,
|
|
134
138
|
inherit: [],
|
|
@@ -145,17 +149,19 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) {
|
|
148
|
-
|
|
152
|
+
const key = getFullyQualifiedName(source, contractDef.name);
|
|
153
|
+
|
|
154
|
+
fromId[contractDef.id] = key;
|
|
149
155
|
|
|
150
156
|
// May be undefined in case of duplicate contract names in Truffle
|
|
151
157
|
const bytecode = solcOutput.contracts[source][contractDef.name]?.evm.bytecode;
|
|
152
158
|
|
|
153
|
-
if (
|
|
154
|
-
inheritIds[
|
|
155
|
-
libraryIds[
|
|
159
|
+
if (key in validation && bytecode !== undefined) {
|
|
160
|
+
inheritIds[key] = contractDef.linearizedBaseContracts.slice(1);
|
|
161
|
+
libraryIds[key] = getReferencedLibraryIds(contractDef);
|
|
156
162
|
|
|
157
|
-
validation[
|
|
158
|
-
validation[
|
|
163
|
+
validation[key].src = decodeSrc(contractDef);
|
|
164
|
+
validation[key].errors = [
|
|
159
165
|
...getConstructorErrors(contractDef, decodeSrc),
|
|
160
166
|
...getOpcodeErrors(contractDef, decodeSrc),
|
|
161
167
|
...getStateVariableErrors(contractDef, decodeSrc),
|
|
@@ -164,25 +170,25 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
164
170
|
...getLinkingErrors(contractDef, bytecode),
|
|
165
171
|
];
|
|
166
172
|
|
|
167
|
-
validation[
|
|
173
|
+
validation[key].layout = extractStorageLayout(
|
|
168
174
|
contractDef,
|
|
169
175
|
decodeSrc,
|
|
170
176
|
deref,
|
|
171
177
|
solcOutput.contracts[source][contractDef.name].storageLayout,
|
|
172
178
|
);
|
|
173
|
-
validation[
|
|
179
|
+
validation[key].methods = [...findAll('FunctionDefinition', contractDef)]
|
|
174
180
|
.filter(fnDef => ['external', 'public'].includes(fnDef.visibility))
|
|
175
181
|
.map(fnDef => getFunctionSignature(fnDef, deref));
|
|
176
182
|
}
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
|
|
180
|
-
for (const
|
|
181
|
-
validation[
|
|
186
|
+
for (const key in inheritIds) {
|
|
187
|
+
validation[key].inherit = inheritIds[key].map(id => fromId[id]);
|
|
182
188
|
}
|
|
183
189
|
|
|
184
|
-
for (const
|
|
185
|
-
validation[
|
|
190
|
+
for (const key in libraryIds) {
|
|
191
|
+
validation[key].libraries = libraryIds[key].map(id => fromId[id]);
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
return validation;
|
package/src/validate.test.ts
CHANGED
|
@@ -133,3 +133,16 @@ testValid('UsesExplicitSafeExternalLibraryNatspec', 'transparent', true);
|
|
|
133
133
|
testValid('UsesExplicitUnsafeInternalLibraryNatspec', 'transparent', true);
|
|
134
134
|
testValid('UsesExplicitUnsafeExternalLibraryNatspec', 'transparent', true);
|
|
135
135
|
testValid('TransitiveLibraryIsUnsafe', 'transparent', false);
|
|
136
|
+
|
|
137
|
+
testValid('contracts/test/ValidationsSameNameSafe.sol:SameName', 'transparent', true);
|
|
138
|
+
testValid('contracts/test/ValidationsSameNameUnsafe.sol:SameName', 'transparent', false);
|
|
139
|
+
|
|
140
|
+
test('ambiguous name', t => {
|
|
141
|
+
const error = t.throws(() => getContractVersion(t.context.validation, 'SameName'));
|
|
142
|
+
t.is(
|
|
143
|
+
error?.message,
|
|
144
|
+
'Contract SameName is ambiguous. Use one of the following:\n' +
|
|
145
|
+
'contracts/test/ValidationsSameNameSafe.sol:SameName\n' +
|
|
146
|
+
'contracts/test/ValidationsSameNameUnsafe.sol:SameName',
|
|
147
|
+
);
|
|
148
|
+
});
|