@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.
Files changed (44) hide show
  1. package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +1 -1
  2. package/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.dbg.json +1 -1
  3. package/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.dbg.json +1 -1
  4. package/artifacts/build-info.json +17 -11
  5. package/dist/manifest-storage-layout.test.js +6 -6
  6. package/dist/manifest-storage-layout.test.js.map +1 -1
  7. package/dist/standalone.test.js +6 -0
  8. package/dist/standalone.test.js.map +1 -1
  9. package/dist/storage/compare.d.ts +2 -0
  10. package/dist/storage/compare.d.ts.map +1 -1
  11. package/dist/storage/compare.js +29 -4
  12. package/dist/storage/compare.js.map +1 -1
  13. package/dist/storage/report-retype-from-contract.test.d.ts +2 -0
  14. package/dist/storage/report-retype-from-contract.test.d.ts.map +1 -0
  15. package/dist/storage/report-retype-from-contract.test.js +148 -0
  16. package/dist/storage/report-retype-from-contract.test.js.map +1 -0
  17. package/dist/storage-memory-0.5.test.d.ts +2 -0
  18. package/dist/storage-memory-0.5.test.d.ts.map +1 -0
  19. package/dist/storage-memory-0.5.test.js +74 -0
  20. package/dist/storage-memory-0.5.test.js.map +1 -0
  21. package/dist/validate/data.d.ts +1 -1
  22. package/dist/validate/data.d.ts.map +1 -1
  23. package/dist/validate/data.js +1 -1
  24. package/dist/validate/query.d.ts +16 -1
  25. package/dist/validate/query.d.ts.map +1 -1
  26. package/dist/validate/query.js +49 -22
  27. package/dist/validate/query.js.map +1 -1
  28. package/dist/validate/run.d.ts.map +1 -1
  29. package/dist/validate/run.js +17 -13
  30. package/dist/validate/run.js.map +1 -1
  31. package/dist/validate.test.js +8 -0
  32. package/dist/validate.test.js.map +1 -1
  33. package/package.json +2 -2
  34. package/src/manifest-storage-layout.test.ts +8 -6
  35. package/src/standalone.test.ts +11 -0
  36. package/src/storage/compare.ts +35 -3
  37. package/src/storage/report-retype-from-contract.test.ts +169 -0
  38. package/src/storage-memory-0.5.test.ts +89 -0
  39. package/src/storage-memory-0.5.test.ts.md +175 -0
  40. package/src/storage-memory-0.5.test.ts.snap +0 -0
  41. package/src/validate/data.ts +2 -2
  42. package/src/validate/query.ts +51 -22
  43. package/src/validate/run.ts +19 -13
  44. package/src/validate.test.ts +13 -0
@@ -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 [contractName] = getContractNameAndRunValidation(dataV3, version);
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(contractName, errors);
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
- const { version } = runData[contractName];
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 contractName;
62
+ let fullContractName;
36
63
 
37
64
  for (const validation of dataV3.log) {
38
- contractName = Object.keys(validation).find(
65
+ fullContractName = Object.keys(validation).find(
39
66
  name => validation[name].version?.withMetadata === version.withMetadata,
40
67
  );
41
- if (contractName !== undefined) {
68
+ if (fullContractName !== undefined) {
42
69
  runValidation = validation;
43
70
  break;
44
71
  }
45
72
  }
46
73
 
47
- if (contractName === undefined || runValidation === undefined) {
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 [contractName, runValidation];
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 [contractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
57
- return unfoldStorageLayout(runValidation, contractName);
83
+ const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
84
+ return unfoldStorageLayout(runValidation, fullContractName);
58
85
  }
59
86
 
60
- export function unfoldStorageLayout(runData: ValidationRunData, contractName: string): StorageLayout {
61
- const c = runData[contractName];
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 [contractName].concat(c.inherit)) {
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 [contractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
117
- const c = runValidation[contractName];
143
+ const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
144
+ const c = runValidation[fullContractName];
118
145
 
119
- const errors = getUsedContractsAndLibraries(contractName, runValidation).flatMap(name => runValidation[name].errors);
146
+ const errors = getUsedContractsAndLibraries(fullContractName, runValidation).flatMap(
147
+ name => runValidation[name].errors,
148
+ );
120
149
 
121
- const selfAndInheritedMethods = getAllMethods(runValidation, contractName);
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(contractName, errors, opts);
159
+ return processExceptions(fullContractName, errors, opts);
131
160
  }
132
161
 
133
- function getAllMethods(runValidation: ValidationRunData, contractName: string): string[] {
134
- const c = runValidation[contractName];
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 [contractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
159
- const methods = getAllMethods(runValidation, contractName);
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 {
@@ -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
- fromId[contractDef.id] = contractDef.name;
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 (contractDef.name in validation && bytecode !== undefined) {
154
- inheritIds[contractDef.name] = contractDef.linearizedBaseContracts.slice(1);
155
- libraryIds[contractDef.name] = getReferencedLibraryIds(contractDef);
159
+ if (key in validation && bytecode !== undefined) {
160
+ inheritIds[key] = contractDef.linearizedBaseContracts.slice(1);
161
+ libraryIds[key] = getReferencedLibraryIds(contractDef);
156
162
 
157
- validation[contractDef.name].src = decodeSrc(contractDef);
158
- validation[contractDef.name].errors = [
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[contractDef.name].layout = extractStorageLayout(
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[contractDef.name].methods = [...findAll('FunctionDefinition', contractDef)]
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 contractName in inheritIds) {
181
- validation[contractName].inherit = inheritIds[contractName].map(id => fromId[id]);
186
+ for (const key in inheritIds) {
187
+ validation[key].inherit = inheritIds[key].map(id => fromId[id]);
182
188
  }
183
189
 
184
- for (const contractName in libraryIds) {
185
- validation[contractName].libraries = libraryIds[contractName].map(id => fromId[id]);
190
+ for (const key in libraryIds) {
191
+ validation[key].libraries = libraryIds[key].map(id => fromId[id]);
186
192
  }
187
193
 
188
194
  return validation;
@@ -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
+ });