@openzeppelin/upgrades-core 1.42.2 → 1.44.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.
Files changed (88) hide show
  1. package/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +3 -3
  2. package/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json +3 -3
  3. package/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json +2 -2
  4. package/artifacts/@openzeppelin/contracts-v5/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +2 -2
  5. package/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json +2 -2
  6. package/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +3 -3
  7. package/artifacts/build-info-v5.json +5159 -2886
  8. package/dist/provider.d.ts.map +1 -1
  9. package/dist/provider.js +1 -0
  10. package/dist/provider.js.map +1 -1
  11. package/dist/solidity-version.json +1 -1
  12. package/dist/storage/extract.d.ts.map +1 -1
  13. package/dist/storage/extract.js +14 -0
  14. package/dist/storage/extract.js.map +1 -1
  15. package/dist/storage/index.d.ts.map +1 -1
  16. package/dist/storage/index.js +23 -0
  17. package/dist/storage/index.js.map +1 -1
  18. package/dist/storage/layout.d.ts +1 -0
  19. package/dist/storage/layout.d.ts.map +1 -1
  20. package/dist/storage/layout.js.map +1 -1
  21. package/dist/storage/namespace.d.ts.map +1 -1
  22. package/dist/storage/namespace.js +47 -1
  23. package/dist/storage/namespace.js.map +1 -1
  24. package/dist/storage-custom-layout.test.d.ts +2 -0
  25. package/dist/storage-custom-layout.test.d.ts.map +1 -0
  26. package/dist/storage-custom-layout.test.js +314 -0
  27. package/dist/storage-custom-layout.test.js.map +1 -0
  28. package/dist/utils/erc7201.d.ts +10 -0
  29. package/dist/utils/erc7201.d.ts.map +1 -0
  30. package/dist/utils/erc7201.js +24 -0
  31. package/dist/utils/erc7201.js.map +1 -0
  32. package/dist/utils/erc7201.test.d.ts +2 -0
  33. package/dist/utils/erc7201.test.d.ts.map +1 -0
  34. package/dist/utils/erc7201.test.js +11 -0
  35. package/dist/utils/erc7201.test.js.map +1 -0
  36. package/dist/utils/integer-literals.d.ts +15 -0
  37. package/dist/utils/integer-literals.d.ts.map +1 -0
  38. package/dist/utils/integer-literals.js +34 -0
  39. package/dist/utils/integer-literals.js.map +1 -0
  40. package/dist/utils/integer-literals.test.d.ts +2 -0
  41. package/dist/utils/integer-literals.test.d.ts.map +1 -0
  42. package/dist/utils/integer-literals.test.js +57 -0
  43. package/dist/utils/integer-literals.test.js.map +1 -0
  44. package/dist/utils/make-namespaced.d.ts.map +1 -1
  45. package/dist/utils/make-namespaced.js +4 -0
  46. package/dist/utils/make-namespaced.js.map +1 -1
  47. package/dist/utils/make-namespaced.test.js +4 -0
  48. package/dist/utils/make-namespaced.test.js.map +1 -1
  49. package/dist/utils/stabilize-layout.d.ts +1 -0
  50. package/dist/utils/stabilize-layout.d.ts.map +1 -1
  51. package/dist/utils/stabilize-layout.js +1 -0
  52. package/dist/utils/stabilize-layout.js.map +1 -1
  53. package/dist/validate/run/initializer.d.ts.map +1 -1
  54. package/dist/validate/run/initializer.js +42 -10
  55. package/dist/validate/run/initializer.js.map +1 -1
  56. package/dist/validate-initializers.test.js +23 -5
  57. package/dist/validate-initializers.test.js.map +1 -1
  58. package/package.json +7 -4
  59. package/src/provider.ts +1 -0
  60. package/src/solidity-version.json +1 -1
  61. package/src/storage/extract.ts +21 -0
  62. package/src/storage/index.ts +29 -0
  63. package/src/storage/layout.ts +1 -0
  64. package/src/storage/namespace.ts +67 -1
  65. package/src/storage-0.8.test.ts.md +4 -0
  66. package/src/storage-0.8.test.ts.snap +0 -0
  67. package/src/storage-custom-layout.test.ts +358 -0
  68. package/src/storage-custom-layout.test.ts.md +353 -0
  69. package/src/storage-custom-layout.test.ts.snap +0 -0
  70. package/src/storage-memory-0.5.test.ts.md +2 -0
  71. package/src/storage-memory-0.5.test.ts.snap +0 -0
  72. package/src/storage-namespaced-layout.test.ts.md +1 -0
  73. package/src/storage-namespaced-layout.test.ts.snap +0 -0
  74. package/src/storage-namespaced.test.ts.md +2 -0
  75. package/src/storage-namespaced.test.ts.snap +0 -0
  76. package/src/storage.test.ts.md +2 -0
  77. package/src/storage.test.ts.snap +0 -0
  78. package/src/utils/erc7201.test.ts +10 -0
  79. package/src/utils/erc7201.ts +25 -0
  80. package/src/utils/integer-literals.test.ts +73 -0
  81. package/src/utils/integer-literals.ts +29 -0
  82. package/src/utils/make-namespaced.test.ts +7 -0
  83. package/src/utils/make-namespaced.test.ts.md +50 -0
  84. package/src/utils/make-namespaced.test.ts.snap +0 -0
  85. package/src/utils/make-namespaced.ts +5 -0
  86. package/src/utils/stabilize-layout.ts +1 -0
  87. package/src/validate/run/initializer.ts +65 -19
  88. package/src/validate-initializers.test.ts +28 -6
