@openzeppelin/upgrades-core 1.17.0 → 1.19.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.
Files changed (64) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/levenshtein.d.ts +4 -1
  6. package/dist/levenshtein.d.ts.map +1 -1
  7. package/dist/levenshtein.js +1 -1
  8. package/dist/levenshtein.js.map +1 -1
  9. package/dist/provider.d.ts.map +1 -1
  10. package/dist/provider.js +2 -0
  11. package/dist/provider.js.map +1 -1
  12. package/dist/storage/compare.d.ts +21 -3
  13. package/dist/storage/compare.d.ts.map +1 -1
  14. package/dist/storage/compare.js +80 -17
  15. package/dist/storage/compare.js.map +1 -1
  16. package/dist/storage/gap.d.ts +18 -0
  17. package/dist/storage/gap.d.ts.map +1 -0
  18. package/dist/storage/gap.js +29 -0
  19. package/dist/storage/gap.js.map +1 -0
  20. package/dist/storage/report-gap.test.d.ts +2 -0
  21. package/dist/storage/report-gap.test.d.ts.map +1 -0
  22. package/dist/storage/report-gap.test.js +106 -0
  23. package/dist/storage/report-gap.test.js.map +1 -0
  24. package/dist/storage/report-rename-retype.test.js +24 -0
  25. package/dist/storage/report-rename-retype.test.js.map +1 -1
  26. package/dist/storage/report.d.ts +1 -1
  27. package/dist/storage/report.d.ts.map +1 -1
  28. package/dist/storage/report.js +59 -10
  29. package/dist/storage/report.js.map +1 -1
  30. package/dist/storage.test.js +421 -2
  31. package/dist/storage.test.js.map +1 -1
  32. package/dist/usage-error.d.ts +9 -0
  33. package/dist/usage-error.d.ts.map +1 -1
  34. package/dist/usage-error.js +16 -1
  35. package/dist/usage-error.js.map +1 -1
  36. package/dist/validate/data.js +25 -5
  37. package/dist/validate/data.js.map +1 -1
  38. package/dist/validate/index.d.ts +1 -1
  39. package/dist/validate/index.d.ts.map +1 -1
  40. package/dist/validate/index.js.map +1 -1
  41. package/dist/validate/overrides.d.ts +34 -3
  42. package/dist/validate/overrides.d.ts.map +1 -1
  43. package/dist/validate/overrides.js.map +1 -1
  44. package/package.json +4 -4
  45. package/src/index.ts +2 -0
  46. package/src/levenshtein.ts +4 -2
  47. package/src/provider.ts +2 -0
  48. package/src/storage/compare.ts +88 -18
  49. package/src/storage/gap.ts +26 -0
  50. package/src/storage/report-gap.test.ts +119 -0
  51. package/src/storage/report-gap.test.ts.md +59 -0
  52. package/src/storage/report-gap.test.ts.snap +0 -0
  53. package/src/storage/report-rename-retype.test.ts +27 -0
  54. package/src/storage/report-rename-retype.test.ts.md +7 -0
  55. package/src/storage/report-rename-retype.test.ts.snap +0 -0
  56. package/src/storage/report.test.ts.md +1 -4
  57. package/src/storage/report.test.ts.snap +0 -0
  58. package/src/storage/report.ts +83 -12
  59. package/src/storage.test.ts +466 -2
  60. package/src/storage.test.ts.snap +0 -0
  61. package/src/usage-error.ts +22 -0
  62. package/src/validate/data.ts +1 -1
  63. package/src/validate/index.ts +1 -1
  64. package/src/validate/overrides.ts +39 -3
@@ -1,10 +1,30 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
4
24
  };
5
25
  Object.defineProperty(exports, "__esModule", { value: true });
6
26
  exports.concatRunData = exports.isCurrentValidationData = exports.normalizeValidationData = void 0;
7
- const compare_versions_1 = __importDefault(require("compare-versions"));
27
+ const versions = __importStar(require("compare-versions"));
8
28
  const currentMajor = '3';
9
29
  const currentVersion = '3.2';
