@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.
- package/CHANGELOG.md +110 -0
- package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +4 -0
- package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +80 -0
- package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +160 -0
- package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +155 -0
- package/artifacts/AdminUpgradeabilityProxy.json +155 -0
- package/artifacts/ProxyAdmin.json +160 -0
- package/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json +155 -0
- package/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json +160 -0
- package/contracts/Initializable.sol +64 -0
- package/dist/ast-dereferencer.d.ts +8 -0
- package/dist/ast-dereferencer.d.ts.map +1 -0
- package/dist/ast-dereferencer.js +29 -0
- package/dist/ast-dereferencer.js.map +1 -0
- package/dist/deployment.d.ts +18 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +110 -0
- package/dist/deployment.js.map +1 -0
- package/dist/deployment.test.d.ts +2 -0
- package/dist/deployment.test.d.ts.map +1 -0
- package/dist/deployment.test.js +92 -0
- package/dist/deployment.test.js.map +1 -0
- package/dist/eip-1967.d.ts +7 -0
- package/dist/eip-1967.d.ts.map +1 -0
- package/dist/eip-1967.js +63 -0
- package/dist/eip-1967.js.map +1 -0
- package/dist/error.d.ts +21 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +21 -0
- package/dist/error.js.map +1 -0
- package/dist/impl-store.d.ts +7 -0
- package/dist/impl-store.d.ts.map +1 -0
- package/dist/impl-store.js +94 -0
- package/dist/impl-store.js.map +1 -0
- package/dist/impl-store.test.d.ts +2 -0
- package/dist/impl-store.test.d.ts.map +1 -0
- package/dist/impl-store.test.js +55 -0
- package/dist/impl-store.test.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/levenshtein.d.ts +12 -0
- package/dist/levenshtein.d.ts.map +1 -0
- package/dist/levenshtein.js +89 -0
- package/dist/levenshtein.js.map +1 -0
- package/dist/levenshtein.test.d.ts +2 -0
- package/dist/levenshtein.test.d.ts.map +1 -0
- package/dist/levenshtein.test.js +88 -0
- package/dist/levenshtein.test.js.map +1 -0
- package/dist/link-refs.d.ts +11 -0
- package/dist/link-refs.d.ts.map +1 -0
- package/dist/link-refs.js +36 -0
- package/dist/link-refs.js.map +1 -0
- package/dist/link-refs.test.d.ts +2 -0
- package/dist/link-refs.test.d.ts.map +1 -0
- package/dist/link-refs.test.js +76 -0
- package/dist/link-refs.test.js.map +1 -0
- package/dist/manifest-storage-layout.d.ts +6 -0
- package/dist/manifest-storage-layout.d.ts.map +1 -0
- package/dist/manifest-storage-layout.js +95 -0
- package/dist/manifest-storage-layout.js.map +1 -0
- package/dist/manifest-storage-layout.test.d.ts +2 -0
- package/dist/manifest-storage-layout.test.d.ts.map +1 -0
- package/dist/manifest-storage-layout.test.js +105 -0
- package/dist/manifest-storage-layout.test.js.map +1 -0
- package/dist/manifest.d.ts +36 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +154 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.test.d.ts +2 -0
- package/dist/manifest.test.d.ts.map +1 -0
- package/dist/manifest.test.js +17 -0
- package/dist/manifest.test.js.map +1 -0
- package/dist/provider.d.ts +37 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +74 -0
- package/dist/provider.js.map +1 -0
- package/dist/provider.test.d.ts +2 -0
- package/dist/provider.test.d.ts.map +1 -0
- package/dist/provider.test.js +41 -0
- package/dist/provider.test.js.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.d.ts +108 -0
- package/dist/scripts/migrate-oz-cli-project.d.ts.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.js +321 -0
- package/dist/scripts/migrate-oz-cli-project.js.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.test.d.ts +2 -0
- package/dist/scripts/migrate-oz-cli-project.test.d.ts.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.test.js +60 -0
- package/dist/scripts/migrate-oz-cli-project.test.js.map +1 -0
- package/dist/set-proxy-kind.d.ts +5 -0
- package/dist/set-proxy-kind.d.ts.map +1 -0
- package/dist/set-proxy-kind.js +25 -0
- package/dist/set-proxy-kind.js.map +1 -0
- package/dist/solc-api.d.ts +39 -0
- package/dist/solc-api.d.ts.map +1 -0
- package/dist/solc-api.js +3 -0
- package/dist/solc-api.js.map +1 -0
- package/dist/src-decoder.d.ts +6 -0
- package/dist/src-decoder.d.ts.map +1 -0
- package/dist/src-decoder.js +36 -0
- package/dist/src-decoder.js.map +1 -0
- package/dist/src-decoder.test.d.ts +2 -0
- package/dist/src-decoder.test.d.ts.map +1 -0
- package/dist/src-decoder.test.js +29 -0
- package/dist/src-decoder.test.js.map +1 -0
- package/dist/storage/compare.d.ts +55 -0
- package/dist/storage/compare.d.ts.map +1 -0
- package/dist/storage/compare.js +181 -0
- package/dist/storage/compare.js.map +1 -0
- package/dist/storage/compat.d.ts +4 -0
- package/dist/storage/compat.d.ts.map +1 -0
- package/dist/storage/compat.js +9 -0
- package/dist/storage/compat.js.map +1 -0
- package/dist/storage/extract.d.ts +7 -0
- package/dist/storage/extract.d.ts.map +1 -0
- package/dist/storage/extract.js +86 -0
- package/dist/storage/extract.js.map +1 -0
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +65 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/layout.d.ts +31 -0
- package/dist/storage/layout.d.ts.map +1 -0
- package/dist/storage/layout.js +46 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/report.d.ts +8 -0
- package/dist/storage/report.d.ts.map +1 -0
- package/dist/storage/report.js +175 -0
- package/dist/storage/report.js.map +1 -0
- package/dist/storage/report.test.d.ts +2 -0
- package/dist/storage/report.test.d.ts.map +1 -0
- package/dist/storage/report.test.js +82 -0
- package/dist/storage/report.test.js.map +1 -0
- package/dist/storage.test.d.ts +2 -0
- package/dist/storage.test.d.ts.map +1 -0
- package/dist/storage.test.js +436 -0
- package/dist/storage.test.js.map +1 -0
- package/dist/stub-provider.d.ts +14 -0
- package/dist/stub-provider.d.ts.map +1 -0
- package/dist/stub-provider.js +128 -0
- package/dist/stub-provider.js.map +1 -0
- package/dist/utils/assert.d.ts +3 -0
- package/dist/utils/assert.d.ts.map +1 -0
- package/dist/utils/assert.js +14 -0
- package/dist/utils/assert.js.map +1 -0
- package/dist/utils/curry.d.ts +6 -0
- package/dist/utils/curry.d.ts.map +1 -0
- package/dist/utils/curry.js +16 -0
- package/dist/utils/curry.js.map +1 -0
- package/dist/utils/debug.d.ts +4 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +8 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/deep-array.d.ts +3 -0
- package/dist/utils/deep-array.d.ts.map +1 -0
- package/dist/utils/deep-array.js +13 -0
- package/dist/utils/deep-array.js.map +1 -0
- package/dist/utils/deep-array.test.d.ts +2 -0
- package/dist/utils/deep-array.test.d.ts.map +1 -0
- package/dist/utils/deep-array.test.js +23 -0
- package/dist/utils/deep-array.test.js.map +1 -0
- package/dist/utils/function.d.ts +4 -0
- package/dist/utils/function.d.ts.map +1 -0
- package/dist/utils/function.js +43 -0
- package/dist/utils/function.js.map +1 -0
- package/dist/utils/function.test.d.ts +2 -0
- package/dist/utils/function.test.d.ts.map +1 -0
- package/dist/utils/function.test.js +34 -0
- package/dist/utils/function.test.js.map +1 -0
- package/dist/utils/indent.d.ts +2 -0
- package/dist/utils/indent.d.ts.map +1 -0
- package/dist/utils/indent.js +8 -0
- package/dist/utils/indent.js.map +1 -0
- package/dist/utils/is-nullish.d.ts +2 -0
- package/dist/utils/is-nullish.d.ts.map +1 -0
- package/dist/utils/is-nullish.js +8 -0
- package/dist/utils/is-nullish.js.map +1 -0
- package/dist/utils/is-nullish.test.d.ts +2 -0
- package/dist/utils/is-nullish.test.d.ts.map +1 -0
- package/dist/utils/is-nullish.test.js +17 -0
- package/dist/utils/is-nullish.test.js.map +1 -0
- package/dist/utils/is-value-type.d.ts +3 -0
- package/dist/utils/is-value-type.d.ts.map +1 -0
- package/dist/utils/is-value-type.js +8 -0
- package/dist/utils/is-value-type.js.map +1 -0
- package/dist/utils/itemize.d.ts +3 -0
- package/dist/utils/itemize.d.ts.map +1 -0
- package/dist/utils/itemize.js +18 -0
- package/dist/utils/itemize.js.map +1 -0
- package/dist/utils/log.d.ts +4 -0
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +35 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/make-non-enumerable.d.ts +2 -0
- package/dist/utils/make-non-enumerable.d.ts.map +1 -0
- package/dist/utils/make-non-enumerable.js +9 -0
- package/dist/utils/make-non-enumerable.js.map +1 -0
- package/dist/utils/map-values.d.ts +2 -0
- package/dist/utils/map-values.d.ts.map +1 -0
- package/dist/utils/map-values.js +12 -0
- package/dist/utils/map-values.js.map +1 -0
- package/dist/utils/parse-type-id.d.ts +9 -0
- package/dist/utils/parse-type-id.d.ts.map +1 -0
- package/dist/utils/parse-type-id.js +92 -0
- package/dist/utils/parse-type-id.js.map +1 -0
- package/dist/utils/parse-type-id.test.d.ts +2 -0
- package/dist/utils/parse-type-id.test.d.ts.map +1 -0
- package/dist/utils/parse-type-id.test.js +25 -0
- package/dist/utils/parse-type-id.test.js.map +1 -0
- package/dist/utils/pick.d.ts +2 -0
- package/dist/utils/pick.d.ts.map +1 -0
- package/dist/utils/pick.js +12 -0
- package/dist/utils/pick.js.map +1 -0
- package/dist/utils/type-id.d.ts +4 -0
- package/dist/utils/type-id.d.ts.map +1 -0
- package/dist/utils/type-id.js +65 -0
- package/dist/utils/type-id.js.map +1 -0
- package/dist/validate/compat.d.ts +6 -0
- package/dist/validate/compat.d.ts.map +1 -0
- package/dist/validate/compat.js +4 -0
- package/dist/validate/compat.js.map +1 -0
- package/dist/validate/data.d.ts +14 -0
- package/dist/validate/data.d.ts.map +1 -0
- package/dist/validate/data.js +50 -0
- package/dist/validate/data.js.map +1 -0
- package/dist/validate/error.d.ts +7 -0
- package/dist/validate/error.d.ts.map +1 -0
- package/dist/validate/error.js +78 -0
- package/dist/validate/error.js.map +1 -0
- package/dist/validate/index.d.ts +8 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +24 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/overrides.d.ts +14 -0
- package/dist/validate/overrides.d.ts.map +1 -0
- package/dist/validate/overrides.js +72 -0
- package/dist/validate/overrides.js.map +1 -0
- package/dist/validate/query.d.ts +15 -0
- package/dist/validate/query.d.ts.map +1 -0
- package/dist/validate/query.js +111 -0
- package/dist/validate/query.js.map +1 -0
- package/dist/validate/query.test.d.ts +2 -0
- package/dist/validate/query.test.d.ts.map +1 -0
- package/dist/validate/query.test.js +48 -0
- package/dist/validate/query.test.js.map +1 -0
- package/dist/validate/run.d.ts +39 -0
- package/dist/validate/run.d.ts.map +1 -0
- package/dist/validate/run.js +199 -0
- package/dist/validate/run.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +116 -0
- package/dist/validate.test.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +66 -0
- package/dist/version.js.map +1 -0
- package/dist/version.test.d.ts +2 -0
- package/dist/version.test.d.ts.map +1 -0
- package/dist/version.test.js +53 -0
- package/dist/version.test.js.map +1 -0
- package/package.json +58 -0
- package/src/ast-dereferencer.ts +42 -0
- package/src/deployment.test.ts +98 -0
- package/src/deployment.ts +122 -0
- package/src/eip-1967.ts +77 -0
- package/src/error.ts +26 -0
- package/src/impl-store.test.ts +59 -0
- package/src/impl-store.ts +124 -0
- package/src/index.ts +18 -0
- package/src/levenshtein.test.ts +91 -0
- package/src/levenshtein.ts +116 -0
- package/src/link-refs.test.ts +77 -0
- package/src/link-refs.ts +41 -0
- package/src/manifest-storage-layout.test.ts +118 -0
- package/src/manifest-storage-layout.ts +107 -0
- package/src/manifest.test.ts +13 -0
- package/src/manifest.ts +178 -0
- package/src/provider.test.ts +43 -0
- package/src/provider.ts +103 -0
- package/src/scripts/migratable-manifest.test.json +175 -0
- package/src/scripts/migrate-oz-cli-project.test.ts +70 -0
- package/src/scripts/migrate-oz-cli-project.test.ts.md +146 -0
- package/src/scripts/migrate-oz-cli-project.test.ts.snap +0 -0
- package/src/scripts/migrate-oz-cli-project.ts +484 -0
- package/src/scripts/project-file.test.json +25 -0
- package/src/set-proxy-kind.ts +27 -0
- package/src/solc-api.ts +42 -0
- package/src/src-decoder.test.ts +30 -0
- package/src/src-decoder.ts +37 -0
- package/src/storage/compare.ts +249 -0
- package/src/storage/compat.ts +3 -0
- package/src/storage/extract.ts +110 -0
- package/src/storage/index.ts +73 -0
- package/src/storage/layout.ts +88 -0
- package/src/storage/report.test.ts +95 -0
- package/src/storage/report.test.ts.md +154 -0
- package/src/storage/report.test.ts.snap +0 -0
- package/src/storage/report.ts +215 -0
- package/src/storage.test.ts +479 -0
- package/src/storage.test.ts.md +643 -0
- package/src/storage.test.ts.snap +0 -0
- package/src/stub-provider.ts +120 -0
- package/src/utils/assert.ts +9 -0
- package/src/utils/curry.ts +17 -0
- package/src/utils/debug.ts +3 -0
- package/src/utils/deep-array.test.ts +21 -0
- package/src/utils/deep-array.ts +9 -0
- package/src/utils/function.test.ts +37 -0
- package/src/utils/function.ts +53 -0
- package/src/utils/indent.ts +3 -0
- package/src/utils/is-nullish.test.ts +15 -0
- package/src/utils/is-nullish.ts +3 -0
- package/src/utils/is-value-type.ts +5 -0
- package/src/utils/itemize.ts +16 -0
- package/src/utils/log.ts +34 -0
- package/src/utils/make-non-enumerable.ts +4 -0
- package/src/utils/map-values.ts +7 -0
- package/src/utils/parse-type-id.test.ts +22 -0
- package/src/utils/parse-type-id.test.ts.md +264 -0
- package/src/utils/parse-type-id.test.ts.snap +0 -0
- package/src/utils/parse-type-id.ts +104 -0
- package/src/utils/pick.ts +7 -0
- package/src/utils/type-id.ts +57 -0
- package/src/validate/compat.ts +9 -0
- package/src/validate/data.ts +55 -0
- package/src/validate/error.ts +75 -0
- package/src/validate/index.ts +17 -0
- package/src/validate/overrides.ts +93 -0
- package/src/validate/query.test.ts +48 -0
- package/src/validate/query.ts +127 -0
- package/src/validate/run.ts +278 -0
- package/src/validate.test.ts +133 -0
- package/src/version.test.ts +59 -0
- package/src/version.ts +71 -0
package/src/link-refs.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SolcBytecode } from './solc-api';
|
|
2
|
+
|
|
3
|
+
export interface LinkReference {
|
|
4
|
+
src: string;
|
|
5
|
+
name: string;
|
|
6
|
+
length: number;
|
|
7
|
+
start: number;
|
|
8
|
+
placeholder: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function extractLinkReferences(bytecode: SolcBytecode): LinkReference[] {
|
|
12
|
+
const linkRefs: LinkReference[] = [];
|
|
13
|
+
const { linkReferences } = bytecode;
|
|
14
|
+
for (const source of Object.keys(linkReferences)) {
|
|
15
|
+
for (const name of Object.keys(linkReferences[source])) {
|
|
16
|
+
for (const { length, start } of linkReferences[source][name]) {
|
|
17
|
+
const placeholder = bytecode.object.substr(start * 2, length * 2);
|
|
18
|
+
linkRefs.push({
|
|
19
|
+
src: source,
|
|
20
|
+
name,
|
|
21
|
+
length,
|
|
22
|
+
start,
|
|
23
|
+
placeholder,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return linkRefs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function unlinkBytecode(bytecode: string, linkReferences: LinkReference[]): string {
|
|
32
|
+
let unlinkedBytecode: string = bytecode.replace(/^0x/, '');
|
|
33
|
+
for (const linkRef of linkReferences) {
|
|
34
|
+
const { length, start, placeholder } = linkRef;
|
|
35
|
+
if ((start + length) * 2 <= unlinkedBytecode.length) {
|
|
36
|
+
unlinkedBytecode =
|
|
37
|
+
unlinkedBytecode.substr(0, start * 2) + placeholder + unlinkedBytecode.substr((start + length) * 2);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return '0x' + unlinkedBytecode;
|
|
41
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import _test, { TestInterface } from 'ava';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
import { artifacts } from 'hardhat';
|
|
5
|
+
|
|
6
|
+
import { solcInputOutputDecoder } from './src-decoder';
|
|
7
|
+
import { validate, ValidationRunData } from './validate/run';
|
|
8
|
+
import { getStorageLayout } from './validate/query';
|
|
9
|
+
import { normalizeValidationData, ValidationData } from './validate/data';
|
|
10
|
+
import { StorageLayout } from './storage/layout';
|
|
11
|
+
|
|
12
|
+
import { Manifest, ManifestData } from './manifest';
|
|
13
|
+
import { getUpdatedStorageLayout, getStorageLayoutForAddress } from './manifest-storage-layout';
|
|
14
|
+
|
|
15
|
+
interface Context {
|
|
16
|
+
validationRun: ValidationRunData;
|
|
17
|
+
validationData: ValidationData;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const test = _test as TestInterface<Context>;
|
|
21
|
+
|
|
22
|
+
test.before(async t => {
|
|
23
|
+
const buildInfo = await artifacts.getBuildInfo('contracts/test/ManifestMigrate.sol:ManifestMigrateUnique');
|
|
24
|
+
assert(buildInfo !== undefined, 'Build info not found');
|
|
25
|
+
const decodeSrc = solcInputOutputDecoder(buildInfo.input, buildInfo.output);
|
|
26
|
+
t.context.validationRun = validate(buildInfo.output, decodeSrc);
|
|
27
|
+
t.context.validationData = normalizeValidationData([t.context.validationRun]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('getStorageLayoutForAddress - update layout', async t => {
|
|
31
|
+
const { version } = t.context.validationRun['ManifestMigrateUnique'];
|
|
32
|
+
assert(version !== undefined);
|
|
33
|
+
const updatedLayout = getStorageLayout(t.context.validationData, version);
|
|
34
|
+
const outdatedLayout = removeStorageLayoutMembers(updatedLayout);
|
|
35
|
+
const address = '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9';
|
|
36
|
+
const manifest = mockManifest({
|
|
37
|
+
manifestVersion: '3.2',
|
|
38
|
+
impls: {
|
|
39
|
+
[version.withoutMetadata]: {
|
|
40
|
+
address,
|
|
41
|
+
txHash: '0x6580b51f3edcacacf30d7b4140e4022b65d2a5ba7cbe7e4d91397f4c3b5e8a6b',
|
|
42
|
+
layout: outdatedLayout,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
proxies: [],
|
|
46
|
+
});
|
|
47
|
+
const layout = await getStorageLayoutForAddress(manifest, t.context.validationData, address);
|
|
48
|
+
t.deepEqual(layout, updatedLayout);
|
|
49
|
+
t.like(manifest.data, {
|
|
50
|
+
impls: {
|
|
51
|
+
[version.withoutMetadata]: {
|
|
52
|
+
address,
|
|
53
|
+
txHash: '0x6580b51f3edcacacf30d7b4140e4022b65d2a5ba7cbe7e4d91397f4c3b5e8a6b',
|
|
54
|
+
layout: updatedLayout,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
proxies: [],
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('getUpdatedLayout - unique layout match', async t => {
|
|
62
|
+
const { version } = t.context.validationRun['ManifestMigrateUnique'];
|
|
63
|
+
assert(version !== undefined);
|
|
64
|
+
const targetLayout = getStorageLayout(t.context.validationData, version);
|
|
65
|
+
const outdatedLayout = removeStorageLayoutMembers(targetLayout);
|
|
66
|
+
const updatedLayout = getUpdatedStorageLayout(t.context.validationData, version.withoutMetadata, outdatedLayout);
|
|
67
|
+
t.deepEqual(updatedLayout, targetLayout);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('getUpdatedLayout - multiple unambiguous layout matches', async t => {
|
|
71
|
+
const { version: version1 } = t.context.validationRun['ManifestMigrateUnambiguous1'];
|
|
72
|
+
const { version: version2 } = t.context.validationRun['ManifestMigrateUnambiguous2'];
|
|
73
|
+
assert(version1 !== undefined && version2 !== undefined);
|
|
74
|
+
t.is(version1.withoutMetadata, version2.withoutMetadata, 'version is meant to be ambiguous');
|
|
75
|
+
t.not(version1.withMetadata, version2.withMetadata, 'version with metadata should be different');
|
|
76
|
+
const targetLayout = getStorageLayout(t.context.validationData, version1);
|
|
77
|
+
const outdatedLayout = removeStorageLayoutMembers(targetLayout);
|
|
78
|
+
const updatedLayout = getUpdatedStorageLayout(t.context.validationData, version1.withoutMetadata, outdatedLayout);
|
|
79
|
+
t.deepEqual(updatedLayout, targetLayout);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('getUpdatedLayout - multiple ambiguous layout matches', async t => {
|
|
83
|
+
const { version: version1 } = t.context.validationRun['ManifestMigrateAmbiguous1'];
|
|
84
|
+
const { version: version2 } = t.context.validationRun['ManifestMigrateAmbiguous2'];
|
|
85
|
+
assert(version1 !== undefined && version2 !== undefined);
|
|
86
|
+
t.is(version1.withoutMetadata, version2.withoutMetadata, 'version is meant to be ambiguous');
|
|
87
|
+
t.not(version1.withMetadata, version2.withMetadata, 'version with metadata should be different');
|
|
88
|
+
const targetLayout = getStorageLayout(t.context.validationData, version1);
|
|
89
|
+
const outdatedLayout = removeStorageLayoutMembers(targetLayout);
|
|
90
|
+
const updatedLayout = getUpdatedStorageLayout(t.context.validationData, version1.withoutMetadata, outdatedLayout);
|
|
91
|
+
t.is(updatedLayout, undefined);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
function mockManifest(data: ManifestData) {
|
|
95
|
+
type Mocked = 'read' | 'write' | 'lockedRun';
|
|
96
|
+
const manifest: Pick<Manifest, Mocked> & { data: ManifestData } = {
|
|
97
|
+
data,
|
|
98
|
+
async read() {
|
|
99
|
+
return this.data;
|
|
100
|
+
},
|
|
101
|
+
async write(data: ManifestData) {
|
|
102
|
+
this.data = data;
|
|
103
|
+
},
|
|
104
|
+
async lockedRun<T>(cb: () => Promise<T>) {
|
|
105
|
+
return cb();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
return manifest as Manifest & { data: ManifestData };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Simulate a layout from a version without struct/enum members
|
|
112
|
+
function removeStorageLayoutMembers(layout: StorageLayout): StorageLayout {
|
|
113
|
+
const res = { ...layout, types: { ...layout.types } };
|
|
114
|
+
for (const id in res.types) {
|
|
115
|
+
res.types[id] = { ...layout.types[id], members: undefined };
|
|
116
|
+
}
|
|
117
|
+
return res;
|
|
118
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Manifest } from './manifest';
|
|
2
|
+
import { isCurrentLayoutVersion } from './storage/extract';
|
|
3
|
+
import { StorageLayout, isStructMembers } from './storage/layout';
|
|
4
|
+
import { ValidationData } from './validate/data';
|
|
5
|
+
import { findVersionWithoutMetadataMatches, unfoldStorageLayout } from './validate/query';
|
|
6
|
+
import { stabilizeTypeIdentifier } from './utils/type-id';
|
|
7
|
+
import { DeepArray, deepEqual } from './utils/deep-array';
|
|
8
|
+
|
|
9
|
+
export async function getStorageLayoutForAddress(
|
|
10
|
+
manifest: Manifest,
|
|
11
|
+
validations: ValidationData,
|
|
12
|
+
implAddress: string,
|
|
13
|
+
): Promise<StorageLayout> {
|
|
14
|
+
const data = await manifest.read();
|
|
15
|
+
const versionWithoutMetadata = Object.keys(data.impls).find(v => data.impls[v]?.address === implAddress);
|
|
16
|
+
if (versionWithoutMetadata === undefined) {
|
|
17
|
+
throw new Error(`Deployment at address ${implAddress} is not registered`);
|
|
18
|
+
}
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20
|
+
const { layout } = data.impls[versionWithoutMetadata]!;
|
|
21
|
+
|
|
22
|
+
if (isCurrentLayoutVersion(layout)) {
|
|
23
|
+
return layout;
|
|
24
|
+
} else {
|
|
25
|
+
const updatedLayout = getUpdatedStorageLayout(validations, versionWithoutMetadata, layout);
|
|
26
|
+
|
|
27
|
+
if (updatedLayout === undefined) {
|
|
28
|
+
return layout;
|
|
29
|
+
} else {
|
|
30
|
+
await manifest.lockedRun(async () => {
|
|
31
|
+
const data = await manifest.read();
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
33
|
+
data.impls[versionWithoutMetadata]!.layout = updatedLayout;
|
|
34
|
+
await manifest.write(data);
|
|
35
|
+
});
|
|
36
|
+
return updatedLayout;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// This function is used to retrieve the updated storage layout for an impl
|
|
42
|
+
// contract from the Manifest file, for which we only have the version hash
|
|
43
|
+
// without metadata and a storage layout. The storage layout is updated in the
|
|
44
|
+
// sense that it is updated to include newer information that hadn't been
|
|
45
|
+
// extracted when the impl contract was deployed, such as struct members.
|
|
46
|
+
export function getUpdatedStorageLayout(
|
|
47
|
+
data: ValidationData,
|
|
48
|
+
versionWithoutMetadata: string,
|
|
49
|
+
layout: StorageLayout,
|
|
50
|
+
): StorageLayout | undefined {
|
|
51
|
+
// In this map we store the candidate storage layouts, based on having the
|
|
52
|
+
// same versionWithoutMetadata and a sufficiently similar layout. The keys of
|
|
53
|
+
// the map are the versionWithMetadata of each contract, to avoid storing the
|
|
54
|
+
// same contract multiple times.
|
|
55
|
+
const sameLayout = new Map<string, StorageLayout>();
|
|
56
|
+
|
|
57
|
+
outer: for (const [contractName, validationRun] of findVersionWithoutMetadataMatches(data, versionWithoutMetadata)) {
|
|
58
|
+
const updatedLayout = unfoldStorageLayout(validationRun, contractName);
|
|
59
|
+
|
|
60
|
+
// Check that the layout roughly matches the one we already have.
|
|
61
|
+
if (updatedLayout.storage.length !== layout.storage.length) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
for (const [i, item] of layout.storage.entries()) {
|
|
65
|
+
const updatedItem = updatedLayout.storage[i];
|
|
66
|
+
if (item.label !== updatedItem.label) {
|
|
67
|
+
continue outer;
|
|
68
|
+
}
|
|
69
|
+
if (stabilizeTypeIdentifier(item.type) !== stabilizeTypeIdentifier(updatedItem.type)) {
|
|
70
|
+
continue outer;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { version } = validationRun[contractName];
|
|
75
|
+
if (version && !sameLayout.has(version.withMetadata)) {
|
|
76
|
+
sameLayout.set(version.withMetadata, updatedLayout);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (sameLayout.size > 1) {
|
|
81
|
+
// Reject multiple matches unless they all have exactly the same layout.
|
|
82
|
+
const typeMembers = new Map<string, DeepArray<string>>();
|
|
83
|
+
for (const { types } of sameLayout.values()) {
|
|
84
|
+
for (const [typeId, item] of Object.entries(types)) {
|
|
85
|
+
if (item.members === undefined) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const members: DeepArray<string> = isStructMembers(item.members)
|
|
90
|
+
? item.members.map(m => [stabilizeTypeIdentifier(m.type), m.label])
|
|
91
|
+
: item.members;
|
|
92
|
+
const stableId = stabilizeTypeIdentifier(typeId);
|
|
93
|
+
|
|
94
|
+
if (!typeMembers.has(stableId)) {
|
|
95
|
+
typeMembers.set(stableId, members);
|
|
96
|
+
} else if (!deepEqual(members, typeMembers.get(stableId))) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (sameLayout.size > 0) {
|
|
104
|
+
const [updatedLayout] = sameLayout.values();
|
|
105
|
+
return updatedLayout;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { Manifest } from './manifest';
|
|
3
|
+
|
|
4
|
+
test('manifest name for a known network', t => {
|
|
5
|
+
const manifest = new Manifest(1);
|
|
6
|
+
t.is(manifest.file, '.openzeppelin/mainnet.json');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('manifest name for an unknown network', t => {
|
|
10
|
+
const id = 55555;
|
|
11
|
+
const manifest = new Manifest(id);
|
|
12
|
+
t.is(manifest.file, `.openzeppelin/unknown-${id}.json`);
|
|
13
|
+
});
|
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { EthereumProvider, getChainId, networkNames } from './provider';
|
|
4
|
+
import lockfile from 'proper-lockfile';
|
|
5
|
+
import { compare as compareVersions } from 'compare-versions';
|
|
6
|
+
|
|
7
|
+
import type { Deployment } from './deployment';
|
|
8
|
+
import type { StorageLayout } from './storage';
|
|
9
|
+
import { pick } from './utils/pick';
|
|
10
|
+
import { mapValues } from './utils/map-values';
|
|
11
|
+
|
|
12
|
+
const currentManifestVersion = '3.2';
|
|
13
|
+
|
|
14
|
+
export interface ManifestData {
|
|
15
|
+
manifestVersion: string;
|
|
16
|
+
impls: {
|
|
17
|
+
[version in string]?: ImplDeployment;
|
|
18
|
+
};
|
|
19
|
+
proxies: ProxyDeployment[];
|
|
20
|
+
admin?: Deployment;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ImplDeployment extends Deployment {
|
|
24
|
+
layout: StorageLayout;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProxyDeployment extends Deployment {
|
|
28
|
+
kind: 'uups' | 'transparent';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function defaultManifest(): ManifestData {
|
|
32
|
+
return {
|
|
33
|
+
manifestVersion: currentManifestVersion,
|
|
34
|
+
impls: {},
|
|
35
|
+
proxies: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const manifestDir = '.openzeppelin';
|
|
40
|
+
|
|
41
|
+
export class Manifest {
|
|
42
|
+
readonly file: string;
|
|
43
|
+
private locked = false;
|
|
44
|
+
|
|
45
|
+
static async forNetwork(provider: EthereumProvider): Promise<Manifest> {
|
|
46
|
+
return new Manifest(await getChainId(provider));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constructor(readonly chainId: number) {
|
|
50
|
+
const name = networkNames[chainId] ?? `unknown-${chainId}`;
|
|
51
|
+
this.file = path.join(manifestDir, `${name}.json`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getAdmin(): Promise<Deployment | undefined> {
|
|
55
|
+
return (await this.read()).admin;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getDeploymentFromAddress(address: string): Promise<ImplDeployment> {
|
|
59
|
+
const data = await this.read();
|
|
60
|
+
const deployment = Object.values(data.impls).find(d => d?.address === address);
|
|
61
|
+
if (deployment === undefined) {
|
|
62
|
+
throw new DeploymentNotFound(`Deployment at address ${address} is not registered`);
|
|
63
|
+
}
|
|
64
|
+
return deployment;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getProxyFromAddress(address: string): Promise<ProxyDeployment> {
|
|
68
|
+
const data = await this.read();
|
|
69
|
+
const deployment = data.proxies.find(d => d?.address === address);
|
|
70
|
+
if (deployment === undefined) {
|
|
71
|
+
throw new DeploymentNotFound(`Proxy at address ${address} is not registered`);
|
|
72
|
+
}
|
|
73
|
+
return deployment;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async addProxy(proxy: ProxyDeployment): Promise<void> {
|
|
77
|
+
await this.lockedRun(async () => {
|
|
78
|
+
const data = await this.read();
|
|
79
|
+
const existing = data.proxies.findIndex(p => p.address === proxy.address);
|
|
80
|
+
if (existing >= 0) {
|
|
81
|
+
data.proxies.splice(existing, 1);
|
|
82
|
+
}
|
|
83
|
+
data.proxies.push(proxy);
|
|
84
|
+
await this.write(data);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async read(): Promise<ManifestData> {
|
|
89
|
+
const release = this.locked ? undefined : await this.lock();
|
|
90
|
+
try {
|
|
91
|
+
const data = JSON.parse(await fs.readFile(this.file, 'utf8')) as ManifestData;
|
|
92
|
+
return validateOrUpdateManifestVersion(data);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
if (e.code === 'ENOENT') {
|
|
95
|
+
return defaultManifest();
|
|
96
|
+
} else {
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
} finally {
|
|
100
|
+
await release?.();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async write(data: ManifestData): Promise<void> {
|
|
105
|
+
if (!this.locked) {
|
|
106
|
+
throw new Error('Manifest must be locked');
|
|
107
|
+
}
|
|
108
|
+
const normalized = normalizeManifestData(data);
|
|
109
|
+
await fs.writeFile(this.file, JSON.stringify(normalized, null, 2) + '\n');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async lockedRun<T>(cb: () => Promise<T>): Promise<T> {
|
|
113
|
+
if (this.locked) {
|
|
114
|
+
throw new Error('Manifest is already locked');
|
|
115
|
+
}
|
|
116
|
+
const release = await this.lock();
|
|
117
|
+
try {
|
|
118
|
+
return await cb();
|
|
119
|
+
} finally {
|
|
120
|
+
await release();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async lock() {
|
|
125
|
+
await fs.mkdir(path.dirname(this.file), { recursive: true });
|
|
126
|
+
const release = await lockfile.lock(this.file, { retries: 3, realpath: false });
|
|
127
|
+
this.locked = true;
|
|
128
|
+
return async () => {
|
|
129
|
+
await release();
|
|
130
|
+
this.locked = false;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateOrUpdateManifestVersion(data: ManifestData): ManifestData {
|
|
136
|
+
if (typeof data.manifestVersion !== 'string') {
|
|
137
|
+
throw new Error('Manifest version is missing');
|
|
138
|
+
} else if (compareVersions(data.manifestVersion, '3.0', '<')) {
|
|
139
|
+
throw new Error('Found a manifest file for OpenZeppelin CLI. An automated migration is not yet available.');
|
|
140
|
+
} else if (compareVersions(data.manifestVersion, currentManifestVersion, '<')) {
|
|
141
|
+
return migrateManifest(data);
|
|
142
|
+
} else if (data.manifestVersion === currentManifestVersion) {
|
|
143
|
+
return data;
|
|
144
|
+
} else {
|
|
145
|
+
throw new Error(`Unknown value for manifest version (${data.manifestVersion})`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function migrateManifest(data: ManifestData): ManifestData {
|
|
150
|
+
switch (data.manifestVersion) {
|
|
151
|
+
case '3.0':
|
|
152
|
+
case '3.1':
|
|
153
|
+
data.manifestVersion = currentManifestVersion;
|
|
154
|
+
data.proxies = [];
|
|
155
|
+
return data;
|
|
156
|
+
default:
|
|
157
|
+
throw new Error('Manifest migration not available');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export class DeploymentNotFound extends Error {}
|
|
162
|
+
|
|
163
|
+
function normalizeManifestData(input: ManifestData): ManifestData {
|
|
164
|
+
return {
|
|
165
|
+
...pick(input, ['manifestVersion', 'admin']),
|
|
166
|
+
proxies: input.proxies.map(p => normalizeDeployment(p, ['kind'])),
|
|
167
|
+
impls: mapValues(input.impls, i => i && normalizeDeployment(i, ['layout'])),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function normalizeDeployment<D extends Deployment>(input: D): Deployment;
|
|
172
|
+
function normalizeDeployment<D extends Deployment, K extends keyof D>(input: D, include: K[]): Deployment & Pick<D, K>;
|
|
173
|
+
function normalizeDeployment<D extends Deployment, K extends keyof D>(
|
|
174
|
+
input: D,
|
|
175
|
+
include: K[] = [],
|
|
176
|
+
): Deployment & Pick<D, K> {
|
|
177
|
+
return pick(input, ['address', 'txHash', ...include]);
|
|
178
|
+
}
|