@@ -1,7 +1,10 @@
1
1
  import type { ContractDefinition, FunctionDefinition } from 'solidity-ast';
2
+ import type { Node } from 'solidity-ast/node';
2
3
  import { ASTDereferencer, findAll } from 'solidity-ast/utils';
3
4
  import { SrcDecoder } from '../../src-decoder';
4
5
  import { ValidationExceptionInitializer, skipCheck, tryDerefFunction } from '../run';
6
+ import { getAnnotationArgs, getDocumentation, hasAnnotationTag } from '../../utils/annotations';
7
+ import { logNote } from '../../utils/log';
5
8
 
6
9
  /**
7
10
  * Reports if this contract is non-abstract and any of the following are true:
@@ -20,12 +23,12 @@ export function* getInitializerExceptions(
20
23
  }
21
24
 
22
25
  const linearizedParentContracts = getLinearizedParentContracts(contractDef, deref);
23
- const parentNameToInitializersMap = getParentNameToInitializersMap(linearizedParentContracts);
26
+ const parentNameToInitializersMap = getParentNameToInitializersMap(linearizedParentContracts, decodeSrc);
24
27
 
25
28
  const remainingParents = getParentsNotInitializedByOtherParents(parentNameToInitializersMap);
26
29
 
27
30
  if (remainingParents.length > 0) {
28
- const contractInitializers = getPossibleInitializers(contractDef, false);
31
+ const contractInitializers = getPossibleInitializers(contractDef, false, decodeSrc);
29
32
 
30
33
  // Report if there is no initializer but parents need initialization
31
34
  if (
@@ -71,10 +74,10 @@ function getLinearizedParentContracts(contractDef: ContractDefinition, deref: AS
71
74
  * Gets a map of parent contract names to their possible initializers.
72
75
  * If a parent contract has no initializers, it is not included in the map.
73
76
  */