10
30
  function normalizeValidationData(data) {
@@ -26,12 +46,12 @@ function isCurrentValidationData(data, exact = true) {
26
46
  else if (!('version' in data)) {
27
47
  return false;
28
48
  }
29
- else if (typeof data.version === 'string' && compare_versions_1.default.validate(data.version)) {
49
+ else if (typeof data.version === 'string' && versions.validate(data.version)) {
30
50
  if (exact) {
31
51
  return data.version === currentVersion;
32
52
  }
33
53
  else {
34
- return compare_versions_1.default.compare(data.version, `${currentMajor}.*`, '=');
54
+ return versions.compare(data.version, `${currentMajor}.*`, '=');
35
55
  }
36
56
  }
37
57
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/validate/data.ts"],"names":[],"mappings":";;;;;;AAAA,wEAAwC;AAQxC,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,cAAc,GAAG,KAAK,CAAC;AAW7B,SAAgB,uBAAuB,CAAC,IAAoB;IAC1D,IAAI,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;QACxC,OAAO,IAAI,CAAC;KACb;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC9B,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;KAC/C;SAAM;QACL,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;KACjD;AACH,CAAC;AARD,0DAQC;AAED,SAAgB,uBAAuB,CAAC,IAAoB,EAAE,KAAK,GAAG,IAAI;IACxE,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACvB,OAAO,KAAK,CAAC;KACd;SAAM,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE;QAC/B,OAAO,KAAK,CAAC;KACd;SAAM,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,0BAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;QAC9E,IAAI,KAAK,EAAE;YACT,OAAO,IAAI,CAAC,OAAO,KAAK,cAAc,CAAC;SACxC;aAAM;YACL,OAAO,0BAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,YAAY,IAAI,EAAE,GAAG,CAAC,CAAC;SACjE;KACF;SAAM;QACL,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;KACjE;AACH,CAAC;AAdD,0DAcC;AAED,SAAgB,aAAa,CAC3B,UAA6B,EAC7B,YAAoC;IAEpC,OAAO;QACL,OAAO,EAAE,cAAc;QACvB,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,IAAI,EAAE,CAAC;KAClD,CAAC;AACJ,CAAC;AARD,sCAQC"}
1
+ {"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/validate/data.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2DAA6C;AAQ7C,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,cAAc,GAAG,KAAK,CAAC;AAW7B,SAAgB,uBAAuB,CAAC,IAAoB;IAC1D,IAAI,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;QACxC,OAAO,IAAI,CAAC;KACb;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC9B,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;KAC/C;SAAM;QACL,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;KACjD;AACH,CAAC;AARD,0DAQC;AAED,SAAgB,uBAAuB,CAAC,IAAoB,EAAE,KAAK,GAAG,IAAI;IACxE,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACvB,OAAO,KAAK,CAAC;KACd;SAAM,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE;QAC/B,OAAO,KAAK,CAAC;KACd;SAAM,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;QAC9E,IAAI,KAAK,EAAE;YACT,OAAO,IAAI,CAAC,OAAO,KAAK,cAAc,CAAC;SACxC;aAAM;YACL,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,YAAY,IAAI,EAAE,GAAG,CAAC,CAAC;SACjE;KACF;SAAM;QACL,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;KACjE;AACH,CAAC;AAdD,0DAcC;AAED,SAAgB,aAAa,CAC3B,UAA6B,EAC7B,YAAoC;IAEpC,OAAO;QACL,OAAO,EAAE,cAAc;QACvB,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,IAAI,EAAE,CAAC;KAClD,CAAC;AACJ,CAAC;AARD,sCAQC"}
@@ -1,5 +1,5 @@
1
1
  export { validate, ValidationRunData, ContractValidation } from './run';
2
- export { ValidationOptions, withValidationDefaults } from './overrides';
2
+ export { ProxyKindOption, StandaloneValidationOptions, ValidationOptions, withValidationDefaults } from './overrides';
3
3
  export { ValidationErrors } from './error';
4
4
  export { RunValidation, ValidationLog, Validation, ValidationResult } from './compat';
5
5
  export { ValidationData, ValidationDataCurrent, isCurrentValidationData, concatRunData } from './data';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvG,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,cAAc,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACtH,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvG,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,cAAc,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":";;;AAAA,6BAAwE;AAA/D,+FAAA,QAAQ,OAAA;AACjB,yCAAwE;AAA5C,mHAAA,sBAAsB,OAAA;AAClD,iCAA2C;AAAlC,yGAAA,gBAAgB,OAAA;AAEzB,+BAAuG;AAAvD,+GAAA,uBAAuB,OAAA;AAAE,qGAAA,aAAa,OAAA;AACtF,iCASiB;AARf,2GAAA,kBAAkB,OAAA;AAClB,wHAAA,+BAA+B,OAAA;AAC/B,yGAAA,gBAAgB,OAAA;AAChB,0GAAA,iBAAiB,OAAA;AACjB,4GAAA,mBAAmB,OAAA;AACnB,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AAEhB,mCAA0D;AAAjD,wHAAA,8BAA8B,OAAA;AAEvC,0BAA0B;AAC1B,oCAA+C;AAAtC,sGAAA,eAAe,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":";;;AAAA,6BAAwE;AAA/D,+FAAA,QAAQ,OAAA;AACjB,yCAAsH;AAA5C,mHAAA,sBAAsB,OAAA;AAChG,iCAA2C;AAAlC,yGAAA,gBAAgB,OAAA;AAEzB,+BAAuG;AAAvD,+GAAA,uBAAuB,OAAA;AAAE,qGAAA,aAAa,OAAA;AACtF,iCASiB;AARf,2GAAA,kBAAkB,OAAA;AAClB,wHAAA,+BAA+B,OAAA;AAC/B,yGAAA,gBAAgB,OAAA;AAChB,0GAAA,iBAAiB,OAAA;AACjB,4GAAA,mBAAmB,OAAA;AACnB,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AAEhB,mCAA0D;AAAjD,wHAAA,8BAA8B,OAAA;AAEvC,0BAA0B;AAC1B,oCAA+C;AAAtC,sGAAA,eAAe,OAAA"}
@@ -1,13 +1,44 @@
1
1
  import { ValidationError } from './run';
2
2
  import { ProxyDeployment } from '../manifest';
3
3
  export { silenceWarnings } from '../utils/log';
4
- export interface ValidationOptions {
4
+ /**
5
+ * Option for the kind of proxy that will be used.
6
+ */
7
+ export interface ProxyKindOption {
8
+ /**
9
+ * The kind of proxy to deploy, upgrade or import, or the kind of proxy that the implementation will be used with. Defaults to `"transparent"`.
10
+ */
11
+ kind?: ProxyDeployment['kind'];
12
+ }
13
+ /**
14
+ * Validation options in a standalone context (without storage layout comparisons with a previous implementation).
15
+ */
16
+ export interface StandaloneValidationOptions extends ProxyKindOption {
17
+ /**
18
+ * @deprecated Equivalent to including "struct-definition" and "enum-definition" in unsafeAllow. No longer necessary.
19
+ */
5
20
  unsafeAllowCustomTypes?: boolean;
21
+ /**
22
+ * @deprecated Equivalent to including `"external-library-linking"` in {@link unsafeAllow}.
23
+ */
6
24
  unsafeAllowLinkedLibraries?: boolean;
25
+ /**
26
+ * Selectively disable one or more validation errors.
27
+ */
28
+ unsafeAllow?: ValidationError['kind'][];
29
+ }
30
+ /**
31
+ * Validation options in the context of an upgrade (with storage layout comparisions with a previous implementation).
32
+ */
33
+ export interface ValidationOptions extends StandaloneValidationOptions {
34
+ /**
35
+ * Configure storage layout check to allow variable renaming.
36
+ */
7
37
  unsafeAllowRenames?: boolean;
38
+ /**
39
+ * Upgrades the proxy or beacon without first checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.
40
+ */
8
41
  unsafeSkipStorageCheck?: boolean;
9
- unsafeAllow?: ValidationError['kind'][];
10
- kind?: ProxyDeployment['kind'];
11
42
  }
12
43
  export declare const ValidationErrorUnsafeMessages: Record<ValidationError['kind'], string[]>;
13
44
  export declare function withValidationDefaults(opts: ValidationOptions): Required<ValidationOptions>;
@@ -1 +1 @@
1
- {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../src/validate/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,WAAW,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IACxC,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CA0BnF,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CA2B3F;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,eAAe,EAAE,EACzB,IAAI,EAAE,iBAAiB,GACtB,eAAe,EAAE,CAwBnB"}
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../src/validate/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,eAAe;IAClE;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;OAEG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,2BAA2B;IACpE;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CA0BnF,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CA2B3F;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,eAAe,EAAE,EACzB,IAAI,EAAE,iBAAiB,GACtB,eAAe,EAAE,CAwBnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../../src/validate/overrides.ts"],"names":[],"mappings":";;;AAEA,sCAA0C;AAE1C,0BAA0B;AAC1B,oCAA+C;AAAtC,sGAAA,eAAe,OAAA;AAWX,QAAA,6BAA6B,GAA8C;IACtF,2BAA2B,EAAE;QAC3B,mEAAmE;QACnE,mEAAmE;KACpE;IACD,0BAA0B,EAAE,CAAC,kEAAkE,CAAC;IAChG,0BAA0B,EAAE;QAC1B,gGAAgG;QAChG,iFAAiF;KAClF;IACD,mBAAmB,EAAE;QACnB,8FAA8F;QAC9F,+EAA+E;KAChF;IACD,iBAAiB,EAAE;QACjB,0FAA0F;QAC1F,+EAA+E;KAChF;IACD,WAAW,EAAE,CAAC,qDAAqD,CAAC;IACpE,YAAY,EAAE,CAAC,sDAAsD,CAAC;IACtE,YAAY,EAAE,CAAC,sDAAsD,CAAC;IACtE,0BAA0B,EAAE;QAC1B,kFAAkF;QAClF,yFAAyF;QACzF,gFAAgF;KACjF;CACF,CAAC;AAEF,SAAgB,sBAAsB,CAAC,IAAuB;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;IAC3C,MAAM,sBAAsB,GAC1B,IAAI,CAAC,sBAAsB;QAC3B,CAAC,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACzF,MAAM,0BAA0B,GAC9B,IAAI,CAAC,0BAA0B,IAAI,WAAW,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC;IAEtF,IAAI,sBAAsB,EAAE;QAC1B,WAAW,CAAC,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;KAC1D;IACD,IAAI,0BAA0B,EAAE;QAC9B,WAAW,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;KAC9C;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC;IAExC,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC;IAC5D,MAAM,sBAAsB,GAAG,IAAI,CAAC,sBAAsB,IAAI,KAAK,CAAC;IAEpE,OAAO;QACL,sBAAsB;QACtB,0BAA0B;QAC1B,kBAAkB;QAClB,sBAAsB;QACtB,WAAW;QACX,IAAI;KACL,CAAC;AACJ,CAAC;AA3BD,wDA2BC;AAED,SAAgB,iBAAiB,CAC/B,YAAoB,EACpB,MAAyB,EACzB,IAAuB;IAEvB,MAAM,EAAE,WAAW,EAAE,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAErD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;QACzD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC,CAAC;KAC5E;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,qCAA6B,CAAC,EAAE;QACzF,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAoC,CAAC,EAAE;YAC9D,IAAI,eAAe,GAAG,KAAK,CAAC;YAE5B,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC7B,MAAM,WAAW,GAAG,SAAS,KAAK,KAAK,CAAC,IAAI,CAAC;gBAC7C,eAAe,GAAG,eAAe,IAAI,WAAW,CAAC;gBACjD,OAAO,CAAC,WAAW,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,IAAI,eAAe,IAAI,gBAAgB,EAAE;gBACvC,IAAA,gBAAU,EAAC,oCAAoC,YAAY,EAAE,EAAE,gBAAgB,CAAC,CAAC;aAClF;SACF;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AA5BD,8CA4BC"}
1
+ {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../../src/validate/overrides.ts"],"names":[],"mappings":";;;AAEA,sCAA0C;AAE1C,0BAA0B;AAC1B,oCAA+C;AAAtC,sGAAA,eAAe,OAAA;AA+CX,QAAA,6BAA6B,GAA8C;IACtF,2BAA2B,EAAE;QAC3B,mEAAmE;QACnE,mEAAmE;KACpE;IACD,0BAA0B,EAAE,CAAC,kEAAkE,CAAC;IAChG,0BAA0B,EAAE;QAC1B,gGAAgG;QAChG,iFAAiF;KAClF;IACD,mBAAmB,EAAE;QACnB,8FAA8F;QAC9F,+EAA+E;KAChF;IACD,iBAAiB,EAAE;QACjB,0FAA0F;QAC1F,+EAA+E;KAChF;IACD,WAAW,EAAE,CAAC,qDAAqD,CAAC;IACpE,YAAY,EAAE,CAAC,sDAAsD,CAAC;IACtE,YAAY,EAAE,CAAC,sDAAsD,CAAC;IACtE,0BAA0B,EAAE;QAC1B,kFAAkF;QAClF,yFAAyF;QACzF,gFAAgF;KACjF;CACF,CAAC;AAEF,SAAgB,sBAAsB,CAAC,IAAuB;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;IAC3C,MAAM,sBAAsB,GAC1B,IAAI,CAAC,sBAAsB;QAC3B,CAAC,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACzF,MAAM,0BAA0B,GAC9B,IAAI,CAAC,0BAA0B,IAAI,WAAW,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC;IAEtF,IAAI,sBAAsB,EAAE;QAC1B,WAAW,CAAC,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;KAC1D;IACD,IAAI,0BAA0B,EAAE;QAC9B,WAAW,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;KAC9C;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC;IAExC,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC;IAC5D,MAAM,sBAAsB,GAAG,IAAI,CAAC,sBAAsB,IAAI,KAAK,CAAC;IAEpE,OAAO;QACL,sBAAsB;QACtB,0BAA0B;QAC1B,kBAAkB;QAClB,sBAAsB;QACtB,WAAW;QACX,IAAI;KACL,CAAC;AACJ,CAAC;AA3BD,wDA2BC;AAED,SAAgB,iBAAiB,CAC/B,YAAoB,EACpB,MAAyB,EACzB,IAAuB;IAEvB,MAAM,EAAE,WAAW,EAAE,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAErD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;QACzD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC,CAAC;KAC5E;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,qCAA6B,CAAC,EAAE;QACzF,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAoC,CAAC,EAAE;YAC9D,IAAI,eAAe,GAAG,KAAK,CAAC;YAE5B,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC7B,MAAM,WAAW,GAAG,SAAS,KAAK,KAAK,CAAC,IAAI,CAAC;gBAC7C,eAAe,GAAG,eAAe,IAAI,WAAW,CAAC;gBACjD,OAAO,CAAC,WAAW,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,IAAI,eAAe,IAAI,gBAAgB,EAAE;gBACvC,IAAA,gBAAU,EAAC,oCAAoC,YAAY,EAAE,EAAE,gBAAgB,CAAC,CAAC;aAClF;SACF;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AA5BD,8CA4BC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/upgrades-core",
3
- "version": "1.17.0",
3
+ "version": "1.19.1",
4
4
  "description": "",
5
5
  "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/core",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol",
17
17
  "/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol",
18
18
  "/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol",
19
- "/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json ",
19
+ "/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json",
20
20
  "/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json",
21
21
  "/artifacts/build-info.json"
22
22
  ],
@@ -52,11 +52,11 @@
52
52
  "dependencies": {
53
53
  "cbor": "^8.0.0",
54
54
  "chalk": "^4.1.0",
55
- "compare-versions": "^4.0.0",
55
+ "compare-versions": "^5.0.0",
56
56
  "debug": "^4.1.1",
57
57
  "ethereumjs-util": "^7.0.3",
58
58
  "proper-lockfile": "^4.1.1",
59
59
  "solidity-ast": "^0.4.15"
60
60
  },
61
- "gitHead": "3cf6771e63c6db6212eda75b9ba7a29ca4f2c15a"
61
+ "gitHead": "4046edb571e48c46838a4d9b927fc21254757b3e"
62
62
  }
package/src/index.ts CHANGED
@@ -50,5 +50,7 @@ export {
50
50
  DeployBeaconProxyImplUnknownError,
51
51
  DeployBeaconProxyKindError,
52
52
  ForceImportUnsupportedError,
53
+ NoContractImportError,
54
+ ValidateUpdateRequiresKindError,
53
55
  assertNotProxy,
54
56
  } from './usage-error';
@@ -10,7 +10,9 @@ export type BasicOperation<T> =
10
10
 
11
11
  export type Operation<T, C> = C | BasicOperation<T>;
12
12
 
13
- type GetChangeOp<T, C> = (a: T, b: T) => C | undefined;
13
+ export type Cost = { cost?: number };
14
+
15
+ type GetChangeOp<T, C> = (a: T, b: T) => (C & Cost) | undefined;
14
16
 
15
17
  export function levenshtein<T, C>(a: T[], b: T[], getChangeOp: GetChangeOp<T, C>): Operation<T, C>[] {
16
18
  const matrix = buildMatrix(a, b, getChangeOp);
@@ -76,7 +78,7 @@ function buildMatrix<T, C>(a: T[], b: T[], getChangeOp: GetChangeOp<T, C>): Matr
76
78
  const predCost = predecessor.totalCost;
77
79
  const change = getChangeOp(original, updated);
78
80
  if (change !== undefined) {
79
- return { kind: 'change', totalCost: predCost + CHANGE_COST, predecessor, change };
81
+ return { kind: 'change', totalCost: predCost + (change.cost ?? CHANGE_COST), predecessor, change };
80
82
  } else {
81
83
  return { kind: 'nop', totalCost: predCost, predecessor };
82
84
  }
package/src/provider.ts CHANGED
@@ -104,6 +104,8 @@ export const networkNames: { [chainId in number]?: string } = Object.freeze({
104
104
  42: 'kovan',
105
105
  137: 'polygon',
106
106
  80001: 'polygon-mumbai',
107
+ 43113: 'avalanche-fuji',
108
+ 43114: 'avalanche',
107
109
  });
108
110
 
109
111
  export async function isDevelopmentNetwork(provider: EthereumProvider): Promise<boolean> {
@@ -1,10 +1,11 @@
1
- import { levenshtein, Operation } from '../levenshtein';
1
+ import { Cost, levenshtein, Operation } from '../levenshtein';
2
2
  import { hasLayout, ParsedTypeDetailed, isEnumMembers, isStructMembers } from './layout';
3
3
  import { UpgradesError } from '../error';
4
4
  import { StorageItem as _StorageItem, StructMember as _StructMember, StorageField as _StorageField } from './layout';
5
5
  import { LayoutCompatibilityReport } from './report';
6
6
  import { assert } from '../utils/assert';
7
7
  import { isValueType } from '../utils/is-value-type';
8
+ import { endMatchesGap, isGap } from './gap';
8
9
 
9
10
  export type StorageItem = _StorageItem<ParsedTypeDetailed>;
10
11
  type StructMember = _StructMember<ParsedTypeDetailed>;
@@ -15,13 +16,14 @@ export type StorageOperation<F extends StorageField> = Operation<F, StorageField
15
16
  export type EnumOperation = Operation<string, { kind: 'replace'; original: string; updated: string }>;
16
17
 
17
18
  type StorageFieldChange<F extends StorageField> = (
18
- | { kind: 'replace' | 'rename' }
19
+ | { kind: 'replace' | 'rename' | 'finishgap' }
19
20
  | { kind: 'typechange'; change: TypeChange }
20
21
  | { kind: 'layoutchange'; change: LayoutChange }
22
+ | { kind: 'shrinkgap'; change: TypeChange }
21
23
  ) & {
22
24
  original: F;
23
25
  updated: F;
24
- };
26
+ } & Cost;
25
27
 
26
28
  export type TypeChange = (
27
29
  | {
@@ -55,11 +57,45 @@ export type TypeChange = (
55
57
 
56
58
  export interface LayoutChange {
57
59
  uncertain?: boolean;
60
+ knownCompatible?: boolean;
58
61
  slot?: Record<'from' | 'to', string>;
59
62
  offset?: Record<'from' | 'to', number>;
60
63
  bytes?: Record<'from' | 'to', string>;
61
64
  }
62
65
 
66
+ /**
67
+ * Gets the storage field's begin position as the number of bytes from 0.
68
+ *
69
+ * @param field the storage field
70
+ * @returns the begin position, or undefined if the slot or offset is undefined
71
+ */
72
+ export function storageFieldBegin(field: StorageField): bigint | undefined {
73
+ if (field.slot === undefined || field.offset === undefined) {
74
+ return undefined;
75
+ }
76
+ return BigInt(field.slot) * 32n + BigInt(field.offset);
77
+ }
78
+
79
+ /**
80
+ * Gets the storage field's end position as the number of bytes from 0.
81
+ *
82
+ * @param field the storage field
83
+ * @returns the end position, or undefined if the slot or offset or number of bytes is undefined
84
+ */
85
+ export function storageFieldEnd(field: StorageField): bigint | undefined {
86
+ const begin = storageFieldBegin(field);
87
+ const numberOfBytes = field.type.item.numberOfBytes;
88
+ if (begin === undefined || numberOfBytes === undefined) {
89
+ return undefined;
90
+ }
91
+ return begin + BigInt(numberOfBytes);
92
+ }
93
+
94
+ const LAYOUTCHANGE_COST = 0;
95
+ const FINISHGAP_COST = 1;
96
+ const SHRINKGAP_COST = 0;
97
+ const TYPECHANGE_COST = 0;
98
+
63
99
  export class StorageLayoutComparator {
64
100
  hasAllowedUncheckedCustomTypes = false;
65
101
 
@@ -80,10 +116,39 @@ export class StorageLayoutComparator {
80
116
  ): StorageOperation<F>[] {
81
117
  const ops = levenshtein(original, updated, (a, b) => this.getFieldChange(a, b));
82
118
 
83
- if (allowAppend) {
84
- return ops.filter(o => o.kind !== 'append');
85
- } else {
86
- return ops;
119
+ // filter operations: the callback function returns true if operation is unsafe, false if safe
120
+ return ops.filter(o => {
121
+ if (o.kind === 'insert') {
122
+ const updatedStart = storageFieldBegin(o.updated);
123
+ const updatedEnd = storageFieldEnd(o.updated);
124
+ // If we cannot get the updated position, or if any entry in the original layout (that is not a gap) overlaps, then the insertion is unsafe.
125
+ return (
126
+ updatedStart === undefined ||
127
+ updatedEnd === undefined ||
128
+ original
129
+ .filter(entry => !isGap(entry))
130
+ .some(entry => {
131
+ const origStart = storageFieldBegin(entry);
132
+ const origEnd = storageFieldEnd(entry);
133
+ return (
134
+ origStart === undefined ||
135
+ origEnd === undefined ||
136
+ overlaps(origStart, origEnd, updatedStart, updatedEnd)
137
+ );
138
+ })
139
+ );
140
+ } else if ((o.kind === 'shrinkgap' || o.kind === 'finishgap') && endMatchesGap(o.original, o.updated)) {
141
+ // Gap shrink or gap replacement that finishes on the same slot is safe
142
+ return false;
143
+ } else if (o.kind === 'append' && allowAppend) {
144
+ return false;
145
+ }
146
+ return true;
147
+ });
148
+
149
+ // https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
150
+ function overlaps(startA: bigint, endA: bigint, startB: bigint, endB: bigint) {
151
+ return startA < endB && startB < endA;
87
152
  }
88
153
  }
89
154
 
@@ -107,28 +172,29 @@ export class StorageLayoutComparator {
107
172
  const typeChange = !retypedFromOriginal && this.getTypeChange(original.type, updated.type, { allowAppend: false });
108
173
  const layoutChange = this.getLayoutChange(original, updated);
109
174
 
110
- if (updated.retypedFrom && layoutChange) {
111
- return { kind: 'layoutchange', original, updated, change: layoutChange };
175
+ if (updated.retypedFrom && layoutChange && (!layoutChange.uncertain || !layoutChange.knownCompatible)) {
176
+ return { kind: 'layoutchange', original, updated, change: layoutChange, cost: LAYOUTCHANGE_COST };
177
+ } else if (nameChange && endMatchesGap(original, updated)) {
178
+ // original is a gap that was consumed by a non-gap ending at the same slot, which is safe
179
+ return { kind: 'finishgap', original, updated, cost: FINISHGAP_COST };
180
+ } else if (typeChange && typeChange.kind === 'array shrink' && endMatchesGap(original, updated)) {
181
+ // original and updated are a gap that was shrunk and ends at the same slot, which is safe
182
+ return { kind: 'shrinkgap', change: typeChange, original, updated, cost: SHRINKGAP_COST };
112
183
  } else if (typeChange && nameChange) {
113
184
  return { kind: 'replace', original, updated };
114
185
  } else if (nameChange) {
115
186
  return { kind: 'rename', original, updated };
116
187
  } else if (typeChange) {
117
- return { kind: 'typechange', change: typeChange, original, updated };
188
+ return { kind: 'typechange', change: typeChange, original, updated, cost: TYPECHANGE_COST };
118
189
  } else if (layoutChange && !layoutChange.uncertain) {
119
190
  // Any layout change should be caught earlier as a type change, but we
120
191
  // add this check as a safety fallback.
121
- return { kind: 'layoutchange', original, updated, change: layoutChange };
192
+ return { kind: 'layoutchange', original, updated, change: layoutChange, cost: LAYOUTCHANGE_COST };
122
193
  }
123
194
  }
124
195
 
125
196
  getLayoutChange(original: StorageField, updated: StorageField): LayoutChange | undefined {
126
- const validPair = ['uint8', 'bool'];
127
- const knownCompatibleTypes =
128
- validPair.includes(original.type.item.label) && validPair.includes(updated.type.item.label);
129
- if (original.type.item.label == updated.type.item.label || knownCompatibleTypes) {
130
- return undefined;
131
- } else if (hasLayout(original) && hasLayout(updated)) {
197
+ if (hasLayout(original) && hasLayout(updated)) {
132
198
  const change = <T>(from: T, to: T) => (from === to ? undefined : { from, to });
133
199
  const slot = change(original.slot, updated.slot);
134
200
  const offset = change(original.offset, updated.offset);
@@ -137,7 +203,11 @@ export class StorageLayoutComparator {
137
203
  return { slot, offset, bytes };
138
204
  }
139
205
  } else {
140
- return { uncertain: true };
206
+ const validChanges = [['uint8', 'bool']];
207
+ const knownCompatible = validChanges.some(
208
+ group => group.includes(original.type.item.label) && group.includes(updated.type.item.label),
209
+ );
210
+ return { uncertain: true, knownCompatible };
141
211
  }
142
212
  }
143
213
 
@@ -0,0 +1,26 @@
1
+ import { StorageField, storageFieldEnd } from './compare';
2
+
3
+ /**
4
+ * Returns true if the field represents a storage gap.
5
+ *
6
+ * @param field the storage field
7
+ * @returns true if field is a gap, otherwise false
8
+ */
9
+ export function isGap(field: StorageField): boolean {
10
+ return field.label === '__gap' && field.type.head === 't_array';
11
+ }
12
+
13
+ /**
14
+ * Returns true if original storage field is a gap and the updated storage field
15
+ * ends at the exact same position as the gap.
16
+ *
17
+ * @param original the original storage field
18
+ * @param updated the updated storage field
19
+ * @returns true if original is a gap and original and updated end at the same position, otherwise false
20
+ */
21
+ export function endMatchesGap(original: StorageField, updated: StorageField) {
22
+ const originalEnd = storageFieldEnd(original);
23
+ const updatedEnd = storageFieldEnd(updated);
24
+
25
+ return isGap(original) && originalEnd !== undefined && updatedEnd !== undefined && originalEnd === updatedEnd;
26
+ }
@@ -0,0 +1,119 @@
1
+ import _test, { TestFn } from 'ava';
2
+ import { ContractDefinition } from 'solidity-ast';
3
+ import { ASTDereferencer, findAll } from 'solidity-ast/utils';
4
+ import { artifacts } from 'hardhat';
5
+ import { astDereferencer } from '../ast-dereferencer';
6
+ import { extractStorageLayout } from './extract';
7
+ import { StorageLayoutComparator } from './compare';
8
+ import { StorageLayout, getDetailedLayout } from './layout';
9
+
10
+ interface Context {
11
+ extractStorageLayout: (contract: string) => ReturnType<typeof extractStorageLayout>;
12
+ }
13
+
14
+ const test = _test as TestFn<Context>;
15
+
16
+ const dummyDecodeSrc = () => 'file.sol:1';
17
+ const testContracts = [
18
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V1',
19
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Ok',
20
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Bad1',
21
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Bad2',
22
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Bad3',
23
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Bad4',
24
+ 'contracts/test/Storage.sol:StorageUpgrade_Gap_V2_Bad5',
25
+ 'contracts/test/Storage.sol:StorageUpgrade_MultiConsumeGap_V1',
26
+ 'contracts/test/Storage.sol:StorageUpgrade_MultiConsumeGap_V2_Ok',
27
+ ];
28
+
29
+ test.before(async t => {
30
+ const contracts: Record<string, ContractDefinition> = {};
31
+ const deref: Record<string, ASTDereferencer> = {};
32
+ const storageLayout: Record<string, StorageLayout> = {};
33
+ for (const contract of testContracts) {
34
+ const buildInfo = await artifacts.getBuildInfo(contract);
35
+ if (buildInfo === undefined) {
36
+ throw new Error(`Build info not found for contract ${contract}`);
37
+ }
38
+ const solcOutput = buildInfo.output;
39
+ for (const def of findAll('ContractDefinition', solcOutput.sources['contracts/test/Storage.sol'].ast)) {
40
+ contracts[def.name] = def;
41
+ deref[def.name] = astDereferencer(solcOutput);
42
+ storageLayout[def.name] = (solcOutput.contracts['contracts/test/Storage.sol'][def.name] as any).storageLayout;
43
+ }
44
+ }
45
+
46
+ t.context.extractStorageLayout = name =>
47
+ extractStorageLayout(contracts[name], dummyDecodeSrc, deref[name], storageLayout[name]);
48
+ });
49
+
50
+ function getReport(original: StorageLayout, updated: StorageLayout) {
51
+ const originalDetailed = getDetailedLayout(original);
52
+ const updatedDetailed = getDetailedLayout(updated);
53
+ const comparator = new StorageLayoutComparator();
54
+ return comparator.compareLayouts(originalDetailed, updatedDetailed);
55
+ }
56
+
57
+ test('shrinkgap', t => {
58
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
59
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Ok');
60
+ const report = getReport(v1, v2);
61
+ t.true(report.ok);
62
+ t.is(report.explain(), '');
63
+ });
64
+
65
+ test('finishgap', t => {
66
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_MultiConsumeGap_V1');
67
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_MultiConsumeGap_V2_Ok');
68
+ const report = getReport(v1, v2);
69
+ t.true(report.ok);
70
+ t.is(report.explain(), '');
71
+ });
72
+
73
+ test('insert var without shrink gap', t => {
74
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
75
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad1');
76
+ const report = getReport(v1, v2);
77
+ t.false(report.ok);
78
+ t.snapshot(report.explain());
79
+ });
80
+
81
+ test('delete var and expand gap', t => {
82
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
83
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad2');
84
+ const report = getReport(v1, v2);
85
+ t.false(report.ok);
86
+ t.snapshot(report.explain());
87
+ });
88
+
89
+ test('shrink gap without adding var', t => {
90
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
91
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad3');
92
+ const report = getReport(v1, v2);
93
+ t.false(report.ok);
94
+ t.snapshot(report.explain());
95
+ });
96
+
97
+ test('insert var and shrink gap too much', t => {
98
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
99
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad4');
100
+ const report = getReport(v1, v2);
101
+ t.false(report.ok);
102
+ t.snapshot(report.explain());
103
+ });
104
+
105
+ test('insert vars and shrink gap not enough', t => {
106
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
107
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad5');
108
+ const report = getReport(v1, v2);
109
+ t.false(report.ok);
110
+ t.snapshot(report.explain());
111
+ });
112
+
113
+ test('insert vars without shrink gap (uint128)', t => {
114
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V1');
115
+ const v2 = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V2_Bad');
116
+ const report = getReport(v1, v2);
117
+ t.false(report.ok);
118
+ t.snapshot(report.explain());
119
+ });
@@ -0,0 +1,59 @@
1
+ # Snapshot report for `src/storage/report-gap.test.ts`
2
+
3
+ The actual snapshot is saved in `report-gap.test.ts.snap`.
4
+
5
+ Generated by [AVA](https://avajs.dev).
6
+
7
+ ## insert var without shrink gap
8
+
9
+ > Snapshot 1
10
+
11
+ `file.sol:1: Layout changed for \`__gap\` (uint256[48] -> uint256[48])␊
12
+ - Slot changed from 2 to 3␊
13
+ > Set __gap array to size 47`
14
+
15
+ ## delete var and expand gap
16
+
17
+ > Snapshot 1
18
+
19
+ `StorageUpgrade_Gap_V1: Deleted \`b\`␊
20
+ > Keep the variable even if unused␊
21
+ ␊
22
+ file.sol:1: Upgraded \`__gap\` to an incompatible type␊
23
+ - Bad storage gap resize from 48 to 49␊
24
+ Size cannot increase`
25
+
26
+ ## shrink gap without adding var
27
+
28
+ > Snapshot 1
29
+
30
+ `file.sol:1: Upgraded \`__gap\` to an incompatible type␊
31
+ - Bad storage gap resize from 48 to 47␊
32
+ Size decrease must match with corresponding variable inserts␊
33
+ > Set __gap array to size 48`
34
+
35
+ ## insert var and shrink gap too much
36
+
37
+ > Snapshot 1
38
+
39
+ `file.sol:1: Upgraded \`__gap\` to an incompatible type␊
40
+ - Bad storage gap resize from 48 to 46␊
41
+ Size decrease must match with corresponding variable inserts␊
42
+ > Set __gap array to size 47`
43
+
44
+ ## insert vars and shrink gap not enough
45
+
46
+ > Snapshot 1
47
+
48
+ `file.sol:1: Upgraded \`__gap\` to an incompatible type␊
49
+ - Bad storage gap resize from 48 to 47␊
50
+ Size decrease must match with corresponding variable inserts␊
51
+ > Set __gap array to size 46`
52
+
53
+ ## insert vars without shrink gap (uint128)
54
+
55
+ > Snapshot 1
56
+
57
+ `file.sol:1: Layout changed for \`__gap\` (uint128[49] -> uint128[49])␊
58
+ - Slot changed from 1 to 2␊
59
+ > Set __gap array to size 48`
@@ -6,6 +6,7 @@ import { astDereferencer } from '../ast-dereferencer';
6
6
  import { extractStorageLayout } from './extract';
7
7
  import { StorageLayoutComparator } from './compare';
8
8
  import { StorageLayout, getDetailedLayout } from './layout';
9
+ import { getStorageUpgradeErrors } from '.';
9
10
 
10
11
  interface Context {
11
12
  extractStorageLayout: (contract: string) => ReturnType<typeof extractStorageLayout>;
@@ -25,6 +26,8 @@ const testContracts = [
25
26
  'contracts/test/RenamedRetyped.sol:ConfusingRetypeV2',
26
27
  'contracts/test/RenamedRetyped.sol:NonHardcodedRetypeV1',
27
28
  'contracts/test/RenamedRetyped.sol:NonHardcodedRetypeV2',
29
+ 'contracts/test/RenamedRetyped.sol:LayoutChangeV1',
30
+ 'contracts/test/RenamedRetyped.sol:LayoutChangeV2',
28
31
  ];
29
32
 
30
33
  test.before(async t => {
@@ -104,3 +107,27 @@ test('non-hardcoded retype', t => {
104
107
  t.true(report.ok);
105
108
  t.snapshot(report.explain());
106
109
  });
110
+
111
+ test('retype with layout change', t => {
112
+ const v1 = t.context.extractStorageLayout('LayoutChangeV1');
113
+ const v2 = t.context.extractStorageLayout('LayoutChangeV2');
114
+
115
+ // ensure both variables' layout changes appear in the internal errors
116
+ t.like(getStorageUpgradeErrors(v1, v2), {
117
+ length: 2,
118
+ 0: {
119
+ kind: 'layoutchange',
120
+ original: { label: 'a' },
121
+ updated: { label: 'a' },
122
+ },
123
+ 1: {
124
+ kind: 'layoutchange',
125
+ original: { label: 'b' },
126
+ updated: { label: 'b' },
127
+ },
128
+ });
129
+
130
+ const report = getReport(v1, v2);
131
+ t.false(report.ok);
132
+ t.snapshot(report.explain());
133
+ });
@@ -42,3 +42,10 @@ Generated by [AVA](https://avajs.dev).
42
42
  > Snapshot 1
43
43
 
44
44
  ''
45
+
46
+ ## retype with layout change
47
+
48
+ > Snapshot 1
49
+
50
+ `file.sol:1: Layout changed for \`a\` (bool -> uint16)␊
51
+ - Number of bytes changed from 1 to 2`