@openzeppelin/upgrades-core 1.27.3 → 1.29.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 (125) hide show
  1. package/dist/cli/cli.test.js +93 -3
  2. package/dist/cli/cli.test.js.map +1 -1
  3. package/dist/cli/index.d.ts +1 -1
  4. package/dist/cli/index.d.ts.map +1 -1
  5. package/dist/cli/index.js +2 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/validate/build-info-file.test.js +2 -4
  8. package/dist/cli/validate/build-info-file.test.js.map +1 -1
  9. package/dist/cli/validate/contract-report.d.ts +3 -1
  10. package/dist/cli/validate/contract-report.d.ts.map +1 -1
  11. package/dist/cli/validate/contract-report.js +7 -6
  12. package/dist/cli/validate/contract-report.js.map +1 -1
  13. package/dist/cli/validate/find-contract.d.ts +14 -0
  14. package/dist/cli/validate/find-contract.d.ts.map +1 -0
  15. package/dist/cli/validate/find-contract.js +32 -0
  16. package/dist/cli/validate/find-contract.js.map +1 -0
  17. package/dist/cli/validate/project-report.test.js +1 -2
  18. package/dist/cli/validate/project-report.test.js.map +1 -1
  19. package/dist/cli/validate/upgradeability-assessment.d.ts +1 -12
  20. package/dist/cli/validate/upgradeability-assessment.d.ts.map +1 -1
  21. package/dist/cli/validate/upgradeability-assessment.js +17 -40
  22. package/dist/cli/validate/upgradeability-assessment.js.map +1 -1
  23. package/dist/cli/validate/validate-upgrade-safety.d.ts +8 -1
  24. package/dist/cli/validate/validate-upgrade-safety.d.ts.map +1 -1
  25. package/dist/cli/validate/validate-upgrade-safety.js +20 -2
  26. package/dist/cli/validate/validate-upgrade-safety.js.map +1 -1
  27. package/dist/cli/validate/validate-upgrade-safety.test.js +4 -6
  28. package/dist/cli/validate/validate-upgrade-safety.test.js.map +1 -1
  29. package/dist/cli/validate.d.ts +2 -0
  30. package/dist/cli/validate.d.ts.map +1 -1
  31. package/dist/cli/validate.js +19 -3
  32. package/dist/cli/validate.js.map +1 -1
  33. package/dist/cli/validate.test.js.map +1 -1
  34. package/dist/deployment.d.ts.map +1 -1
  35. package/dist/deployment.js.map +1 -1
  36. package/dist/impl-store.test.js +2 -4
  37. package/dist/impl-store.test.js.map +1 -1
  38. package/dist/scripts/migrate-oz-cli-project.test.js +2 -4
  39. package/dist/scripts/migrate-oz-cli-project.test.js.map +1 -1
  40. package/dist/standalone.d.ts.map +1 -1
  41. package/dist/standalone.js.map +1 -1
  42. package/dist/storage/compare.d.ts.map +1 -1
  43. package/dist/storage/compare.js.map +1 -1
  44. package/dist/storage/extract.d.ts +1 -1
  45. package/dist/storage/extract.d.ts.map +1 -1
  46. package/dist/storage/extract.js.map +1 -1
  47. package/dist/storage/report-gap.test.js +1 -2
  48. package/dist/storage/report-gap.test.js.map +1 -1
  49. package/dist/storage/report-rename-retype.test.js +1 -2
  50. package/dist/storage/report-rename-retype.test.js.map +1 -1
  51. package/dist/storage/report-retype-from-contract.test.js +1 -2
  52. package/dist/storage/report-retype-from-contract.test.js.map +1 -1
  53. package/dist/storage/report.test.js +1 -2
  54. package/dist/storage/report.test.js.map +1 -1
  55. package/dist/storage-0.8.test.js +1 -2
  56. package/dist/storage-0.8.test.js.map +1 -1
  57. package/dist/storage-memory-0.5.test.js +1 -2
  58. package/dist/storage-memory-0.5.test.js.map +1 -1
  59. package/dist/storage.test.js +1 -2
  60. package/dist/storage.test.js.map +1 -1
  61. package/dist/utils/function.d.ts +1 -1
  62. package/dist/utils/function.d.ts.map +1 -1
  63. package/dist/utils/function.test.js +1 -2
  64. package/dist/utils/function.test.js.map +1 -1
  65. package/dist/validate/error.d.ts.map +1 -1
  66. package/dist/validate/error.js.map +1 -1
  67. package/dist/validate/overrides.js +1 -1
  68. package/dist/validate/overrides.js.map +1 -1
  69. package/dist/validate/query.d.ts.map +1 -1
  70. package/dist/validate/query.js +3 -1
  71. package/dist/validate/query.js.map +1 -1
  72. package/dist/validate/report.js +2 -2
  73. package/dist/validate/report.js.map +1 -1
  74. package/dist/validate/run.d.ts +1 -1
  75. package/dist/validate/run.d.ts.map +1 -1
  76. package/dist/validate/run.js +1 -2
  77. package/dist/validate/run.js.map +1 -1
  78. package/dist/validate.test.js +3 -0
  79. package/dist/validate.test.js.map +1 -1
  80. package/package.json +7 -7
  81. package/src/cli/cli.test.ts +131 -2
  82. package/src/cli/cli.test.ts.md +96 -2
  83. package/src/cli/cli.test.ts.snap +0 -0
  84. package/src/cli/index.ts +1 -1
  85. package/src/cli/validate/build-info-file.test.ts +1 -4
  86. package/src/cli/validate/contract-report.ts +19 -7
  87. package/src/cli/validate/find-contract.ts +44 -0
  88. package/src/cli/validate/project-report.test.ts +1 -2
  89. package/src/cli/validate/project-report.test.ts.md +4 -4
  90. package/src/cli/validate/project-report.test.ts.snap +0 -0
  91. package/src/cli/validate/upgradeability-assessment.ts +18 -50
  92. package/src/cli/validate/validate-upgrade-safety.test.ts +2 -5
  93. package/src/cli/validate/validate-upgrade-safety.test.ts.md +2 -2
  94. package/src/cli/validate/validate-upgrade-safety.test.ts.snap +0 -0
  95. package/src/cli/validate/validate-upgrade-safety.ts +32 -2
  96. package/src/cli/validate/validations.test.ts.md +2 -2
  97. package/src/cli/validate/validations.test.ts.snap +0 -0
  98. package/src/cli/validate.test.ts +1 -1
  99. package/src/cli/validate.ts +28 -4
  100. package/src/deployment.ts +5 -1
  101. package/src/impl-store.test.ts +1 -4
  102. package/src/scripts/migrate-oz-cli-project.test.ts +1 -3
  103. package/src/standalone.ts +6 -1
  104. package/src/storage/compare.ts +4 -1
  105. package/src/storage/extract.ts +1 -2
  106. package/src/storage/report-gap.test.ts +1 -2
  107. package/src/storage/report-rename-retype.test.ts +1 -2
  108. package/src/storage/report-retype-from-contract.test.ts +1 -2
  109. package/src/storage/report.test.ts +1 -2
  110. package/src/storage-0.8.test.ts +1 -2
  111. package/src/storage-memory-0.5.test.ts +1 -2
  112. package/src/storage.test.ts +1 -2
  113. package/src/utils/function.test.ts +1 -2
  114. package/src/utils/function.ts +1 -1
  115. package/src/validate/error.ts +4 -1
  116. package/src/validate/overrides.ts +1 -1
  117. package/src/validate/query.ts +5 -1
  118. package/src/validate/report.ts +3 -2
  119. package/src/validate/run.ts +2 -3
  120. package/src/validate.test.ts +3 -0
  121. package/dist/ast-dereferencer.d.ts +0 -8
  122. package/dist/ast-dereferencer.d.ts.map +0 -1
  123. package/dist/ast-dereferencer.js +0 -29
  124. package/dist/ast-dereferencer.js.map +0 -1
  125. package/src/ast-dereferencer.ts +0 -42
