@openzeppelin/upgrades-core 1.9.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 (336) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +4 -0
  3. package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +80 -0
  4. package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +160 -0
  5. package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +155 -0
  6. package/artifacts/AdminUpgradeabilityProxy.json +155 -0
  7. package/artifacts/ProxyAdmin.json +160 -0
  8. package/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json +155 -0
  9. package/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json +160 -0
  10. package/contracts/Initializable.sol +64 -0
  11. package/dist/ast-dereferencer.d.ts +8 -0
  12. package/dist/ast-dereferencer.d.ts.map +1 -0
  13. package/dist/ast-dereferencer.js +29 -0
  14. package/dist/ast-dereferencer.js.map +1 -0
  15. package/dist/deployment.d.ts +18 -0
  16. package/dist/deployment.d.ts.map +1 -0
  17. package/dist/deployment.js +110 -0
  18. package/dist/deployment.js.map +1 -0
  19. package/dist/deployment.test.d.ts +2 -0
  20. package/dist/deployment.test.d.ts.map +1 -0
  21. package/dist/deployment.test.js +92 -0
  22. package/dist/deployment.test.js.map +1 -0
  23. package/dist/eip-1967.d.ts +7 -0
  24. package/dist/eip-1967.d.ts.map +1 -0
  25. package/dist/eip-1967.js +63 -0
  26. package/dist/eip-1967.js.map +1 -0
  27. package/dist/error.d.ts +21 -0
  28. package/dist/error.d.ts.map +1 -0
  29. package/dist/error.js +21 -0
  30. package/dist/error.js.map +1 -0
  31. package/dist/impl-store.d.ts +7 -0
  32. package/dist/impl-store.d.ts.map +1 -0
  33. package/dist/impl-store.js +94 -0
  34. package/dist/impl-store.js.map +1 -0
  35. package/dist/impl-store.test.d.ts +2 -0
  36. package/dist/impl-store.test.d.ts.map +1 -0
  37. package/dist/impl-store.test.js +55 -0
  38. package/dist/impl-store.test.js.map +1 -0
  39. package/dist/index.d.ts +16 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +32 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/levenshtein.d.ts +12 -0
  44. package/dist/levenshtein.d.ts.map +1 -0
  45. package/dist/levenshtein.js +89 -0
  46. package/dist/levenshtein.js.map +1 -0
  47. package/dist/levenshtein.test.d.ts +2 -0
  48. package/dist/levenshtein.test.d.ts.map +1 -0
  49. package/dist/levenshtein.test.js +88 -0
  50. package/dist/levenshtein.test.js.map +1 -0
  51. package/dist/link-refs.d.ts +11 -0
  52. package/dist/link-refs.d.ts.map +1 -0
  53. package/dist/link-refs.js +36 -0
  54. package/dist/link-refs.js.map +1 -0
  55. package/dist/link-refs.test.d.ts +2 -0
  56. package/dist/link-refs.test.d.ts.map +1 -0
  57. package/dist/link-refs.test.js +76 -0
  58. package/dist/link-refs.test.js.map +1 -0
  59. package/dist/manifest-storage-layout.d.ts +6 -0
  60. package/dist/manifest-storage-layout.d.ts.map +1 -0
  61. package/dist/manifest-storage-layout.js +95 -0
  62. package/dist/manifest-storage-layout.js.map +1 -0
  63. package/dist/manifest-storage-layout.test.d.ts +2 -0
  64. package/dist/manifest-storage-layout.test.d.ts.map +1 -0
  65. package/dist/manifest-storage-layout.test.js +105 -0
  66. package/dist/manifest-storage-layout.test.js.map +1 -0
  67. package/dist/manifest.d.ts +36 -0
  68. package/dist/manifest.d.ts.map +1 -0
  69. package/dist/manifest.js +154 -0
  70. package/dist/manifest.js.map +1 -0
  71. package/dist/manifest.test.d.ts +2 -0
  72. package/dist/manifest.test.d.ts.map +1 -0
  73. package/dist/manifest.test.js +17 -0
  74. package/dist/manifest.test.js.map +1 -0
  75. package/dist/provider.d.ts +37 -0
  76. package/dist/provider.d.ts.map +1 -0
  77. package/dist/provider.js +74 -0
  78. package/dist/provider.js.map +1 -0
  79. package/dist/provider.test.d.ts +2 -0
  80. package/dist/provider.test.d.ts.map +1 -0
  81. package/dist/provider.test.js +41 -0
  82. package/dist/provider.test.js.map +1 -0
  83. package/dist/scripts/migrate-oz-cli-project.d.ts +108 -0
  84. package/dist/scripts/migrate-oz-cli-project.d.ts.map +1 -0
  85. package/dist/scripts/migrate-oz-cli-project.js +321 -0
  86. package/dist/scripts/migrate-oz-cli-project.js.map +1 -0
  87. package/dist/scripts/migrate-oz-cli-project.test.d.ts +2 -0
  88. package/dist/scripts/migrate-oz-cli-project.test.d.ts.map +1 -0
  89. package/dist/scripts/migrate-oz-cli-project.test.js +60 -0
  90. package/dist/scripts/migrate-oz-cli-project.test.js.map +1 -0
  91. package/dist/set-proxy-kind.d.ts +5 -0
  92. package/dist/set-proxy-kind.d.ts.map +1 -0
  93. package/dist/set-proxy-kind.js +25 -0
  94. package/dist/set-proxy-kind.js.map +1 -0
  95. package/dist/solc-api.d.ts +39 -0
  96. package/dist/solc-api.d.ts.map +1 -0
  97. package/dist/solc-api.js +3 -0
  98. package/dist/solc-api.js.map +1 -0
  99. package/dist/src-decoder.d.ts +6 -0
  100. package/dist/src-decoder.d.ts.map +1 -0
  101. package/dist/src-decoder.js +36 -0
  102. package/dist/src-decoder.js.map +1 -0
  103. package/dist/src-decoder.test.d.ts +2 -0
  104. package/dist/src-decoder.test.d.ts.map +1 -0
  105. package/dist/src-decoder.test.js +29 -0
  106. package/dist/src-decoder.test.js.map +1 -0
  107. package/dist/storage/compare.d.ts +55 -0
  108. package/dist/storage/compare.d.ts.map +1 -0
  109. package/dist/storage/compare.js +181 -0
  110. package/dist/storage/compare.js.map +1 -0
  111. package/dist/storage/compat.d.ts +4 -0
  112. package/dist/storage/compat.d.ts.map +1 -0
  113. package/dist/storage/compat.js +9 -0
  114. package/dist/storage/compat.js.map +1 -0
  115. package/dist/storage/extract.d.ts +7 -0
  116. package/dist/storage/extract.d.ts.map +1 -0
  117. package/dist/storage/extract.js +86 -0
  118. package/dist/storage/extract.js.map +1 -0
  119. package/dist/storage/index.d.ts +8 -0
  120. package/dist/storage/index.d.ts.map +1 -0
  121. package/dist/storage/index.js +65 -0
  122. package/dist/storage/index.js.map +1 -0
  123. package/dist/storage/layout.d.ts +31 -0
  124. package/dist/storage/layout.d.ts.map +1 -0
  125. package/dist/storage/layout.js +46 -0
  126. package/dist/storage/layout.js.map +1 -0
  127. package/dist/storage/report.d.ts +8 -0
  128. package/dist/storage/report.d.ts.map +1 -0
  129. package/dist/storage/report.js +175 -0
  130. package/dist/storage/report.js.map +1 -0
  131. package/dist/storage/report.test.d.ts +2 -0
  132. package/dist/storage/report.test.d.ts.map +1 -0
  133. package/dist/storage/report.test.js +82 -0
  134. package/dist/storage/report.test.js.map +1 -0
  135. package/dist/storage.test.d.ts +2 -0
  136. package/dist/storage.test.d.ts.map +1 -0
  137. package/dist/storage.test.js +436 -0
  138. package/dist/storage.test.js.map +1 -0
  139. package/dist/stub-provider.d.ts +14 -0
  140. package/dist/stub-provider.d.ts.map +1 -0
  141. package/dist/stub-provider.js +128 -0
  142. package/dist/stub-provider.js.map +1 -0
  143. package/dist/utils/assert.d.ts +3 -0
  144. package/dist/utils/assert.d.ts.map +1 -0
  145. package/dist/utils/assert.js +14 -0
  146. package/dist/utils/assert.js.map +1 -0
  147. package/dist/utils/curry.d.ts +6 -0
  148. package/dist/utils/curry.d.ts.map +1 -0
  149. package/dist/utils/curry.js +16 -0
  150. package/dist/utils/curry.js.map +1 -0
  151. package/dist/utils/debug.d.ts +4 -0
  152. package/dist/utils/debug.d.ts.map +1 -0
  153. package/dist/utils/debug.js +8 -0
  154. package/dist/utils/debug.js.map +1 -0
  155. package/dist/utils/deep-array.d.ts +3 -0
  156. package/dist/utils/deep-array.d.ts.map +1 -0
  157. package/dist/utils/deep-array.js +13 -0
  158. package/dist/utils/deep-array.js.map +1 -0
  159. package/dist/utils/deep-array.test.d.ts +2 -0
  160. package/dist/utils/deep-array.test.d.ts.map +1 -0
  161. package/dist/utils/deep-array.test.js +23 -0
  162. package/dist/utils/deep-array.test.js.map +1 -0
  163. package/dist/utils/function.d.ts +4 -0
  164. package/dist/utils/function.d.ts.map +1 -0
  165. package/dist/utils/function.js +43 -0
  166. package/dist/utils/function.js.map +1 -0
  167. package/dist/utils/function.test.d.ts +2 -0
  168. package/dist/utils/function.test.d.ts.map +1 -0
  169. package/dist/utils/function.test.js +34 -0
  170. package/dist/utils/function.test.js.map +1 -0
  171. package/dist/utils/indent.d.ts +2 -0
  172. package/dist/utils/indent.d.ts.map +1 -0
  173. package/dist/utils/indent.js +8 -0
  174. package/dist/utils/indent.js.map +1 -0
  175. package/dist/utils/is-nullish.d.ts +2 -0
  176. package/dist/utils/is-nullish.d.ts.map +1 -0
  177. package/dist/utils/is-nullish.js +8 -0
  178. package/dist/utils/is-nullish.js.map +1 -0
  179. package/dist/utils/is-nullish.test.d.ts +2 -0
  180. package/dist/utils/is-nullish.test.d.ts.map +1 -0
  181. package/dist/utils/is-nullish.test.js +17 -0
  182. package/dist/utils/is-nullish.test.js.map +1 -0
  183. package/dist/utils/is-value-type.d.ts +3 -0
  184. package/dist/utils/is-value-type.d.ts.map +1 -0
  185. package/dist/utils/is-value-type.js +8 -0
  186. package/dist/utils/is-value-type.js.map +1 -0
  187. package/dist/utils/itemize.d.ts +3 -0
  188. package/dist/utils/itemize.d.ts.map +1 -0
  189. package/dist/utils/itemize.js +18 -0
  190. package/dist/utils/itemize.js.map +1 -0
  191. package/dist/utils/log.d.ts +4 -0
  192. package/dist/utils/log.d.ts.map +1 -0
  193. package/dist/utils/log.js +35 -0
  194. package/dist/utils/log.js.map +1 -0
  195. package/dist/utils/make-non-enumerable.d.ts +2 -0
  196. package/dist/utils/make-non-enumerable.d.ts.map +1 -0
  197. package/dist/utils/make-non-enumerable.js +9 -0
  198. package/dist/utils/make-non-enumerable.js.map +1 -0
  199. package/dist/utils/map-values.d.ts +2 -0
  200. package/dist/utils/map-values.d.ts.map +1 -0
  201. package/dist/utils/map-values.js +12 -0
  202. package/dist/utils/map-values.js.map +1 -0
  203. package/dist/utils/parse-type-id.d.ts +9 -0
  204. package/dist/utils/parse-type-id.d.ts.map +1 -0
  205. package/dist/utils/parse-type-id.js +92 -0
  206. package/dist/utils/parse-type-id.js.map +1 -0
  207. package/dist/utils/parse-type-id.test.d.ts +2 -0
  208. package/dist/utils/parse-type-id.test.d.ts.map +1 -0
  209. package/dist/utils/parse-type-id.test.js +25 -0
  210. package/dist/utils/parse-type-id.test.js.map +1 -0
  211. package/dist/utils/pick.d.ts +2 -0
  212. package/dist/utils/pick.d.ts.map +1 -0
  213. package/dist/utils/pick.js +12 -0
  214. package/dist/utils/pick.js.map +1 -0
  215. package/dist/utils/type-id.d.ts +4 -0
  216. package/dist/utils/type-id.d.ts.map +1 -0
  217. package/dist/utils/type-id.js +65 -0
  218. package/dist/utils/type-id.js.map +1 -0
  219. package/dist/validate/compat.d.ts +6 -0
  220. package/dist/validate/compat.d.ts.map +1 -0
  221. package/dist/validate/compat.js +4 -0
  222. package/dist/validate/compat.js.map +1 -0
  223. package/dist/validate/data.d.ts +14 -0
  224. package/dist/validate/data.d.ts.map +1 -0
  225. package/dist/validate/data.js +50 -0
  226. package/dist/validate/data.js.map +1 -0
  227. package/dist/validate/error.d.ts +7 -0
  228. package/dist/validate/error.d.ts.map +1 -0
  229. package/dist/validate/error.js +78 -0
  230. package/dist/validate/error.js.map +1 -0
  231. package/dist/validate/index.d.ts +8 -0
  232. package/dist/validate/index.d.ts.map +1 -0
  233. package/dist/validate/index.js +24 -0
  234. package/dist/validate/index.js.map +1 -0
  235. package/dist/validate/overrides.d.ts +14 -0
  236. package/dist/validate/overrides.d.ts.map +1 -0
  237. package/dist/validate/overrides.js +72 -0
  238. package/dist/validate/overrides.js.map +1 -0
  239. package/dist/validate/query.d.ts +15 -0
  240. package/dist/validate/query.d.ts.map +1 -0
  241. package/dist/validate/query.js +111 -0
  242. package/dist/validate/query.js.map +1 -0
  243. package/dist/validate/query.test.d.ts +2 -0
  244. package/dist/validate/query.test.d.ts.map +1 -0
  245. package/dist/validate/query.test.js +48 -0
  246. package/dist/validate/query.test.js.map +1 -0
  247. package/dist/validate/run.d.ts +39 -0
  248. package/dist/validate/run.d.ts.map +1 -0
  249. package/dist/validate/run.js +199 -0
  250. package/dist/validate/run.js.map +1 -0
  251. package/dist/validate.test.d.ts +2 -0
  252. package/dist/validate.test.d.ts.map +1 -0
  253. package/dist/validate.test.js +116 -0
  254. package/dist/validate.test.js.map +1 -0
  255. package/dist/version.d.ts +9 -0
  256. package/dist/version.d.ts.map +1 -0
  257. package/dist/version.js +66 -0
  258. package/dist/version.js.map +1 -0
  259. package/dist/version.test.d.ts +2 -0
  260. package/dist/version.test.d.ts.map +1 -0
  261. package/dist/version.test.js +53 -0
  262. package/dist/version.test.js.map +1 -0
  263. package/package.json +58 -0
  264. package/src/ast-dereferencer.ts +42 -0
  265. package/src/deployment.test.ts +98 -0
  266. package/src/deployment.ts +122 -0
  267. package/src/eip-1967.ts +77 -0
  268. package/src/error.ts +26 -0
  269. package/src/impl-store.test.ts +59 -0
  270. package/src/impl-store.ts +124 -0
  271. package/src/index.ts +18 -0
  272. package/src/levenshtein.test.ts +91 -0
  273. package/src/levenshtein.ts +116 -0
  274. package/src/link-refs.test.ts +77 -0
  275. package/src/link-refs.ts +41 -0
  276. package/src/manifest-storage-layout.test.ts +118 -0
  277. package/src/manifest-storage-layout.ts +107 -0
  278. package/src/manifest.test.ts +13 -0
  279. package/src/manifest.ts +178 -0
  280. package/src/provider.test.ts +43 -0
  281. package/src/provider.ts +103 -0
  282. package/src/scripts/migratable-manifest.test.json +175 -0
  283. package/src/scripts/migrate-oz-cli-project.test.ts +70 -0
  284. package/src/scripts/migrate-oz-cli-project.test.ts.md +146 -0
  285. package/src/scripts/migrate-oz-cli-project.test.ts.snap +0 -0
  286. package/src/scripts/migrate-oz-cli-project.ts +484 -0
  287. package/src/scripts/project-file.test.json +25 -0
  288. package/src/set-proxy-kind.ts +27 -0
  289. package/src/solc-api.ts +42 -0
  290. package/src/src-decoder.test.ts +30 -0
  291. package/src/src-decoder.ts +37 -0
  292. package/src/storage/compare.ts +249 -0
  293. package/src/storage/compat.ts +3 -0
  294. package/src/storage/extract.ts +110 -0
  295. package/src/storage/index.ts +73 -0
  296. package/src/storage/layout.ts +88 -0
  297. package/src/storage/report.test.ts +95 -0
  298. package/src/storage/report.test.ts.md +154 -0
  299. package/src/storage/report.test.ts.snap +0 -0
  300. package/src/storage/report.ts +215 -0
  301. package/src/storage.test.ts +479 -0
  302. package/src/storage.test.ts.md +643 -0
  303. package/src/storage.test.ts.snap +0 -0
  304. package/src/stub-provider.ts +120 -0
  305. package/src/utils/assert.ts +9 -0
  306. package/src/utils/curry.ts +17 -0
  307. package/src/utils/debug.ts +3 -0
  308. package/src/utils/deep-array.test.ts +21 -0
  309. package/src/utils/deep-array.ts +9 -0
  310. package/src/utils/function.test.ts +37 -0
  311. package/src/utils/function.ts +53 -0
  312. package/src/utils/indent.ts +3 -0
  313. package/src/utils/is-nullish.test.ts +15 -0
  314. package/src/utils/is-nullish.ts +3 -0
  315. package/src/utils/is-value-type.ts +5 -0
  316. package/src/utils/itemize.ts +16 -0
  317. package/src/utils/log.ts +34 -0
  318. package/src/utils/make-non-enumerable.ts +4 -0
  319. package/src/utils/map-values.ts +7 -0
  320. package/src/utils/parse-type-id.test.ts +22 -0
  321. package/src/utils/parse-type-id.test.ts.md +264 -0
  322. package/src/utils/parse-type-id.test.ts.snap +0 -0
  323. package/src/utils/parse-type-id.ts +104 -0
  324. package/src/utils/pick.ts +7 -0
  325. package/src/utils/type-id.ts +57 -0
  326. package/src/validate/compat.ts +9 -0
  327. package/src/validate/data.ts +55 -0
  328. package/src/validate/error.ts +75 -0
  329. package/src/validate/index.ts +17 -0
  330. package/src/validate/overrides.ts +93 -0
  331. package/src/validate/query.test.ts +48 -0
  332. package/src/validate/query.ts +127 -0
  333. package/src/validate/run.ts +278 -0
  334. package/src/validate.test.ts +133 -0
  335. package/src/version.test.ts +59 -0
  336. package/src/version.ts +71 -0