74
- function getParentNameToInitializersMap(linearizedParentContracts: ContractDefinition[]) {
77
+ function getParentNameToInitializersMap(linearizedParentContracts: ContractDefinition[], decodeSrc: SrcDecoder) {
75
78
  const map = new Map();
76
79
  for (const parent of linearizedParentContracts) {
77
- const initializers = getPossibleInitializers(parent, true);
80
+ const initializers = getPossibleInitializers(parent, true, decodeSrc);
78
81
  if (initializers.length > 0) {
79
82
  map.set(parent.name, initializers);
80
83
  }
@@ -318,22 +321,65 @@ function getRecursiveFunctionIds(
318
321
  }
319
322
 
320
323
  /**
321
- * Get all functions that could be initializers. Does not include private functions.
322
- * For parent contracts, only internal and public functions which contain statements are included.
324
+ * Get all functions that are annotated as initializers or are inferred to be initializers.
325
+ * Logs a note if any reinitializer is found.
323
326
  */
324
- function getPossibleInitializers(contractDef: ContractDefinition, isParentContract: boolean) {
327
+ function getPossibleInitializers(
328
+ contractDef: ContractDefinition,
329
+ isParentContract: boolean,
330
+ decodeSrc: SrcDecoder,
331
+ ): FunctionDefinition[] {
325
332
  const fns = [...findAll('FunctionDefinition', contractDef)];
326
- return fns.filter(
327
- fnDef =>
328
- (fnDef.modifiers.some(modifier => ['initializer', 'onlyInitializing'].includes(modifier.modifierName.name)) ||
329
- ['initialize', 'initializer'].includes(fnDef.name)) &&
330
- // Skip virtual functions without a body, since that indicates an abstract function and is not itself an initializer
331
- !(fnDef.virtual && !fnDef.body) &&
332
- // Ignore private functions, since they cannot be called outside the contract
333
- fnDef.visibility !== 'private' &&
334
- // For parent contracts, only internal and public functions which contain statements need to be called
335
- (isParentContract
336
- ? fnDef.body?.statements?.length && (fnDef.visibility === 'internal' || fnDef.visibility === 'public')
337
- : true),
333
+ return fns.filter((fnDef: FunctionDefinition) => {
334
+ const validateAsInitializer = hasValidateAsInitializerAnnotation(fnDef, decodeSrc);
335
+ if (!validateAsInitializer && fnDef.modifiers.some(modifier => 'reinitializer' === modifier.modifierName.name)) {
336
+ logNote(`Reinitializers are not included in validations by default`, [
337
+ `${decodeSrc(fnDef)}: If you want to validate this function as an initializer, annotate it with '@custom:oz-upgrades-validate-as-initializer'`,
338
+ ]);
339
+ }
340
+
341
+ return inferPossibleInitializer(fnDef, validateAsInitializer, isParentContract);
342
+ });
343
+ }
344
+
345
+ function hasValidateAsInitializerAnnotation(node: Node, decodeSrc: SrcDecoder): boolean {
346
+ const doc = getDocumentation(node);
347
+ const tag = 'oz-upgrades-validate-as-initializer';
348
+ const validateAsInitializer = hasAnnotationTag(doc, tag);
349
+ if (validateAsInitializer) {
350
+ const annotationArgs = getAnnotationArgs(doc, tag);
351
+ if (annotationArgs.length !== 0) {
352
+ throw new Error(`${decodeSrc(node)}: @custom:${tag} annotation must not have any arguments`);
353
+ }
354
+ }
355
+ return validateAsInitializer;
356
+ }
357
+
358
+ function hasInitializerNameOrModifier(fnDef: FunctionDefinition) {
359
+ return (
360
+ fnDef.modifiers.some(modifier => ['initializer', 'onlyInitializing'].includes(modifier.modifierName.name)) ||
361
+ ['initialize', 'initializer'].includes(fnDef.name)
362
+ );
363
+ }
364
+
365
+ /**
366
+ * Infers whether a function could be an initializer. Does not include private functions.
367
+ * For parent contracts, only internal and public functions which contain statements are included.
368
+ */
369
+ function inferPossibleInitializer(
370
+ fnDef: FunctionDefinition,
371
+ validateAsInitializer: boolean,
372
+ isParentContract: boolean,
373
+ ): boolean {
374
+ return (
375
+ (validateAsInitializer || hasInitializerNameOrModifier(fnDef)) &&
376
+ // Skip virtual functions without a body, since that indicates an abstract function and is not itself an initializer
377
+ !(fnDef.virtual && !fnDef.body) &&
378
+ // Ignore private functions, since they cannot be called outside the contract
379
+ fnDef.visibility !== 'private' &&
380
+ // For parent contracts, only internal and public functions which contain statements need to be called
381
+ (isParentContract
382
+ ? Boolean(fnDef.body?.statements?.length) && (fnDef.visibility === 'internal' || fnDef.visibility === 'public')
383
+ : true)
338
384
  );
339
385
  }
@@ -42,7 +42,7 @@ interface ExpectedErrors {
42
42
  count: number;
43
43
  }
44
44
 
45
- function testRejects(name: string, kind: ValidationOptions['kind'], expectedErrors?: ExpectedErrors) {
45
+ function testRejects(name: string, kind: ValidationOptions['kind'], expectedErrors: ExpectedErrors) {
46
46
  testOverride(name, kind, {}, expectedErrors);
47
47
  }
48
48
 
@@ -98,10 +98,18 @@ testRejects('MissingInitializer_Bad', 'transparent', {
98
98
  testAccepts('MissingInitializer_UnsafeAllow_Contract', 'transparent');
99
99
  testOverride('MissingInitializer_Bad', 'transparent', { unsafeAllow: ['missing-initializer'] });
100
100
 
101
+ testAccepts('ValidateAsInitializer_Ok', 'transparent');
102
+
103
+ testRejects('Reinitializer_NotDetected', 'transparent', {
104
+ contains: ['Missing initializer'],
105
+ count: 1,
106
+ });
107
+ testAccepts('Reinitializer_ValidateAsInitializer_Ok', 'transparent');
108
+
101
109
  testAccepts('InitializationOrder_Ok', 'transparent');
102
110
  testAccepts('InitializationOrder_Ok_2', 'transparent');
103
111
 
104
- testAccepts('InitializationOrder_WrongOrder_Bad', 'transparent'); // warn 'Expected initializers to be called for parent contracts in the following order: A, B, C'
112
+ testAccepts('InitializationOrder_WrongOrder_Bad', 'transparent'); // warn 'Expected: A, B, C'
105
113
  testAccepts('InitializationOrder_WrongOrder_UnsafeAllow_Contract', 'transparent');
106
114
  testAccepts('InitializationOrder_WrongOrder_UnsafeAllow_Function', 'transparent');
107
115
  testOverride('InitializationOrder_WrongOrder_Bad', 'transparent', { unsafeAllow: ['incorrect-initializer-order'] }); // skips the warning
@@ -122,7 +130,21 @@ testAccepts('InitializationOrder_Duplicate_UnsafeAllow_Contract', 'transparent')
122
130
  testAccepts('InitializationOrder_Duplicate_UnsafeAllow_Function', 'transparent');
123
131
  testAccepts('InitializationOrder_Duplicate_UnsafeAllow_Call', 'transparent');
124
132
  testOverride('InitializationOrder_Duplicate_Bad', 'transparent', { unsafeAllow: ['duplicate-initializer-call'] });
125
- testAccepts('InitializationOrder_UnsafeAllowDuplicate_But_WrongOrder', 'transparent'); // warn 'Expected initializers to be called for parent contracts in the following order: A, B, C'
133
+ testAccepts('InitializationOrder_UnsafeAllowDuplicate_But_WrongOrder', 'transparent'); // warn 'Expected: A, B, C'
134
+
135
+ testAccepts('InitializationOrder_ValidateAsInitializer_Ok', 'transparent');
136
+ testAccepts('InitializationOrder_ValidateAsInitializer_WrongOrder', 'transparent'); // warn 'Expected: A, B, C, Parent_ValidateAsInitializer'
137
+ testRejects('InitializationOrder_ValidateAsInitializer_MissingCall', 'transparent', {
138
+ contains: ['Missing initializer calls for one or more parent contracts: `Parent_ValidateAsInitializer`'],
139
+ count: 1,
140
+ });
141
+ testRejects('InitializationOrder_ValidateAsInitializer_DuplicateCall', 'transparent', {
142
+ contains: ['Duplicate calls found to initializer `parentAssumeInit` for contract `Parent_ValidateAsInitializer`'],
143
+ count: 1,
144
+ });
145
+
146
+ testAccepts('Parent_ValidateAsInitializer_External_Ok', 'transparent');
147
+ testAccepts('Child_Of_ValidateAsInitializer_External_Ok', 'transparent');
126
148
 
127
149
  testAccepts('WithRequire_Ok', 'transparent');
128
150
  testRejects('WithRequire_Bad', 'transparent', {
@@ -178,11 +200,11 @@ testRejects('TransitiveChild_Bad_Parent', 'transparent', {
178
200
  'Missing initializer calls for one or more parent contracts: `TransitiveGrandparent2`', // occurs twice: missing initializer for child, missing initializer for parent
179
201
  ],
180
202
  count: 2,
181
- }); // warn 'Expected initializers to be called for parent contracts in the following order: TransitiveGrandparent2, TransitiveParent_Bad'
203
+ }); // warn 'Expected: TransitiveGrandparent2, TransitiveParent_Bad'
182
204
  testRejects('TransitiveChild_Bad_Order', 'transparent', {
183
205
  contains: ['Missing initializer calls for one or more parent contracts: `TransitiveGrandparent2`'],
184
206
  count: 1,
185
- }); // warn 'Expected initializers to be called for parent contracts in the following order: TransitiveGrandparent2, TransitiveParent_Bad'
207
+ }); // warn 'Expected: TransitiveGrandparent2, TransitiveParent_Bad'
186
208
  testRejects('TransitiveChild_Good_Order_Bad_Parent', 'transparent', {
187
209
  contains: ['Missing initializer calls for one or more parent contracts: `TransitiveGrandparent2`'],
188
210
  count: 1,
@@ -218,7 +240,7 @@ testAccepts('Recursive_Ok', 'transparent');
218
240
  testAccepts('ERC20_Ok', 'uups');
219
241
  testRejects('ERC20_Bad', 'uups', {
220
242
  contains: [
221
- 'Missing initializer calls for one or more parent contracts: `ERC20Upgradeable, ERC20PausableUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable`',
243
+ 'Missing initializer calls for one or more parent contracts: `ERC20Upgradeable, OwnableUpgradeable, ERC20PermitUpgradeable`',
222
244
  ],
223
245
  count: 1,
224
246
  });