@@ -2,28 +2,58 @@ import { ValidationOptions } from '../..';
2
2
 
3
3
  import { getBuildInfoFiles } from './build-info-file';
4
4
  import { getContractReports } from './contract-report';
5
+ import { findContract } from './find-contract';
5
6
  import { ProjectReport, getProjectReport } from './project-report';
6
- import { validateBuildInfoContracts } from './validations';
7
+ import { SourceContract, validateBuildInfoContracts } from './validations';
7
8
 
8
9
  /**
9
10
  * Validation options for upgrade safety checks.
10
11
  */
11
12
  export type ValidateUpgradeSafetyOptions = Omit<ValidationOptions, 'kind'>;
12
13
 
14
+ export type SpecifiedContracts = {
15
+ contract: SourceContract;
16
+ reference?: SourceContract;
17
+ };
18
+
13
19
  /**
14
20
  * Validates the upgrade safety of all contracts in the build info dir's build info files.
15
21
  * Only contracts that are detected as upgradeable will be validated.
16
22
  *
17
23
  * @param buildInfoDir Absolute path of build info directory, or undefined to use the default Hardhat or Foundry build-info dir.
24
+ * @param contract The name or fully qualified name of the contract to validate. If not specified, all upgradeable contracts in the build info directory will be validated.
25
+ * @param reference The name or fully qualified name of the reference contract to use for storage layout comparisons. Can only be used along with `contract`. If not specified, uses the `@custom:oz-upgrades-from` annotation in the contract that is being validated.
18
26
  * @param opts Validation options, or undefined to use the default validation options.
19
27
  * @returns The project report.
20
28
  */