@@ -0,0 +1,127 @@
1
+ import { Version, getVersion } from '../version';
2
+ import { ValidationRunData, ValidationError } from './run';
3
+ import { StorageLayout } from '../storage/layout';
4
+ import { unlinkBytecode } from '../link-refs';
5
+ import { ValidationOptions, processExceptions } from './overrides';
6
+ import { ValidationErrors } from './error';
7
+ import { ValidationData, normalizeValidationData } from './data';
8
+
9
+ export function assertUpgradeSafe(data: ValidationData, version: Version, opts: ValidationOptions): void {
10
+ const dataV3 = normalizeValidationData(data);
11
+ const [contractName] = getContractNameAndRunValidation(dataV3, version);
12
+
13
+ let errors = getErrors(dataV3, version);
14
+ errors = processExceptions(contractName, errors, opts);
15
+
16
+ if (errors.length > 0) {
17
+ throw new ValidationErrors(contractName, errors);
18
+ }
19
+ }
20
+
21
+ export function getContractVersion(runData: ValidationRunData, contractName: string): Version {
22
+ const { version } = runData[contractName];
23
+ if (version === undefined) {
24
+ throw new Error(`Contract ${contractName} is abstract`);
25
+ }
26
+ return version;
27
+ }
28
+
29
+ export function getContractNameAndRunValidation(data: ValidationData, version: Version): [string, ValidationRunData] {
30
+ const dataV3 = normalizeValidationData(data);
31
+
32
+ let runValidation;
33
+ let contractName;
34
+
35
+ for (const validation of dataV3.log) {
36
+ contractName = Object.keys(validation).find(
37
+ name => validation[name].version?.withMetadata === version.withMetadata,
38
+ );
39
+ if (contractName !== undefined) {
40
+ runValidation = validation;
41
+ break;
42
+ }
43
+ }
44
+
45
+ if (contractName === undefined || runValidation === undefined) {
46
+ throw new Error('The requested contract was not found. Make sure the source code is available for compilation');
47
+ }
48
+
49
+ return [contractName, runValidation];
50
+ }
51
+
52
+ export function getStorageLayout(data: ValidationData, version: Version): StorageLayout {
53
+ const dataV3 = normalizeValidationData(data);
54
+ const [contractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
55
+ return unfoldStorageLayout(runValidation, contractName);
56
+ }
57
+
58
+ export function unfoldStorageLayout(runData: ValidationRunData, contractName: string): StorageLayout {
59
+ const c = runData[contractName];
60
+ const layout: StorageLayout = { storage: [], types: {} };
61
+ for (const name of [contractName].concat(c.inherit)) {
62
+ layout.storage.unshift(...runData[name].layout.storage);
63
+ Object.assign(layout.types, runData[name].layout.types);
64
+ }
65
+ return layout;
66
+ }
67
+
68
+ export function* findVersionWithoutMetadataMatches(
69
+ data: ValidationData,
70
+ versionWithoutMetadata: string,
71
+ ): Generator<[string, ValidationRunData]> {
72
+ const dataV3 = normalizeValidationData(data);
73
+
74
+ for (const validation of dataV3.log) {
75
+ for (const contractName in validation) {
76
+ if (validation[contractName].version?.withoutMetadata === versionWithoutMetadata) {
77
+ yield [contractName, validation];
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ export function getUnlinkedBytecode(data: ValidationData, bytecode: string): string {
84
+ const dataV3 = normalizeValidationData(data);
85
+
86
+ for (const validation of dataV3.log) {
87
+ const linkableContracts = Object.keys(validation).filter(name => validation[name].linkReferences.length > 0);
88
+
89
+ for (const name of linkableContracts) {
90
+ const { linkReferences } = validation[name];
91
+ const unlinkedBytecode = unlinkBytecode(bytecode, linkReferences);
92
+ const version = getVersion(unlinkedBytecode);
93
+
94
+ if (validation[name].version?.withMetadata === version.withMetadata) {
95
+ return unlinkedBytecode;
96
+ }
97
+ }
98
+ }
99
+
100
+ return bytecode;
101
+ }
102
+
103
+ export function getErrors(data: ValidationData, version: Version): ValidationError[] {
104
+ const dataV3 = normalizeValidationData(data);
105
+ const [contractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
106
+ const c = runValidation[contractName];
107
+
108
+ const errors = c.errors
109
+ .concat(...c.inherit.map(name => runValidation[name].errors))
110
+ .concat(...c.libraries.map(name => runValidation[name].errors));
111
+
112
+ const selfAndInheritedMethods = c.methods.concat(...c.inherit.map(name => runValidation[name].methods));
113
+
114
+ if (!selfAndInheritedMethods.includes('upgradeTo(address)')) {
115
+ errors.push({
116
+ src: c.src,
117
+ kind: 'missing-public-upgradeto',
118
+ });
119
+ }
120
+
121
+ return errors;
122
+ }
123
+
124
+ export function isUpgradeSafe(data: ValidationData, version: Version): boolean {
125
+ const dataV3 = normalizeValidationData(data);
126
+ return getErrors(dataV3, version).length == 0;
127
+ }
@@ -0,0 +1,278 @@
1
+ import { Node } from 'solidity-ast/node';
2
+ import { isNodeType, findAll } from 'solidity-ast/utils';
3
+ import type { ContractDefinition } from 'solidity-ast';
4
+
5
+ import { SolcOutput, SolcBytecode } from '../solc-api';
6
+ import { SrcDecoder } from '../src-decoder';
7
+ import { astDereferencer } from '../ast-dereferencer';
8
+ import { isNullish } from '../utils/is-nullish';
9
+ import { getFunctionSignature } from '../utils/function';
10
+ import { Version, getVersion } from '../version';
11
+ import { extractLinkReferences, LinkReference } from '../link-refs';
12
+ import { extractStorageLayout } from '../storage/extract';
13
+ import { StorageLayout } from '../storage/layout';
14
+
15
+ export type ValidationRunData = Record<string, ContractValidation>;
16
+
17
+ export interface ContractValidation {
18
+ version?: Version;
19
+ src: string;
20
+ inherit: string[];
21
+ libraries: string[];
22
+ methods: string[];
23
+ linkReferences: LinkReference[];
24
+ errors: ValidationError[];
25
+ layout: StorageLayout;
26
+ }
27
+
28
+ const errorKinds = [
29
+ 'state-variable-assignment',
30
+ 'state-variable-immutable',
31
+ 'external-library-linking',
32
+ 'struct-definition',
33
+ 'enum-definition',
34
+ 'constructor',
35
+ 'delegatecall',
36
+ 'selfdestruct',
37
+ 'missing-public-upgradeto',
38
+ ] as const;
39
+
40
+ export type ValidationError =
41
+ | ValidationErrorConstructor
42
+ | ValidationErrorOpcode
43
+ | ValidationErrorWithName
44
+ | ValidationErrorUpgradeability;
45
+
46
+ interface ValidationErrorBase {
47
+ src: string;
48
+ kind: typeof errorKinds[number];
49
+ }
50
+
51
+ interface ValidationErrorWithName extends ValidationErrorBase {
52
+ name: string;
53
+ kind:
54
+ | 'state-variable-assignment'
55
+ | 'state-variable-immutable'
56
+ | 'external-library-linking'
57
+ | 'struct-definition'
58
+ | 'enum-definition';
59
+ }
60
+
61
+ interface ValidationErrorConstructor extends ValidationErrorBase {
62
+ kind: 'constructor';
63
+ contract: string;
64
+ }
65
+
66
+ interface ValidationErrorOpcode extends ValidationErrorBase {
67
+ kind: 'delegatecall' | 'selfdestruct';
68
+ }
69
+
70
+ interface ValidationErrorUpgradeability extends ValidationErrorBase {
71
+ kind: 'missing-public-upgradeto';
72
+ }
73
+
74
+ function* execall(re: RegExp, text: string) {
75
+ re = new RegExp(re, re.flags + (re.sticky ? '' : 'y'));
76
+ while (true) {
77
+ const match = re.exec(text);
78
+ if (match && match[0] !== '') {
79
+ yield match;
80
+ } else {
81
+ break;
82
+ }
83
+ }
84
+ }
85
+
86
+ function getAllowed(node: Node): string[] {
87
+ if ('documentation' in node) {
88
+ const doc = typeof node.documentation === 'string' ? node.documentation : node.documentation?.text ?? '';
89
+
90
+ const result: string[] = [];
91
+ for (const { groups } of execall(
92
+ /^\s*(?:@(?<title>\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s@\w+)[^])*)/m,
93
+ doc,
94
+ )) {
95
+ if (groups && groups.title === 'custom' && groups.tag === 'oz-upgrades-unsafe-allow') {
96
+ result.push(...groups.args.split(/\s+/));
97
+ }
98
+ }
99
+
100
+ result.forEach(arg => {
101
+ if (!(errorKinds as readonly string[]).includes(arg)) {
102
+ throw new Error(`NatSpec: oz-upgrades-unsafe-allow argument not recognized: ${arg}`);
103
+ }
104
+ });
105
+
106
+ return result;
107
+ } else {
108
+ return [];
109
+ }
110
+ }
111
+
112
+ function skipCheck(error: string, node: Node): boolean {
113
+ return getAllowed(node).includes(error);
114
+ }
115
+
116
+ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder): ValidationRunData {
117
+ const validation: ValidationRunData = {};
118
+ const fromId: Record<number, string> = {};
119
+ const inheritIds: Record<string, number[]> = {};
120
+ const libraryIds: Record<string, number[]> = {};
121
+
122
+ const deref = astDereferencer(solcOutput);
123
+
124
+ for (const source in solcOutput.contracts) {
125
+ for (const contractName in solcOutput.contracts[source]) {
126
+ const bytecode = solcOutput.contracts[source][contractName].evm.bytecode;
127
+ const version = bytecode.object === '' ? undefined : getVersion(bytecode.object);
128
+ const linkReferences = extractLinkReferences(bytecode);
129
+
130
+ validation[contractName] = {
131
+ src: contractName,
132
+ version,
133
+ inherit: [],
134
+ libraries: [],
135
+ methods: [],
136
+ linkReferences,
137
+ errors: [],
138
+ layout: {
139
+ storage: [],
140
+ types: {},
141
+ },
142
+ };
143
+ }
144
+
145
+ for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) {
146
+ fromId[contractDef.id] = contractDef.name;
147
+
148
+ // May be undefined in case of duplicate contract names in Truffle
149
+ const bytecode = solcOutput.contracts[source][contractDef.name]?.evm.bytecode;
150
+
151
+ if (contractDef.name in validation && bytecode !== undefined) {
152
+ inheritIds[contractDef.name] = contractDef.linearizedBaseContracts.slice(1);
153
+ libraryIds[contractDef.name] = getReferencedLibraryIds(contractDef);
154
+
155
+ validation[contractDef.name].src = decodeSrc(contractDef);
156
+ validation[contractDef.name].errors = [
157
+ ...getConstructorErrors(contractDef, decodeSrc),
158
+ ...getOpcodeErrors(contractDef, decodeSrc),
159
+ ...getStateVariableErrors(contractDef, decodeSrc),
160
+ // TODO: add linked libraries support
161
+ // https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/52
162
+ ...getLinkingErrors(contractDef, bytecode),
163
+ ];
164
+
165
+ validation[contractDef.name].layout = extractStorageLayout(contractDef, decodeSrc, deref);
166
+ validation[contractDef.name].methods = [...findAll('FunctionDefinition', contractDef)]
167
+ .filter(fnDef => ['external', 'public'].includes(fnDef.visibility))
168
+ .map(fnDef => getFunctionSignature(fnDef, deref));
169
+ }
170
+ }
171
+ }
172
+
173
+ for (const contractName in inheritIds) {
174
+ validation[contractName].inherit = inheritIds[contractName].map(id => fromId[id]);
175
+ }
176
+
177
+ for (const contractName in libraryIds) {
178
+ validation[contractName].libraries = libraryIds[contractName].map(id => fromId[id]);
179
+ }
180
+
181
+ return validation;
182
+ }
183
+
184
+ function* getConstructorErrors(contractDef: ContractDefinition, decodeSrc: SrcDecoder): Generator<ValidationError> {
185
+ for (const fnDef of findAll('FunctionDefinition', contractDef, node => skipCheck('constructor', node))) {
186
+ if (fnDef.kind === 'constructor' && ((fnDef.body?.statements?.length ?? 0) > 0 || fnDef.modifiers.length > 0)) {
187
+ yield {
188
+ kind: 'constructor',
189
+ contract: contractDef.name,
190
+ src: decodeSrc(fnDef),
191
+ };
192
+ }
193
+ }
194
+ }
195
+
196
+ function* getOpcodeErrors(contractDef: ContractDefinition, decodeSrc: SrcDecoder): Generator<ValidationErrorOpcode> {
197
+ for (const fnCall of findAll('FunctionCall', contractDef, node => skipCheck('delegatecall', node))) {
198
+ const fn = fnCall.expression;
199
+ if (fn.typeDescriptions.typeIdentifier?.match(/^t_function_baredelegatecall_/)) {
200
+ yield {
201
+ kind: 'delegatecall',
202
+ src: decodeSrc(fnCall),
203
+ };
204
+ }
205
+ }
206
+ for (const fnCall of findAll('FunctionCall', contractDef, node => skipCheck('selfdestruct', node))) {
207
+ const fn = fnCall.expression;
208
+ if (fn.typeDescriptions.typeIdentifier?.match(/^t_function_selfdestruct_/)) {
209
+ yield {
210
+ kind: 'selfdestruct',
211
+ src: decodeSrc(fnCall),
212
+ };
213
+ }
214
+ }
215
+ }
216
+
217
+ function* getStateVariableErrors(
218
+ contractDef: ContractDefinition,
219
+ decodeSrc: SrcDecoder,
220
+ ): Generator<ValidationErrorWithName> {
221
+ for (const varDecl of contractDef.nodes) {
222
+ if (isNodeType('VariableDeclaration', varDecl)) {
223
+ if (!varDecl.constant && !isNullish(varDecl.value)) {
224
+ if (!skipCheck('state-variable-assignment', contractDef) && !skipCheck('state-variable-assignment', varDecl)) {
225
+ yield {
226
+ kind: 'state-variable-assignment',
227
+ name: varDecl.name,
228
+ src: decodeSrc(varDecl),
229
+ };
230
+ }
231
+ }
232
+ if (varDecl.mutability === 'immutable') {
233
+ if (!skipCheck('state-variable-immutable', contractDef) && !skipCheck('state-variable-immutable', varDecl)) {
234
+ yield {
235
+ kind: 'state-variable-immutable',
236
+ name: varDecl.name,
237
+ src: decodeSrc(varDecl),
238
+ };
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ function getReferencedLibraryIds(contractDef: ContractDefinition): number[] {
246
+ const implicitUsage = [...findAll('UsingForDirective', contractDef)].map(
247
+ usingForDirective => usingForDirective.libraryName.referencedDeclaration,
248
+ );
249
+
250
+ const explicitUsage = [...findAll('Identifier', contractDef)]
251
+ .filter(identifier => identifier.typeDescriptions.typeString?.match(/^type\(library/))
252
+ .map(identifier => {
253
+ if (isNullish(identifier.referencedDeclaration)) {
254
+ throw new Error('Broken invariant: Identifier.referencedDeclaration should not be null');
255
+ }
256
+ return identifier.referencedDeclaration;
257
+ });
258
+
259
+ return [...new Set(implicitUsage.concat(explicitUsage))];
260
+ }
261
+
262
+ function* getLinkingErrors(
263
+ contractDef: ContractDefinition,
264
+ bytecode: SolcBytecode,
265
+ ): Generator<ValidationErrorWithName> {
266
+ const { linkReferences } = bytecode;
267
+ for (const source of Object.keys(linkReferences)) {
268
+ for (const libName of Object.keys(linkReferences[source])) {
269
+ if (!skipCheck('external-library-linking', contractDef)) {
270
+ yield {
271
+ kind: 'external-library-linking',
272
+ name: libName,
273
+ src: source,
274
+ };
275
+ }
276
+ }
277
+ }
278
+ }
@@ -0,0 +1,133 @@
1
+ import _test, { TestInterface } from 'ava';
2
+ import { artifacts } from 'hardhat';
3
+
4
+ import {
5
+ validate,
6
+ getStorageLayout,
7
+ getContractVersion,
8
+ assertUpgradeSafe,
9
+ ValidationOptions,
10
+ RunValidation,
11
+ } from './validate';
12
+ import { solcInputOutputDecoder } from './src-decoder';
13
+
14
+ interface Context {
15
+ validation: RunValidation;
16
+ }
17
+
18
+ const test = _test as TestInterface<Context>;
19
+
20
+ test.before(async t => {
21
+ const contracts = [
22
+ 'contracts/test/Validations.sol:HasEmptyConstructor',
23
+ 'contracts/test/ValidationsNatspec.sol:HasNonEmptyConstructorNatspec1',
24
+ 'contracts/test/Proxiable.sol:ChildOfProxiable',
25
+ ];
26
+
27
+ t.context.validation = {} as RunValidation;
28
+ for (const contract of contracts) {
29
+ const buildInfo = await artifacts.getBuildInfo(contract);
30
+ if (buildInfo === undefined) {
31
+ throw new Error(`Build info not found for contract ${contract}`);
32
+ }
33
+ const solcOutput = buildInfo.output;
34
+ const solcInput = buildInfo.input;
35
+ const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput);
36
+ Object.assign(t.context.validation, validate(solcOutput, decodeSrc));
37
+ }
38
+ });
39
+
40
+ function testValid(name: string, kind: ValidationOptions['kind'], valid: boolean) {
41
+ testOverride(name, kind, {}, valid);
42
+ }
43
+
44
+ function testOverride(name: string, kind: ValidationOptions['kind'], opts: ValidationOptions, valid: boolean) {
45
+ const optKeys = Object.keys(opts);
46
+ const describeOpts = optKeys.length > 0 ? '(' + optKeys.join(', ') + ')' : '';
47
+ const testName = [valid ? 'accepts' : 'rejects', kind, name, describeOpts].join(' ');
48
+ test(testName, t => {
49
+ const version = getContractVersion(t.context.validation, name);
50
+ const assertUpgSafe = () => assertUpgradeSafe([t.context.validation], version, { kind, ...opts });
51
+ if (valid) {
52
+ t.notThrows(assertUpgSafe);
53
+ } else {
54
+ t.throws(assertUpgSafe);
55
+ }
56
+ });
57
+ }
58
+
59
+ testValid('HasEmptyConstructor', 'transparent', true);
60
+ testValid('HasConstantStateVariableAssignment', 'transparent', true);
61
+ testValid('HasStateVariable', 'transparent', true);
62
+ testValid('UsesImplicitSafeInternalLibrary', 'transparent', true);
63
+ testValid('UsesExplicitSafeInternalLibrary', 'transparent', true);
64
+
65
+ testValid('HasNonEmptyConstructor', 'transparent', false);
66
+ testValid('ParentHasNonEmptyConstructor', 'transparent', false);
67
+ testValid('AncestorHasNonEmptyConstructor', 'transparent', false);
68
+ testValid('HasStateVariableAssignment', 'transparent', false);
69
+ testValid('HasImmutableStateVariable', 'transparent', false);
70
+ testValid('HasSelfDestruct', 'transparent', false);
71
+ testValid('HasDelegateCall', 'transparent', false);
72
+ testValid('ImportedParentHasStateVariableAssignment', 'transparent', false);
73
+ testValid('UsesImplicitUnsafeInternalLibrary', 'transparent', false);
74
+ testValid('UsesExplicitUnsafeInternalLibrary', 'transparent', false);
75
+ testValid('UsesImplicitUnsafeExternalLibrary', 'transparent', false);
76
+ testValid('UsesExplicitUnsafeExternalLibrary', 'transparent', false);
77
+
78
+ // Linked external libraries are not yet supported
79
+ // see: https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/52
80
+ testValid('UsesImplicitSafeExternalLibrary', 'transparent', false);
81
+ testValid('UsesExplicitSafeExternalLibrary', 'transparent', false);
82
+
83
+ test('inherited storage', t => {
84
+ const version = getContractVersion(t.context.validation, 'StorageInheritChild');
85
+ const layout = getStorageLayout([t.context.validation], version);
86
+ t.is(layout.storage.length, 8);
87
+ for (let i = 0; i < layout.storage.length; i++) {
88
+ t.is(layout.storage[i].label, `v${i}`);
89
+ t.truthy(layout.types[layout.storage[i].type]);
90
+ }
91
+ });
92
+
93
+ testOverride('UsesImplicitSafeExternalLibrary', 'transparent', { unsafeAllowLinkedLibraries: true }, true);
94
+ testOverride('UsesExplicitSafeExternalLibrary', 'transparent', { unsafeAllowLinkedLibraries: true }, true);
95
+ testOverride('UsesImplicitSafeExternalLibrary', 'transparent', { unsafeAllow: ['external-library-linking'] }, true);
96
+ testOverride('UsesExplicitSafeExternalLibrary', 'transparent', { unsafeAllow: ['external-library-linking'] }, true);
97
+
98
+ testValid('HasEmptyConstructor', 'uups', false);
99
+ testValid('HasInternalUpgradeToFunction', 'uups', false);
100
+ testValid('HasUpgradeToFunction', 'uups', true);
101
+ testValid('ParentHasUpgradeToFunction', 'uups', true);
102
+ testValid('ChildOfProxiable', 'uups', true);
103
+
104
+ testValid('HasNonEmptyConstructorNatspec1', 'transparent', true);
105
+ testValid('HasNonEmptyConstructorNatspec2', 'transparent', true);
106
+ testValid('HasNonEmptyConstructorNatspec3', 'transparent', true);
107
+ testValid('HasNonEmptyConstructorNatspec4', 'transparent', true);
108
+ testValid('ParentHasNonEmptyConstructorNatspec1', 'transparent', true);
109
+ testValid('ParentHasNonEmptyConstructorNatspec2', 'transparent', true);
110
+ testValid('AncestorHasNonEmptyConstructorNatspec1', 'transparent', true);
111
+ testValid('AncestorHasNonEmptyConstructorNatspec2', 'transparent', true);
112
+ testValid('HasStateVariableAssignmentNatspec1', 'transparent', true);
113
+ testValid('HasStateVariableAssignmentNatspec2', 'transparent', true);
114
+ testValid('HasStateVariableAssignmentNatspec3', 'transparent', false);
115
+ testValid('HasImmutableStateVariableNatspec1', 'transparent', true);
116
+ testValid('HasImmutableStateVariableNatspec2', 'transparent', true);
117
+ testValid('HasImmutableStateVariableNatspec3', 'transparent', false);
118
+ testValid('HasSelfDestructNatspec1', 'transparent', true);
119
+ testValid('HasSelfDestructNatspec2', 'transparent', true);
120
+ testValid('HasSelfDestructNatspec3', 'transparent', true);
121
+ testValid('HasDelegateCallNatspec1', 'transparent', true);
122
+ testValid('HasDelegateCallNatspec2', 'transparent', true);
123
+ testValid('HasDelegateCallNatspec3', 'transparent', true);
124
+ testValid('ImportedParentHasStateVariableAssignmentNatspec1', 'transparent', true);
125
+ testValid('ImportedParentHasStateVariableAssignmentNatspec2', 'transparent', true);
126
+ testValid('UsesImplicitSafeInternalLibraryNatspec', 'transparent', true);
127
+ testValid('UsesImplicitSafeExternalLibraryNatspec', 'transparent', true);
128
+ testValid('UsesImplicitUnsafeInternalLibraryNatspec', 'transparent', true);
129
+ testValid('UsesImplicitUnsafeExternalLibraryNatspec', 'transparent', true);
130
+ testValid('UsesExplicitSafeInternalLibraryNatspec', 'transparent', true);
131
+ testValid('UsesExplicitSafeExternalLibraryNatspec', 'transparent', true);
132
+ testValid('UsesExplicitUnsafeInternalLibraryNatspec', 'transparent', true);
133
+ testValid('UsesExplicitUnsafeExternalLibraryNatspec', 'transparent', true);