21
29
  export async function validateUpgradeSafety(
22
30
  buildInfoDir?: string,
31
+ contract?: string,
32
+ reference?: string,
23
33
  opts: ValidateUpgradeSafetyOptions = {},
24
34
  ): Promise<ProjectReport> {
25
35
  const buildInfoFiles = await getBuildInfoFiles(buildInfoDir);
26
36
  const sourceContracts = validateBuildInfoContracts(buildInfoFiles);
27
- const contractReports = getContractReports(sourceContracts, opts);
37
+
38
+ const specifiedContracts = findSpecifiedContracts(sourceContracts, contract, reference);
39
+
40
+ const contractReports = getContractReports(sourceContracts, opts, specifiedContracts);
28
41
  return getProjectReport(contractReports);
29
42
  }
43
+
44
+ function findSpecifiedContracts(
45
+ sourceContracts: SourceContract[],
46
+ contractName?: string,
47
+ referenceName?: string,
48
+ ): SpecifiedContracts | undefined {
49
+ if (contractName !== undefined) {
50
+ return {
51
+ contract: findContract(contractName, undefined, sourceContracts),
52
+ reference: referenceName !== undefined ? findContract(referenceName, undefined, sourceContracts) : undefined,
53
+ };
54
+ } else if (referenceName !== undefined) {
55
+ throw new Error(`The reference option can only be specified when the contract option is also specified.`);
56
+ } else {
57
+ return undefined;
58
+ }
59
+ }
@@ -50,8 +50,8 @@ Generated by [AVA](https://avajs.dev).
50
50
 
51
51
  ` ✘ contracts/test/cli/Validate.sol:UpgradesFromUUPS (upgrades from contracts/test/cli/Validate.sol:HasUpgradeTo)␊
52
52
  ␊
53
- contracts/test/cli/Validate.sol:72: Implementation is missing a public \`upgradeTo(address)\` function␊
54
- Inherit UUPSUpgradeable to include this function in your contract␊
53
+ contracts/test/cli/Validate.sol:72: Implementation is missing a public \`upgradeTo(address)\` or \`upgradeToAndCall(address,bytes)\` function␊
54
+ Inherit UUPSUpgradeable to include one or both of these functions in your contract␊
55
55
  @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol␊
56
56
  https://zpl.in/upgrades/error-008`
57
57
 
@@ -99,7 +99,7 @@ test('withDefaults - all', t => {
99
99
  t.is(opts.unsafeSkipStorageCheck, true);
100
100
  t.is(opts.unsafeAllowCustomTypes, true);
101
101
  t.is(opts.unsafeAllowLinkedLibraries, true);
102
- t.true(opts.unsafeAllow.every((kind: string) => errorKinds.includes(kind as typeof errorKinds[number])));
102
+ t.true(opts.unsafeAllow.every((kind: string) => errorKinds.includes(kind as (typeof errorKinds)[number])));
103
103
  });
104
104
 
105
105
  test('withDefaults - invalid unsafeAllow', t => {
@@ -13,6 +13,8 @@ Arguments:
13
13
  <BUILD_INFO_DIR> Optional path to the build info directory which contains JSON files with Solidity compiler input and output. Defaults to 'artifacts/build-info' for Hardhat projects or 'out/build-info' for Foundry projects. If your project uses a custom output directory, you must specify its build info directory here.
14
14
 
15
15
  Options:
16
+ --contract <CONTRACT> The name or fully qualified name of the contract to validate. If not specified, all upgradeable contracts in the build info directory will be validated.
17
+ --reference <REFERENCE_CONTRACT> The name or fully qualified name of the reference contract to use for storage layout comparisons. Can only be used along with the --contract option. If not specified, uses the @custom:oz-upgrades-from annotation in the contract that is being validated.
16
18
  --unsafeAllow "<VALIDATION_ERRORS>" Selectively disable one or more validation errors. Comma-separated list with one or more of the following: ${errorKinds.join(
17
19
  ', ',
18
20
  )}
@@ -24,7 +26,12 @@ export async function main(args: string[]): Promise<void> {
24
26
 
25
27
  if (!help(parsedArgs, extraArgs)) {
26
28
  const functionArgs = getFunctionArgs(parsedArgs, extraArgs);
27
- const result = await validateUpgradeSafety(functionArgs.buildInfoDir, functionArgs.opts);
29
+ const result = await validateUpgradeSafety(
30
+ functionArgs.buildInfoDir,
31
+ functionArgs.contract,
32
+ functionArgs.reference,
33
+ functionArgs.opts,
34
+ );
28
35
  console.log(result.explain());
29
36
  process.exitCode = result.ok ? 0 : 1;
30
37
  }
@@ -39,7 +46,7 @@ function parseArgs(args: string[]) {
39
46
  'unsafeAllowCustomTypes',
40
47
  'unsafeAllowLinkedLibraries',
41
48
  ],
42
- string: ['unsafeAllow'],
49
+ string: ['unsafeAllow', 'contract', 'reference'],
43
50
  alias: { h: 'help' },
44
51
  });
45
52
  const extraArgs = parsedArgs._;
@@ -59,6 +66,8 @@ function help(parsedArgs: minimist.ParsedArgs, extraArgs: string[]): boolean {
59
66
 
60
67
  interface FunctionArgs {
61
68
  buildInfoDir?: string;
69
+ contract?: string;
70
+ reference?: string;
62
71
  opts: Required<ValidateUpgradeSafetyOptions>;
63
72
  }
64
73
 
@@ -76,9 +85,22 @@ export function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: stri
76
85
  throw new Error('The validate command takes only one argument: the build info directory.');
77
86
  } else {
78
87
  const buildInfoDir = extraArgs.length === 1 ? undefined : extraArgs[1];
88
+ const contract = getAndValidateString(parsedArgs, 'contract');
89
+ const reference = getAndValidateString(parsedArgs, 'reference');
90
+ if (reference !== undefined && contract === undefined) {
91
+ throw new Error('The --reference option can only be used along with the --contract option.');
92
+ }
79
93
  const opts = withDefaults(parsedArgs);
80
- return { buildInfoDir, opts };
94
+ return { buildInfoDir, contract, reference, opts };
95
+ }
96
+ }
97
+
98
+ function getAndValidateString(parsedArgs: minimist.ParsedArgs, option: string): string | undefined {
99
+ const value = parsedArgs[option];
100
+ if (value !== undefined && value.trim().length === 0) {
101
+ throw new Error(`Invalid option: --${option} cannot be empty`);
81
102
  }
103
+ return value;
82
104
  }
83
105
 
84
106
  function validateOptions(parsedArgs: minimist.ParsedArgs) {
@@ -93,6 +115,8 @@ function validateOptions(parsedArgs: minimist.ParsedArgs) {
93
115
  'unsafeAllowCustomTypes',
94
116
  'unsafeAllowLinkedLibraries',
95
117
  'unsafeAllow',
118
+ 'contract',
119
+ 'reference',
96
120
  ].includes(key),
97
121
  );
98
122
  if (invalidArgs.length > 0) {
@@ -101,7 +125,7 @@ function validateOptions(parsedArgs: minimist.ParsedArgs) {
101
125
  }
102
126
 
103
127
  function getUnsafeAllowKinds(unsafeAllow: string | undefined): ValidationError['kind'][] {
104
- type errorKindsType = typeof errorKinds[number];
128
+ type errorKindsType = (typeof errorKinds)[number];
105
129
 
106
130
  if (unsafeAllow === undefined) {
107
131
  return [];
package/src/deployment.ts CHANGED
@@ -257,7 +257,11 @@ export async function waitAndValidateDeployment(
257
257
  }
258
258
 
259
259
  export class TransactionMinedTimeout extends UpgradesError {
260
- constructor(readonly deployment: Deployment & RemoteDeploymentId, type?: string, configurableTimeout?: boolean) {
260
+ constructor(
261
+ readonly deployment: Deployment & RemoteDeploymentId,
262
+ type?: string,
263
+ configurableTimeout?: boolean,
264
+ ) {
261
265
  super(
262
266
  `Timed out waiting for ${type ? type + ' ' : ''}contract deployment to address ${deployment.address} with ${
263
267
  deployment.remoteDeploymentId
@@ -1,8 +1,7 @@
1
1
  import test from 'ava';
2
2
 
3
3
  import { promises as fs } from 'fs';
4
- import rimrafAsync from 'rimraf';
5
- import util from 'util';
4
+ import { rimraf } from 'rimraf';
6
5
  import path from 'path';
7
6
  import os from 'os';
8
7
  import sinon from 'sinon';
@@ -12,8 +11,6 @@ import { getVersion } from './version';
12
11
  import { stubProvider } from './stub-provider';
13
12
  import { ImplDeployment } from './manifest';
14
13
 
15
- const rimraf = util.promisify(rimrafAsync);
16
-
17
14
  test.before(async () => {
18
15
  process.chdir(await fs.mkdtemp(path.join(os.tmpdir(), 'upgrades-core-test-')));
19
16
  });
@@ -1,8 +1,7 @@
1
1
  import os from 'os';
2
2
  import test from 'ava';
3
3
  import path from 'path';
4
- import util from 'util';
5
- import rimrafAsync from 'rimraf';
4
+ import { rimraf } from 'rimraf';
6
5
  import { promises as fs } from 'fs';
7
6
  import { compare as compareVersions } from 'compare-versions';
8
7
  import {
@@ -13,7 +12,6 @@ import {
13
12
  migrateLegacyProject,
14
13
  } from './migrate-oz-cli-project';
15
14
 
16
- const rimraf = util.promisify(rimrafAsync);
17
15
  const BASE_PATH = 'src/scripts';
18
16
  const OPENZEPPELIN_FOLDER = '.openzeppelin';
19
17
  const PROJECT_FILE = path.join(OPENZEPPELIN_FOLDER, 'project.json');
package/src/standalone.ts CHANGED
@@ -23,7 +23,12 @@ export class UpgradeableContract {
23
23
  readonly errors: ValidationError[];
24
24
  readonly layout: StorageLayout;
25
25
 
26
- constructor(readonly name: string, solcInput: SolcInput, solcOutput: SolcOutput, opts: ValidationOptions = {}) {
26
+ constructor(
27
+ readonly name: string,
28
+ solcInput: SolcInput,
29
+ solcOutput: SolcOutput,
30
+ opts: ValidationOptions = {},
31
+ ) {
27
32
  const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput);
28
33
  const validation = validate(solcOutput, decodeSrc);
29
34
  this.version = getContractVersion(validation, name);
@@ -103,7 +103,10 @@ export class StorageLayoutComparator {
103
103
  stack = new Set<string>();
104
104
  cache = new Map<string, TypeChange | undefined>();
105
105
 
106
- constructor(readonly unsafeAllowCustomTypes = false, readonly unsafeAllowRenames = false) {}
106
+ constructor(
107
+ readonly unsafeAllowCustomTypes = false,
108
+ readonly unsafeAllowRenames = false,
109
+ ) {}
107
110
 
108
111
  compareLayouts(original: StorageItem[], updated: StorageItem[]): LayoutCompatibilityReport {
109
112
  return new LayoutCompatibilityReport(this.layoutLevenshtein(original, updated, { allowAppend: true }));
@@ -6,11 +6,10 @@ import {
6
6
  TypeDescriptions,
7
7
  VariableDeclaration,
8
8
  } from 'solidity-ast';
9
- import { isNodeType, findAll } from 'solidity-ast/utils';
9
+ import { isNodeType, findAll, ASTDereferencer } from 'solidity-ast/utils';
10
10
  import { StorageLayout, TypeItem } from './layout';
11
11
  import { normalizeTypeIdentifier } from '../utils/type-id';
12
12
  import { SrcDecoder } from '../src-decoder';
13
- import { ASTDereferencer } from '../ast-dereferencer';
14
13
  import { mapValues } from '../utils/map-values';
15
14
  import { pick } from '../utils/pick';
16
15
  import { execall } from '../utils/execall';
@@ -1,8 +1,7 @@
1
1
  import _test, { TestFn } from 'ava';
2
2
  import { ContractDefinition } from 'solidity-ast';
3
- import { ASTDereferencer, findAll } from 'solidity-ast/utils';
3
+ import { ASTDereferencer, astDereferencer, findAll } from 'solidity-ast/utils';
4
4
  import { artifacts } from 'hardhat';
5
- import { astDereferencer } from '../ast-dereferencer';
6
5
  import { extractStorageLayout } from './extract';
7
6
  import { StorageLayoutComparator } from './compare';
8
7
  import { StorageLayout, getDetailedLayout } from './layout';
@@ -1,8 +1,7 @@
1
1
  import _test, { TestFn } from 'ava';
2
2
  import { ContractDefinition } from 'solidity-ast';
3
- import { ASTDereferencer, findAll } from 'solidity-ast/utils';
3
+ import { ASTDereferencer, astDereferencer, findAll } from 'solidity-ast/utils';
4
4
  import { artifacts } from 'hardhat';
5
- import { astDereferencer } from '../ast-dereferencer';
6
5
  import { extractStorageLayout } from './extract';
7
6
  import { StorageLayoutComparator } from './compare';
8
7
  import { StorageLayout, getDetailedLayout } from './layout';
@@ -1,8 +1,7 @@
1
1
  import _test, { TestFn } from 'ava';
2
2
  import { ContractDefinition } from 'solidity-ast';
3
- import { ASTDereferencer, findAll } from 'solidity-ast/utils';
3
+ import { ASTDereferencer, astDereferencer, findAll } from 'solidity-ast/utils';
4
4
  import { artifacts } from 'hardhat';
5
- import { astDereferencer } from '../ast-dereferencer';
6
5
  import { extractStorageLayout } from './extract';
7
6
  import { StorageLayoutComparator, stripContractSubstrings } from './compare';
8
7
  import { StorageLayout, getDetailedLayout } from './layout';
@@ -1,10 +1,9 @@
1
1
  import _test, { TestFn } from 'ava';
2
2
  import { ContractDefinition } from 'solidity-ast';
3
- import { findAll } from 'solidity-ast/utils';
3
+ import { findAll, astDereferencer } from 'solidity-ast/utils';
4
4
  import { artifacts } from 'hardhat';
5
5
 
6
6
  import { SolcOutput } from '../solc-api';
7
- import { astDereferencer } from '../ast-dereferencer';
8
7
  import { extractStorageLayout } from '../storage/extract';
9
8
  import { StorageLayoutComparator } from './compare';
10
9
  import { StorageLayout, getDetailedLayout } from './layout';
@@ -1,9 +1,8 @@
1
1
  import _test, { TestFn } from 'ava';
2
- import { findAll } from 'solidity-ast/utils';
2
+ import { findAll, astDereferencer } from 'solidity-ast/utils';
3
3
  import { artifacts } from 'hardhat';
4
4
 
5
5
  import { SolcOutput } from './solc-api';
6
- import { astDereferencer } from './ast-dereferencer';
7
6
  import { getStorageUpgradeErrors } from './storage';
8
7
  import { extractStorageLayout } from './storage/extract';
9
8
  import { BuildInfo } from 'hardhat/types';
@@ -1,9 +1,8 @@
1
1
  import _test, { TestFn } from 'ava';
2
- import { findAll } from 'solidity-ast/utils';
2
+ import { findAll, astDereferencer } from 'solidity-ast/utils';
3
3
  import { artifacts } from 'hardhat';
4
4
 
5
5
  import { SolcOutput } from './solc-api';
6
- import { astDereferencer } from './ast-dereferencer';
7
6
  import { getStorageUpgradeErrors } from './storage';
8
7
  import { extractStorageLayout } from './storage/extract';
9
8
  import { BuildInfo } from 'hardhat/types';
@@ -1,10 +1,9 @@
1
1
  import _test, { TestFn } from 'ava';
2
2
  import { ContractDefinition } from 'solidity-ast';
3
- import { findAll } from 'solidity-ast/utils';
3
+ import { findAll, astDereferencer } from 'solidity-ast/utils';
4
4
  import { artifacts } from 'hardhat';
5
5
 
6
6
  import { SolcOutput } from './solc-api';
7
- import { astDereferencer } from './ast-dereferencer';
8
7
  import { getStorageUpgradeErrors } from './storage';
9
8
  import { StorageLayout } from './storage/layout';
10
9
  import { extractStorageLayout } from './storage/extract';
@@ -1,11 +1,10 @@
1
1
  import test from 'ava';
2
2
  import { artifacts } from 'hardhat';
3
3
  import { FunctionDefinition } from 'solidity-ast';
4
- import { findAll } from 'solidity-ast/utils';
4
+ import { findAll, astDereferencer } from 'solidity-ast/utils';
5
5
 
6
6
  import { getFunctionSignature } from './function';
7
7
  import { SolcOutput } from '../solc-api';
8
- import { astDereferencer } from '../ast-dereferencer';
9
8
 
10
9
  testContract('ContractFunctionSignatures');
11
10
  testContract('LibraryFunctionSignatures');
@@ -1,5 +1,5 @@
1
1
  import type { FunctionDefinition, TypeName, VariableDeclaration } from 'solidity-ast';
2
- import { ASTDereferencer } from '../ast-dereferencer';
2
+ import { ASTDereferencer } from 'solidity-ast/utils';
3
3
  import { assert, assertUnreachable } from './assert';
4
4
 
5
5
  function serializeParameterType(parameter: VariableDeclaration, deref: ASTDereferencer): string {
@@ -3,7 +3,10 @@ import { UpgradesError } from '../error';
3
3
  import { UpgradeableContractErrorReport } from './report';
4
4
 
5
5
  export class ValidationErrors extends UpgradesError {
6
- constructor(contractName: string, readonly errors: ValidationError[]) {
6
+ constructor(
7
+ contractName: string,
8
+ readonly errors: ValidationError[],
9
+ ) {
7
10
  super(`Contract \`${contractName}\` is not upgrade safe`, () =>
8
11
  new UpgradeableContractErrorReport(errors).explain(),
9
12
  );
@@ -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
  };
@@ -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);
@@ -147,7 +148,10 @@ export function getErrors(data: ValidationData, version: Version, opts: Validati
147
148
 
148
149
  const selfAndInheritedMethods = getAllMethods(runValidation, fullContractName);
149
150
 
150
- if (!selfAndInheritedMethods.includes(upgradeToSignature)) {
151
+ if (
152
+ !selfAndInheritedMethods.includes(upgradeToSignature) &&
153
+ !selfAndInheritedMethods.includes(upgradeToAndCallSignature)
154
+ ) {
151
155
  errors.push({
152
156
  src: c.src,
153
157
  kind: 'missing-public-upgradeto',
@@ -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: () => `Implementation is missing a public \`upgradeTo(address)\` function`,
60
+ msg: () =>
61
+ `Implementation is missing a public \`upgradeTo(address)\` or \`upgradeToAndCall(address,bytes)\` function`,
61
62
  hint: () =>
62
- `Inherit UUPSUpgradeable to include this function in your contract\n` +
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
  },
@@ -1,10 +1,9 @@
1
1
  import { Node } from 'solidity-ast/node';
2
- import { isNodeType, findAll, ASTDereferencer } from 'solidity-ast/utils';
2
+ import { isNodeType, findAll, ASTDereferencer, astDereferencer } from 'solidity-ast/utils';
3
3
  import type { ContractDefinition, FunctionDefinition } from 'solidity-ast';
4
4
 
5
5
  import { SolcOutput, SolcBytecode } from '../solc-api';
6
6
  import { SrcDecoder } from '../src-decoder';
7
- import { astDereferencer } from '../ast-dereferencer';
8
7
  import { isNullish } from '../utils/is-nullish';
9
8
  import { getFunctionSignature } from '../utils/function';
10
9
  import { Version, getVersion } from '../version';
@@ -48,7 +47,7 @@ export type ValidationError =
48
47
 
49
48
  interface ValidationErrorBase {
50
49
  src: string;
51
- kind: typeof errorKinds[number];
50
+ kind: (typeof errorKinds)[number];
52
51
  }
53
52
 
54
53
  interface ValidationErrorWithName extends ValidationErrorBase {
@@ -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);
@@ -1,8 +0,0 @@
1
- import type { NodeType, NodeTypeMap } from 'solidity-ast/node';
2
- import type { SolcOutput } from './solc-api';
3
- export interface ASTDereferencer {
4
- <T extends NodeType>(nodeTypes: T[]): (id: number) => NodeTypeMap[T];
5
- <T extends NodeType>(nodeTypes: T[], id: number): NodeTypeMap[T];
6
- }
7
- export declare function astDereferencer(solcOutput: SolcOutput): ASTDereferencer;
8
- //# sourceMappingURL=ast-dereferencer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ast-dereferencer.d.ts","sourceRoot":"","sources":["../src/ast-dereferencer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAQ,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,MAAM,WAAW,eAAe;IAC9B,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;CAClE;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,eAAe,CA0BvE"}
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.astDereferencer = void 0;
4
- const utils_1 = require("solidity-ast/utils");
5
- const curry_1 = require("./utils/curry");
6
- function astDereferencer(solcOutput) {
7
- const asts = Array.from(Object.values(solcOutput.sources), s => s.ast);
8
- const cache = new Map();
9
- function deref(nodeTypes, id) {
10
- const cached = cache.get(id);
11
- if (cached) {
12
- if (nodeTypes.includes(cached.nodeType)) {
13
- return cached;
14
- }
15
- }
16
- for (const ast of asts) {
17
- for (const node of (0, utils_1.findAll)(nodeTypes, ast)) {
18
- if (node.id === id) {
19
- cache.set(id, node);
20
- return node;
21
- }
22
- }
23
- }
24
- throw new Error(`No node with id ${id} of type ${nodeTypes}`);
25
- }
26
- return (0, curry_1.curry2)(deref);
27
- }
28
- exports.astDereferencer = astDereferencer;
29
- //# sourceMappingURL=ast-dereferencer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ast-dereferencer.js","sourceRoot":"","sources":["../src/ast-dereferencer.ts"],"names":[],"mappings":";;;AAAA,8CAA6C;AAG7C,yCAAuC;AAYvC,SAAgB,eAAe,CAAC,UAAsB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEtC,SAAS,KAAK,CAAqB,SAAc,EAAE,EAAU;QAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAE7B,IAAI,MAAM,EAAE;YACV,IAAK,SAAwB,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;gBACvD,OAAO,MAAwB,CAAC;aACjC;SACF;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;YACtB,KAAK,MAAM,IAAI,IAAI,IAAA,eAAO,EAAC,SAAS,EAAE,GAAG,CAAC,EAAE;gBAC1C,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE;oBAClB,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;oBACpB,OAAO,IAAI,CAAC;iBACb;aACF;SACF;QAED,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE,YAAY,SAAS,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AA1BD,0CA0BC"}
@@ -1,42 +0,0 @@
1
- import { findAll } from 'solidity-ast/utils';
2
- import type { Node, NodeType, NodeTypeMap } from 'solidity-ast/node';
3
-
4
- import { curry2 } from './utils/curry';
5
- import type { SolcOutput } from './solc-api';
6
-
7
- // An ASTDereferencer is a function that looks up an AST node given its id, in all of the source files involved in a
8
- // solc run. It will generally be used together with the AST property `referencedDeclaration` (found in Identifier,
9
- // UserDefinedTypeName, etc.) to look up a variable definition or type definition.
10
-
11
- export interface ASTDereferencer {
12
- <T extends NodeType>(nodeTypes: T[]): (id: number) => NodeTypeMap[T];
13
- <T extends NodeType>(nodeTypes: T[], id: number): NodeTypeMap[T];
14
- }
15
-
16
- export function astDereferencer(solcOutput: SolcOutput): ASTDereferencer {
17
- const asts = Array.from(Object.values(solcOutput.sources), s => s.ast);
18
- const cache = new Map<number, Node>();
19
-
20
- function deref<T extends NodeType>(nodeTypes: T[], id: number): NodeTypeMap[T] {
21
- const cached = cache.get(id);
22
-
23
- if (cached) {
24
- if ((nodeTypes as NodeType[]).includes(cached.nodeType)) {
25
- return cached as NodeTypeMap[T];
26
- }
27
- }
28
-
29
- for (const ast of asts) {
30
- for (const node of findAll(nodeTypes, ast)) {
31
- if (node.id === id) {
32
- cache.set(id, node);
33
- return node;
34
- }
35
- }
36
- }
37
-
38
- throw new Error(`No node with id ${id} of type ${nodeTypes}`);
39
- }
40
-
41
- return curry2(deref);
42
- }