@openzeppelin/upgrades-core 1.9.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.dbg.json +4 -0
- package/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.json +80 -0
- package/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.dbg.json +4 -0
- package/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json +113 -0
- package/artifacts/AdminUpgradeabilityProxy.json +2 -2
- package/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json +2 -2
- package/dist/ast-dereferencer.js +2 -2
- package/dist/ast-dereferencer.js.map +1 -1
- package/dist/beacon.d.ts +8 -0
- package/dist/beacon.d.ts.map +1 -0
- package/dist/beacon.js +25 -0
- package/dist/beacon.js.map +1 -0
- package/dist/deployment.js +18 -18
- package/dist/deployment.js.map +1 -1
- package/dist/deployment.test.js +39 -39
- package/dist/deployment.test.js.map +1 -1
- package/dist/eip-1967-type.d.ts +4 -0
- package/dist/eip-1967-type.d.ts.map +1 -0
- package/dist/eip-1967-type.js +37 -0
- package/dist/eip-1967-type.js.map +1 -0
- package/dist/eip-1967.d.ts +5 -0
- package/dist/eip-1967.d.ts.map +1 -1
- package/dist/eip-1967.js +22 -15
- package/dist/eip-1967.js.map +1 -1
- package/dist/impl-address.d.ts +19 -0
- package/dist/impl-address.d.ts.map +1 -0
- package/dist/impl-address.js +67 -0
- package/dist/impl-address.js.map +1 -0
- package/dist/impl-store.d.ts.map +1 -1
- package/dist/impl-store.js +8 -6
- package/dist/impl-store.js.map +1 -1
- package/dist/impl-store.test.js +18 -18
- package/dist/impl-store.test.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -3
- package/dist/index.js.map +1 -1
- package/dist/levenshtein.test.js +14 -14
- package/dist/levenshtein.test.js.map +1 -1
- package/dist/link-refs.test.js +4 -4
- package/dist/link-refs.test.js.map +1 -1
- package/dist/manifest-storage-layout.js +8 -8
- package/dist/manifest-storage-layout.js.map +1 -1
- package/dist/manifest-storage-layout.test.js +16 -16
- package/dist/manifest-storage-layout.test.js.map +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +6 -6
- package/dist/manifest.js.map +1 -1
- package/dist/manifest.test.js +2 -2
- package/dist/manifest.test.js.map +1 -1
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +11 -1
- package/dist/provider.js.map +1 -1
- package/dist/provider.test.js +12 -12
- package/dist/provider.test.js.map +1 -1
- package/dist/proxy-kind.d.ts +12 -0
- package/dist/proxy-kind.d.ts.map +1 -0
- package/dist/proxy-kind.js +50 -0
- package/dist/proxy-kind.js.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.js +1 -1
- package/dist/scripts/migrate-oz-cli-project.js.map +1 -1
- package/dist/scripts/migrate-oz-cli-project.test.js +9 -9
- package/dist/scripts/migrate-oz-cli-project.test.js.map +1 -1
- package/dist/src-decoder.test.js +2 -3
- package/dist/src-decoder.test.js.map +1 -1
- package/dist/standalone.d.ts +19 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +24 -0
- package/dist/standalone.js.map +1 -0
- package/dist/standalone.test.d.ts +2 -0
- package/dist/standalone.test.d.ts.map +1 -0
- package/dist/standalone.test.js +33 -0
- package/dist/standalone.test.js.map +1 -0
- package/dist/storage/compare.js +7 -7
- package/dist/storage/compare.js.map +1 -1
- package/dist/storage/extract.js +9 -9
- package/dist/storage/extract.js.map +1 -1
- package/dist/storage/index.d.ts +7 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +22 -23
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/layout.js +1 -1
- package/dist/storage/layout.js.map +1 -1
- package/dist/storage/report.d.ts +3 -2
- package/dist/storage/report.d.ts.map +1 -1
- package/dist/storage/report.js +17 -11
- package/dist/storage/report.js.map +1 -1
- package/dist/storage/report.test.js +5 -5
- package/dist/storage/report.test.js.map +1 -1
- package/dist/storage.test.js +32 -32
- package/dist/storage.test.js.map +1 -1
- package/dist/stub-provider.d.ts.map +1 -1
- package/dist/stub-provider.js +0 -1
- package/dist/stub-provider.js.map +1 -1
- package/dist/usage-error.d.ts +21 -0
- package/dist/usage-error.d.ts.map +1 -0
- package/dist/usage-error.js +51 -0
- package/dist/usage-error.js.map +1 -0
- package/dist/utils/address.d.ts +8 -0
- package/dist/utils/address.d.ts.map +1 -0
- package/dist/utils/address.js +20 -0
- package/dist/utils/address.js.map +1 -0
- package/dist/utils/debug.js +1 -1
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/deep-array.test.js +12 -12
- package/dist/utils/deep-array.test.js.map +1 -1
- package/dist/utils/function.js +5 -5
- package/dist/utils/function.js.map +1 -1
- package/dist/utils/function.test.js +4 -4
- package/dist/utils/function.test.js.map +1 -1
- package/dist/utils/is-nullish.test.js +6 -6
- package/dist/utils/is-nullish.test.js.map +1 -1
- package/dist/utils/itemize.js +1 -1
- package/dist/utils/itemize.js.map +1 -1
- package/dist/utils/log.d.ts +2 -0
- package/dist/utils/log.d.ts.map +1 -1
- package/dist/utils/log.js +22 -4
- package/dist/utils/log.js.map +1 -1
- package/dist/utils/make-non-enumerable.d.ts +1 -1
- package/dist/utils/make-non-enumerable.d.ts.map +1 -1
- package/dist/utils/make-non-enumerable.js +0 -1
- package/dist/utils/make-non-enumerable.js.map +1 -1
- package/dist/utils/parse-type-id.js +1 -1
- package/dist/utils/parse-type-id.js.map +1 -1
- package/dist/utils/parse-type-id.test.js +2 -2
- package/dist/utils/parse-type-id.test.js.map +1 -1
- package/dist/utils/type-id.js +1 -1
- package/dist/utils/type-id.js.map +1 -1
- package/dist/validate/error.d.ts.map +1 -1
- package/dist/validate/error.js +2 -67
- package/dist/validate/error.js.map +1 -1
- package/dist/validate/index.d.ts +4 -2
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +7 -2
- package/dist/validate/index.js.map +1 -1
- package/dist/validate/overrides.d.ts +1 -2
- package/dist/validate/overrides.d.ts.map +1 -1
- package/dist/validate/overrides.js +7 -18
- package/dist/validate/overrides.js.map +1 -1
- package/dist/validate/query.d.ts +3 -1
- package/dist/validate/query.d.ts.map +1 -1
- package/dist/validate/query.js +45 -19
- package/dist/validate/query.js.map +1 -1
- package/dist/validate/query.test.js +4 -4
- package/dist/validate/query.test.js.map +1 -1
- package/dist/validate/report.d.ts +8 -0
- package/dist/validate/report.d.ts.map +1 -0
- package/dist/validate/report.js +79 -0
- package/dist/validate/report.js.map +1 -0
- package/dist/validate/run.js +15 -15
- package/dist/validate/run.js.map +1 -1
- package/dist/validate.test.js +7 -6
- package/dist/validate.test.js.map +1 -1
- package/dist/version.d.ts +3 -3
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +14 -8
- package/dist/version.js.map +1 -1
- package/dist/version.test.js +11 -11
- package/dist/version.test.js.map +1 -1
- package/package.json +11 -6
- package/src/beacon.ts +20 -0
- package/src/eip-1967-type.ts +35 -0
- package/src/eip-1967.ts +16 -12
- package/src/impl-address.ts +76 -0
- package/src/impl-store.ts +2 -0
- package/src/index.ts +18 -1
- package/src/manifest.ts +2 -2
- package/src/provider.ts +16 -0
- package/src/proxy-kind.ts +59 -0
- package/src/src-decoder.test.ts +0 -2
- package/src/standalone.test.ts +40 -0
- package/src/standalone.ts +41 -0
- package/src/storage/index.ts +23 -20
- package/src/storage/report.ts +10 -5
- package/src/stub-provider.ts +0 -1
- package/src/usage-error.ts +68 -0
- package/src/utils/address.ts +16 -0
- package/src/utils/log.ts +23 -2
- package/src/utils/make-non-enumerable.ts +1 -2
- package/src/validate/error.ts +5 -69
- package/src/validate/index.ts +6 -1
- package/src/validate/overrides.ts +5 -17
- package/src/validate/query.ts +38 -9
- package/src/validate/report.ts +78 -0
- package/src/validate.test.ts +1 -0
- package/src/version.ts +14 -6
- package/CHANGELOG.md +0 -106
- package/dist/set-proxy-kind.d.ts +0 -5
- package/dist/set-proxy-kind.d.ts.map +0 -1
- package/dist/set-proxy-kind.js +0 -25
- package/dist/set-proxy-kind.js.map +0 -1
- package/src/set-proxy-kind.ts +0 -27
package/dist/version.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const ava_1 = __importDefault(require("ava"));
|
|
|
7
7
|
const hardhat_1 = require("hardhat");
|
|
8
8
|
const version_1 = require("./version");
|
|
9
9
|
const contractBytecode = '01234567890abcdef';
|
|
10
|
-
ava_1.default('same code different formatting produces different metadata', async (t) => {
|
|
10
|
+
(0, ava_1.default)('same code different formatting produces different metadata', async (t) => {
|
|
11
11
|
async function getBytecodeByContractName(contractName) {
|
|
12
12
|
const buildInfo = await hardhat_1.artifacts.getBuildInfo('contracts/test/Version.sol:Greeter');
|
|
13
13
|
if (buildInfo === undefined) {
|
|
@@ -18,17 +18,17 @@ ava_1.default('same code different formatting produces different metadata', asyn
|
|
|
18
18
|
}
|
|
19
19
|
const GreeterBytecode = await getBytecodeByContractName('Greeter');
|
|
20
20
|
const GreeterDifferentFormattingBytecode = await getBytecodeByContractName('GreeterDifferentFormatting');
|
|
21
|
-
t.not(version_1.hashBytecode(GreeterBytecode), version_1.hashBytecode(GreeterDifferentFormattingBytecode));
|
|
22
|
-
t.is(version_1.hashBytecodeWithoutMetadata(GreeterBytecode), version_1.hashBytecodeWithoutMetadata(GreeterDifferentFormattingBytecode));
|
|
21
|
+
t.not((0, version_1.hashBytecode)(GreeterBytecode), (0, version_1.hashBytecode)(GreeterDifferentFormattingBytecode));
|
|
22
|
+
t.is((0, version_1.hashBytecodeWithoutMetadata)(GreeterBytecode), (0, version_1.hashBytecodeWithoutMetadata)(GreeterDifferentFormattingBytecode));
|
|
23
23
|
});
|
|
24
|
-
ava_1.default('removes metadata from Solidity 0.4 bytecode', t => {
|
|
24
|
+
(0, ava_1.default)('removes metadata from Solidity 0.4 bytecode', t => {
|
|
25
25
|
const swarmHash = '69b1869ae52f674ffccdd8f6d35de04d578a778e919a1b41b7a2177668e08e1a';
|
|
26
26
|
const swarmHashWrapper = `65627a7a72305820${swarmHash}`;
|
|
27
27
|
const metadata = `a1${swarmHashWrapper}0029`;
|
|
28
28
|
const bytecode = `0x${contractBytecode}${metadata}`;
|
|
29
|
-
t.is(version_1.hashBytecodeWithoutMetadata(bytecode), version_1.hashBytecode(`0x${contractBytecode}`));
|
|
29
|
+
t.is((0, version_1.hashBytecodeWithoutMetadata)(bytecode), (0, version_1.hashBytecode)(`0x${contractBytecode}`));
|
|
30
30
|
});
|
|
31
|
-
ava_1.default('removes metadata from Solidity ^0.5.9 bytecode', t => {
|
|
31
|
+
(0, ava_1.default)('removes metadata from Solidity ^0.5.9 bytecode', t => {
|
|
32
32
|
// Solidity 0.5.9 includes solc version into the CBOR encoded metadata
|
|
33
33
|
// https://github.com/ethereum/solidity/releases/tag/v0.5.9
|
|
34
34
|
const swarmHash = '69b1869ae52f674ffccdd8f6d35de04d578a778e919a1b41b7a2177668e08e1a';
|
|
@@ -36,18 +36,18 @@ ava_1.default('removes metadata from Solidity ^0.5.9 bytecode', t => {
|
|
|
36
36
|
const solcVersionWrapper = '64736f6c6343000509';
|
|
37
37
|
const metadata = `a2${swarmHashWrapper}${solcVersionWrapper}0032`;
|
|
38
38
|
const bytecode = `0x${contractBytecode}${metadata}`;
|
|
39
|
-
t.is(version_1.hashBytecodeWithoutMetadata(bytecode), version_1.hashBytecode(`0x${contractBytecode}`));
|
|
39
|
+
t.is((0, version_1.hashBytecodeWithoutMetadata)(bytecode), (0, version_1.hashBytecode)(`0x${contractBytecode}`));
|
|
40
40
|
});
|
|
41
|
-
ava_1.default('does not change the bytecode if metadata encoding is invalid', t => {
|
|
41
|
+
(0, ava_1.default)('does not change the bytecode if metadata encoding is invalid', t => {
|
|
42
42
|
const swarmHash = '69b1869ae52f674ffccdd8f6d35de04d578a778e919a1b41b7a2177668e08e1a';
|
|
43
43
|
const invalidSwarmHashWrapper = `a165627a7a72305821${swarmHash}0029`;
|
|
44
44
|
const bytecode = `0x${contractBytecode}${invalidSwarmHashWrapper}`;
|
|
45
|
-
t.is(version_1.hashBytecodeWithoutMetadata(bytecode), version_1.hashBytecode(bytecode));
|
|
45
|
+
t.is((0, version_1.hashBytecodeWithoutMetadata)(bytecode), (0, version_1.hashBytecode)(bytecode));
|
|
46
46
|
});
|
|
47
|
-
ava_1.default('does not change the bytecode if metadata length is not reliable', t => {
|
|
47
|
+
(0, ava_1.default)('does not change the bytecode if metadata length is not reliable', t => {
|
|
48
48
|
const swarmHash = '69b1869ae52f674ffccdd8f6d35de04d578a778e919a1b41b7a2177668e08e1a';
|
|
49
49
|
const invalidSwarmHashWrapper = `a165627a7a72305821${swarmHash}9929`;
|
|
50
50
|
const bytecode = `0x${contractBytecode}${invalidSwarmHashWrapper}`;
|
|
51
|
-
t.is(version_1.hashBytecodeWithoutMetadata(bytecode), version_1.hashBytecode(bytecode));
|
|
51
|
+
t.is((0, version_1.hashBytecodeWithoutMetadata)(bytecode), (0, version_1.hashBytecode)(bytecode));
|
|
52
52
|
});
|
|
53
53
|
//# sourceMappingURL=version.test.js.map
|
package/dist/version.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.test.js","sourceRoot":"","sources":["../src/version.test.ts"],"names":[],"mappings":";;;;;AAAA,8CAAuB;AACvB,qCAAoC;AACpC,uCAAsE;AAEtE,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAE7C,aAAI,
|
|
1
|
+
{"version":3,"file":"version.test.js","sourceRoot":"","sources":["../src/version.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openzeppelin/upgrades-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/core",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,14 +13,17 @@
|
|
|
13
13
|
"/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json",
|
|
14
14
|
"/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json",
|
|
15
15
|
"/contracts/Initializable.sol",
|
|
16
|
+
"/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol",
|
|
17
|
+
"/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol",
|
|
16
18
|
"/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol",
|
|
17
19
|
"/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json ",
|
|
18
20
|
"/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json"
|
|
19
21
|
],
|
|
20
22
|
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
23
|
+
"clean": "hardhat clean && rimraf dist *.tsbuildinfo",
|
|
24
|
+
"compile": "tsc -b && yarn compile:contracts",
|
|
25
|
+
"compile:contracts": "hardhat compile",
|
|
26
|
+
"prepare": "yarn clean && yarn compile",
|
|
24
27
|
"prepublishOnly": "bash scripts/copy-artifacts.sh",
|
|
25
28
|
"test": "tsc -b && hardhat compile --force && ava",
|
|
26
29
|
"test:watch": "hardhat compile --force && fgbg 'ava --watch' 'tsc -b --watch' --",
|
|
@@ -31,6 +34,7 @@
|
|
|
31
34
|
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
|
32
35
|
"@nomiclabs/hardhat-etherscan": "^2.1.0",
|
|
33
36
|
"@openzeppelin/contracts": "4.1.0",
|
|
37
|
+
"@openzeppelin/contracts-upgradeable": "4.1.0",
|
|
34
38
|
"@types/bn.js": "^5.0.0",
|
|
35
39
|
"@types/cbor": "^5.0.0",
|
|
36
40
|
"@types/debug": "^4.1.5",
|
|
@@ -49,10 +53,11 @@
|
|
|
49
53
|
"bn.js": "^5.1.2",
|
|
50
54
|
"cbor": "^8.0.0",
|
|
51
55
|
"chalk": "^4.1.0",
|
|
52
|
-
"compare-versions": "^
|
|
56
|
+
"compare-versions": "^4.0.0",
|
|
53
57
|
"debug": "^4.1.1",
|
|
54
58
|
"ethereumjs-util": "^7.0.3",
|
|
55
59
|
"proper-lockfile": "^4.1.1",
|
|
56
60
|
"solidity-ast": "^0.4.15"
|
|
57
|
-
}
|
|
61
|
+
},
|
|
62
|
+
"gitHead": "a32bd11a82ede9136d18de4cf0a1d18f44127103"
|
|
58
63
|
}
|
package/src/beacon.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { InvalidBeacon, getImplementationAddressFromBeacon } from './impl-address';
|
|
2
|
+
import { EthereumProvider } from './provider';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks if the address looks like a beacon.
|
|
6
|
+
*
|
|
7
|
+
* @returns true if the address has an implementation() function that returns an address, false otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export async function isBeacon(provider: EthereumProvider, beaconAddress: string): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
await getImplementationAddressFromBeacon(provider, beaconAddress);
|
|
12
|
+
return true;
|
|
13
|
+
} catch (e: any) {
|
|
14
|
+
if (e instanceof InvalidBeacon) {
|
|
15
|
+
return false;
|
|
16
|
+
} else {
|
|
17
|
+
throw e;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EIP1967BeaconNotFound,
|
|
3
|
+
EIP1967ImplementationNotFound,
|
|
4
|
+
getBeaconAddress,
|
|
5
|
+
getImplementationAddress,
|
|
6
|
+
} from './eip-1967';
|
|
7
|
+
import { EthereumProvider } from './provider';
|
|
8
|
+
|
|
9
|
+
export async function isTransparentOrUUPSProxy(provider: EthereumProvider, address: string): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
await getImplementationAddress(provider, address);
|
|
12
|
+
// if an exception was not encountered above, then this address is a transparent/uups proxy
|
|
13
|
+
return true;
|
|
14
|
+
} catch (e: any) {
|
|
15
|
+
if (e instanceof EIP1967ImplementationNotFound) {
|
|
16
|
+
return false;
|
|
17
|
+
} else {
|
|
18
|
+
throw e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function isBeaconProxy(provider: EthereumProvider, address: string): Promise<boolean> {
|
|
24
|
+
try {
|
|
25
|
+
await getBeaconAddress(provider, address);
|
|
26
|
+
// if an exception was not encountered above, then this address is a beacon proxy
|
|
27
|
+
return true;
|
|
28
|
+
} catch (e: any) {
|
|
29
|
+
if (e instanceof EIP1967BeaconNotFound) {
|
|
30
|
+
return false;
|
|
31
|
+
} else {
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/eip-1967.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { toChecksumAddress, keccak256 } from 'ethereumjs-util';
|
|
2
1
|
import BN from 'bn.js';
|
|
3
|
-
|
|
2
|
+
import { keccak256 } from 'ethereumjs-util';
|
|
3
|
+
import { UpgradesError } from './error';
|
|
4
4
|
import { EthereumProvider, getStorageAt } from './provider';
|
|
5
|
+
import { parseAddress } from './utils/address';
|
|
6
|
+
|
|
7
|
+
export class EIP1967ImplementationNotFound extends UpgradesError {}
|
|
8
|
+
|
|
9
|
+
export class EIP1967BeaconNotFound extends UpgradesError {}
|
|
5
10
|
|
|
6
11
|
export async function getAdminAddress(provider: EthereumProvider, address: string): Promise<string> {
|
|
7
12
|
const storage = await getStorageFallback(
|
|
@@ -11,7 +16,7 @@ export async function getAdminAddress(provider: EthereumProvider, address: strin
|
|
|
11
16
|
toFallbackEip1967Hash('org.zeppelinos.proxy.admin'),
|
|
12
17
|
);
|
|
13
18
|
|
|
14
|
-
return
|
|
19
|
+
return parseAddressFromStorage(storage);
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export async function getImplementationAddress(provider: EthereumProvider, address: string): Promise<string> {
|
|
@@ -23,20 +28,20 @@ export async function getImplementationAddress(provider: EthereumProvider, addre
|
|
|
23
28
|
);
|
|
24
29
|
|
|
25
30
|
if (isEmptySlot(storage)) {
|
|
26
|
-
throw new
|
|
31
|
+
throw new EIP1967ImplementationNotFound(`Contract at ${address} doesn't look like an administered ERC 1967 proxy`);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
return
|
|
34
|
+
return parseAddressFromStorage(storage);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
export async function getBeaconAddress(provider: EthereumProvider, address: string): Promise<string> {
|
|
33
38
|
const storage = await getStorageFallback(provider, address, toEip1967Hash('eip1967.proxy.beacon'));
|
|
34
39
|
|
|
35
40
|
if (isEmptySlot(storage)) {
|
|
36
|
-
throw new
|
|
41
|
+
throw new EIP1967BeaconNotFound(`Contract at ${address} doesn't look like an ERC 1967 beacon proxy`);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
return
|
|
44
|
+
return parseAddressFromStorage(storage);
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
async function getStorageFallback(provider: EthereumProvider, address: string, ...slots: string[]): Promise<string> {
|
|
@@ -67,11 +72,10 @@ function isEmptySlot(storage: string): boolean {
|
|
|
67
72
|
return new BN(storage, 'hex').isZero();
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
75
|
+
function parseAddressFromStorage(storage: string): string {
|
|
76
|
+
const address = parseAddress(storage);
|
|
77
|
+
if (address === undefined) {
|
|
73
78
|
throw new Error(`Value in storage is not an address (${storage})`);
|
|
74
79
|
}
|
|
75
|
-
|
|
76
|
-
return toChecksumAddress(address);
|
|
80
|
+
return address;
|
|
77
81
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { keccak256 } from 'ethereumjs-util';
|
|
2
|
+
import {
|
|
3
|
+
call,
|
|
4
|
+
EIP1967BeaconNotFound,
|
|
5
|
+
EIP1967ImplementationNotFound,
|
|
6
|
+
getBeaconAddress,
|
|
7
|
+
getImplementationAddress,
|
|
8
|
+
UpgradesError,
|
|
9
|
+
} from '.';
|
|
10
|
+
|
|
11
|
+
import { EthereumProvider } from './provider';
|
|
12
|
+
import { parseAddress } from './utils/address';
|
|
13
|
+
|
|
14
|
+
export class InvalidBeacon extends UpgradesError {}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gets the implementation address from the beacon using its implementation() function.
|
|
18
|
+
* @param provider
|
|
19
|
+
* @param beaconAddress
|
|
20
|
+
* @returns The implementation address.
|
|
21
|
+
* @throws {InvalidBeacon} If the implementation() function could not be called or does not return an address.
|
|
22
|
+
*/
|
|
23
|
+
export async function getImplementationAddressFromBeacon(
|
|
24
|
+
provider: EthereumProvider,
|
|
25
|
+
beaconAddress: string,
|
|
26
|
+
): Promise<string> {
|
|
27
|
+
const implementationFunction = '0x' + keccak256(Buffer.from('implementation()')).toString('hex').slice(0, 8);
|
|
28
|
+
let result: string | undefined;
|
|
29
|
+
try {
|
|
30
|
+
const implAddress = await call(provider, beaconAddress, implementationFunction);
|
|
31
|
+
result = parseAddress(implAddress);
|
|
32
|
+
} catch (e: any) {
|
|
33
|
+
if (
|
|
34
|
+
!(
|
|
35
|
+
e.message.includes('function selector was not recognized') ||
|
|
36
|
+
e.message.includes('invalid opcode') ||
|
|
37
|
+
e.message.includes('Transaction reverted')
|
|
38
|
+
)
|
|
39
|
+
) {
|
|
40
|
+
throw e;
|
|
41
|
+
} // otherwise fall through with no result
|
|
42
|
+
}
|
|
43
|
+
if (result === undefined) {
|
|
44
|
+
throw new InvalidBeacon(`Contract at ${beaconAddress} doesn't look like a beacon`);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets the implementation address from a UUPS/Transparent/Beacon proxy.
|
|
51
|
+
*
|
|
52
|
+
* @returns a Promise with the implementation address, or undefined if a UUPS/Transparent/Beacon proxy is not located at the address.
|
|
53
|
+
*/
|
|
54
|
+
export async function getImplementationAddressFromProxy(
|
|
55
|
+
provider: EthereumProvider,
|
|
56
|
+
proxyAddress: string,
|
|
57
|
+
): Promise<string | undefined> {
|
|
58
|
+
try {
|
|
59
|
+
return await getImplementationAddress(provider, proxyAddress);
|
|
60
|
+
} catch (e: any) {
|
|
61
|
+
if (e instanceof EIP1967ImplementationNotFound) {
|
|
62
|
+
try {
|
|
63
|
+
const beaconAddress = await getBeaconAddress(provider, proxyAddress);
|
|
64
|
+
return await getImplementationAddressFromBeacon(provider, beaconAddress);
|
|
65
|
+
} catch (e: any) {
|
|
66
|
+
if (e instanceof EIP1967BeaconNotFound) {
|
|
67
|
+
return undefined;
|
|
68
|
+
} else {
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/impl-store.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Manifest, ManifestData, ImplDeployment } from './manifest';
|
|
|
3
3
|
import { EthereumProvider, isDevelopmentNetwork } from './provider';
|
|
4
4
|
import { Deployment, InvalidDeployment, resumeOrDeploy, waitAndValidateDeployment } from './deployment';
|
|
5
5
|
import type { Version } from './version';
|
|
6
|
+
import assert from 'assert';
|
|
6
7
|
|
|
7
8
|
interface ManifestLens<T> {
|
|
8
9
|
description: string;
|
|
@@ -46,6 +47,7 @@ async function fetchOrDeployGeneric<T extends Deployment>(
|
|
|
46
47
|
// If we run into a deployment error, we remove it from the manifest.
|
|
47
48
|
if (e instanceof InvalidDeployment) {
|
|
48
49
|
await manifest.lockedRun(async () => {
|
|
50
|
+
assert(e instanceof InvalidDeployment); // Not sure why this is needed but otherwise doesn't type
|
|
49
51
|
const data = await manifest.read();
|
|
50
52
|
const deployment = lens(data);
|
|
51
53
|
const stored = deployment.get();
|
package/src/index.ts
CHANGED
|
@@ -9,10 +9,27 @@ export * from './src-decoder';
|
|
|
9
9
|
export * from './solc-api';
|
|
10
10
|
export * from './deployment';
|
|
11
11
|
export * from './link-refs';
|
|
12
|
+
export * from './error';
|
|
12
13
|
|
|
13
14
|
export { getStorageLayoutForAddress } from './manifest-storage-layout';
|
|
14
15
|
|
|
15
16
|
export * from './scripts/migrate-oz-cli-project';
|
|
16
17
|
|
|
17
18
|
export { logWarning } from './utils/log';
|
|
18
|
-
export { setProxyKind } from './
|
|
19
|
+
export { setProxyKind, processProxyKind } from './proxy-kind';
|
|
20
|
+
|
|
21
|
+
export { UpgradeableContract } from './standalone';
|
|
22
|
+
|
|
23
|
+
export { isTransparentOrUUPSProxy, isBeaconProxy } from './eip-1967-type';
|
|
24
|
+
export { getImplementationAddressFromBeacon, getImplementationAddressFromProxy } from './impl-address';
|
|
25
|
+
export { isBeacon } from './beacon';
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
BeaconProxyUnsupportedError,
|
|
29
|
+
LoadProxyUnsupportedError,
|
|
30
|
+
PrepareUpgradeUnsupportedError,
|
|
31
|
+
DeployBeaconProxyUnsupportedError,
|
|
32
|
+
DeployBeaconProxyImplUnknownError,
|
|
33
|
+
DeployBeaconProxyKindError,
|
|
34
|
+
assertNotProxy,
|
|
35
|
+
} from './usage-error';
|
package/src/manifest.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface ImplDeployment extends Deployment {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface ProxyDeployment extends Deployment {
|
|
28
|
-
kind: 'uups' | 'transparent';
|
|
28
|
+
kind: 'uups' | 'transparent' | 'beacon';
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function defaultManifest(): ManifestData {
|
|
@@ -90,7 +90,7 @@ export class Manifest {
|
|
|
90
90
|
try {
|
|
91
91
|
const data = JSON.parse(await fs.readFile(this.file, 'utf8')) as ManifestData;
|
|
92
92
|
return validateOrUpdateManifestVersion(data);
|
|
93
|
-
} catch (e) {
|
|
93
|
+
} catch (e: any) {
|
|
94
94
|
if (e.code === 'ENOENT') {
|
|
95
95
|
return defaultManifest();
|
|
96
96
|
} else {
|
package/src/provider.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface EthereumProvider {
|
|
|
5
5
|
send(method: 'net_version', params: []): Promise<string>;
|
|
6
6
|
send(method: 'eth_chainId', params: []): Promise<string>;
|
|
7
7
|
send(method: 'eth_getCode', params: [string, string]): Promise<string>;
|
|
8
|
+
send(method: 'eth_call', params: unknown[]): Promise<string>;
|
|
8
9
|
send(method: 'eth_getStorageAt', params: [string, string, string]): Promise<string>;
|
|
9
10
|
send(method: 'eth_getTransactionByHash', params: [string]): Promise<null | EthereumTransaction>;
|
|
10
11
|
send(method: 'eth_getTransactionReceipt', params: [string]): Promise<null | EthereumTransactionReceipt>;
|
|
@@ -53,6 +54,21 @@ export async function getCode(provider: EthereumProvider, address: string, block
|
|
|
53
54
|
return provider.send('eth_getCode', [address, block]);
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
export async function call(
|
|
58
|
+
provider: EthereumProvider,
|
|
59
|
+
address: string,
|
|
60
|
+
data: string,
|
|
61
|
+
block = 'latest',
|
|
62
|
+
): Promise<string> {
|
|
63
|
+
return provider.send('eth_call', [
|
|
64
|
+
{
|
|
65
|
+
to: address,
|
|
66
|
+
data: data,
|
|
67
|
+
},
|
|
68
|
+
block,
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
export async function hasCode(provider: EthereumProvider, address: string, block?: string): Promise<boolean> {
|
|
57
73
|
const code = await getCode(provider, address, block);
|
|
58
74
|
return code !== '0x';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EthereumProvider } from './provider';
|
|
2
|
+
import { inferProxyKind, ValidationData, ValidationOptions } from './validate';
|
|
3
|
+
import { Manifest, DeploymentNotFound, ProxyDeployment } from './manifest';
|
|
4
|
+
import { isBeaconProxy } from './eip-1967-type';
|
|
5
|
+
import { Version } from './version';
|
|
6
|
+
import { BeaconProxyUnsupportedError } from './usage-error';
|
|
7
|
+
|
|
8
|
+
export async function setProxyKind(
|
|
9
|
+
provider: EthereumProvider,
|
|
10
|
+
proxyAddress: string,
|
|
11
|
+
opts: ValidationOptions,
|
|
12
|
+
): Promise<ProxyDeployment['kind']> {
|
|
13
|
+
const manifest = await Manifest.forNetwork(provider);
|
|
14
|
+
|
|
15
|
+
const manifestDeployment = await manifest.getProxyFromAddress(proxyAddress).catch(e => {
|
|
16
|
+
if (e instanceof DeploymentNotFound) {
|
|
17
|
+
return undefined;
|
|
18
|
+
} else {
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (opts.kind === undefined) {
|
|
24
|
+
opts.kind = manifestDeployment?.kind ?? 'transparent';
|
|
25
|
+
} else if (manifestDeployment && opts.kind !== manifestDeployment.kind) {
|
|
26
|
+
throw new Error(`Requested an upgrade of kind ${opts.kind} but proxy is ${manifestDeployment.kind}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return opts.kind;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Processes opts.kind when deploying the implementation for a UUPS or Transparent proxy.
|
|
34
|
+
*
|
|
35
|
+
* @throws {BeaconProxyUnsupportedError} If this function is called for a Beacon proxy.
|
|
36
|
+
*/
|
|
37
|
+
export async function processProxyKind(
|
|
38
|
+
provider: EthereumProvider,
|
|
39
|
+
proxyAddress: string | undefined,
|
|
40
|
+
opts: ValidationOptions,
|
|
41
|
+
data: ValidationData,
|
|
42
|
+
version: Version,
|
|
43
|
+
) {
|
|
44
|
+
if (opts.kind === undefined) {
|
|
45
|
+
if (proxyAddress !== undefined && (await isBeaconProxy(provider, proxyAddress))) {
|
|
46
|
+
opts.kind = 'beacon';
|
|
47
|
+
} else {
|
|
48
|
+
opts.kind = inferProxyKind(data, version);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (proxyAddress !== undefined) {
|
|
53
|
+
await setProxyKind(provider, proxyAddress, opts);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (opts.kind === 'beacon') {
|
|
57
|
+
throw new BeaconProxyUnsupportedError();
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/src-decoder.test.ts
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import _test, { TestInterface } from 'ava';
|
|
2
|
+
import { artifacts } from 'hardhat';
|
|
3
|
+
|
|
4
|
+
import { SolcInput, SolcOutput } from './solc-api';
|
|
5
|
+
import { UpgradeableContract } from './standalone';
|
|
6
|
+
|
|
7
|
+
interface Context {
|
|
8
|
+
solcInput: SolcInput;
|
|
9
|
+
solcOutput: SolcOutput;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const test = _test as TestInterface<Context>;
|
|
13
|
+
|
|
14
|
+
test.before(async t => {
|
|
15
|
+
const buildInfo = await artifacts.getBuildInfo('contracts/test/Standalone.sol:StandaloneV1');
|
|
16
|
+
if (buildInfo === undefined) {
|
|
17
|
+
throw new Error('Build info not found');
|
|
18
|
+
}
|
|
19
|
+
t.context.solcInput = buildInfo.input;
|
|
20
|
+
t.context.solcOutput = buildInfo.output;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('reports unsafe operation', t => {
|
|
24
|
+
const impl = new UpgradeableContract('StandaloneV1', t.context.solcInput, t.context.solcOutput);
|
|
25
|
+
const report = impl.getErrorReport();
|
|
26
|
+
t.false(report.ok);
|
|
27
|
+
t.true(report.errors[0].kind === 'delegatecall');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('reports storage upgrade errors', t => {
|
|
31
|
+
const v1 = new UpgradeableContract('StandaloneV1', t.context.solcInput, t.context.solcOutput);
|
|
32
|
+
|
|
33
|
+
const v2Good = new UpgradeableContract('StandaloneV2Good', t.context.solcInput, t.context.solcOutput);
|
|
34
|
+
const goodReport = v1.getStorageUpgradeReport(v2Good);
|
|
35
|
+
t.true(goodReport.ok);
|
|
36
|
+
|
|
37
|
+
const v2Bad = new UpgradeableContract('StandaloneV2Bad', t.context.solcInput, t.context.solcOutput);
|
|
38
|
+
const badReport = v1.getStorageUpgradeReport(v2Bad);
|
|
39
|
+
t.false(badReport.ok);
|
|
40
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SolcOutput, SolcInput } from './solc-api';
|
|
2
|
+
import { solcInputOutputDecoder } from './src-decoder';
|
|
3
|
+
import {
|
|
4
|
+
getContractVersion,
|
|
5
|
+
getErrors,
|
|
6
|
+
getStorageLayout,
|
|
7
|
+
UpgradeableContractErrorReport,
|
|
8
|
+
validate,
|
|
9
|
+
ValidationOptions,
|
|
10
|
+
withValidationDefaults,
|
|
11
|
+
} from './validate';
|
|
12
|
+
import { getStorageUpgradeReport, StorageLayout } from './storage';
|
|
13
|
+
import { Version } from './version';
|
|
14
|
+
import { ValidationError } from './validate/run';
|
|
15
|
+
|
|
16
|
+
export interface Report {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
explain(color?: boolean): string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class UpgradeableContract {
|
|
22
|
+
readonly version: Version;
|
|
23
|
+
readonly errors: ValidationError[];
|
|
24
|
+
readonly layout: StorageLayout;
|
|
25
|
+
|
|
26
|
+
constructor(readonly name: string, solcInput: SolcInput, solcOutput: SolcOutput, opts: ValidationOptions = {}) {
|
|
27
|
+
const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput);
|
|
28
|
+
const validation = validate(solcOutput, decodeSrc);
|
|
29
|
+
this.version = getContractVersion(validation, name);
|
|
30
|
+
this.errors = getErrors(validation, this.version, opts);
|
|
31
|
+
this.layout = getStorageLayout(validation, this.version);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getErrorReport() {
|
|
35
|
+
return new UpgradeableContractErrorReport(this.errors);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getStorageUpgradeReport(newVersion: UpgradeableContract, opts: ValidationOptions = {}) {
|
|
39
|
+
return getStorageUpgradeReport(this.layout, newVersion.layout, withValidationDefaults(opts));
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/storage/index.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
1
|
export * from './compat';
|
|
4
2
|
|
|
5
3
|
import { UpgradesError } from '../error';
|
|
6
4
|
import { StorageLayout, getDetailedLayout } from './layout';
|
|
7
5
|
import { StorageOperation, StorageItem, StorageLayoutComparator } from './compare';
|
|
8
6
|
import { LayoutCompatibilityReport } from './report';
|
|
9
|
-
import { ValidationOptions,
|
|
10
|
-
import { logWarning } from '../utils/log';
|
|
7
|
+
import { ValidationOptions, withValidationDefaults } from '../validate/overrides';
|
|
8
|
+
import { logNote, logWarning } from '../utils/log';
|
|
11
9
|
|
|
12
10
|
export function assertStorageUpgradeSafe(
|
|
13
11
|
original: StorageLayout,
|
|
@@ -31,31 +29,36 @@ export function assertStorageUpgradeSafe(
|
|
|
31
29
|
opts = withValidationDefaults({ unsafeAllowCustomTypes });
|
|
32
30
|
}
|
|
33
31
|
|
|
32
|
+
const report = getStorageUpgradeReport(original, updated, opts);
|
|
33
|
+
|
|
34
|
+
if (!report.pass) {
|
|
35
|
+
throw new StorageUpgradeErrors(report);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getStorageUpgradeReport(
|
|
40
|
+
original: StorageLayout,
|
|
41
|
+
updated: StorageLayout,
|
|
42
|
+
opts: Required<ValidationOptions>,
|
|
43
|
+
): LayoutCompatibilityReport {
|
|
34
44
|
const originalDetailed = getDetailedLayout(original);
|
|
35
45
|
const updatedDetailed = getDetailedLayout(updated);
|
|
36
46
|
const comparator = new StorageLayoutComparator(opts.unsafeAllowCustomTypes, opts.unsafeAllowRenames);
|
|
37
47
|
const report = comparator.compareLayouts(originalDetailed, updatedDetailed);
|
|
38
48
|
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.error(
|
|
47
|
-
chalk.yellow.bold('Note:') +
|
|
48
|
-
` \`unsafeAllowCustomTypes\` is no longer necessary. Structs are enums are automatically checked.\n`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
49
|
+
if (comparator.hasAllowedUncheckedCustomTypes) {
|
|
50
|
+
logWarning(`Potentially unsafe deployment`, [
|
|
51
|
+
`You are using \`unsafeAllowCustomTypes\` to force approve structs or enums with missing data.`,
|
|
52
|
+
`Make sure you have manually checked the storage layout for incompatibilities.`,
|
|
53
|
+
]);
|
|
54
|
+
} else if (opts.unsafeAllowCustomTypes) {
|
|
55
|
+
logNote(`\`unsafeAllowCustomTypes\` is no longer necessary. Structs are enums are automatically checked.\n`);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
throw new StorageUpgradeErrors(report);
|
|
55
|
-
}
|
|
58
|
+
return report;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
class StorageUpgradeErrors extends UpgradesError {
|
|
61
|
+
export class StorageUpgradeErrors extends UpgradesError {
|
|
59
62
|
constructor(readonly report: LayoutCompatibilityReport) {
|
|
60
63
|
super(`New storage layout is incompatible`, () => report.explain());
|
|
61
64
|
}
|
package/src/storage/report.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _chalk from 'chalk';
|
|
2
2
|
|
|
3
3
|
import type { BasicOperation } from '../levenshtein';
|
|
4
4
|
import type { ParsedTypeDetailed } from './layout';
|
|
@@ -8,13 +8,18 @@ import { indent } from '../utils/indent';
|
|
|
8
8
|
import { assert } from '../utils/assert';
|
|
9
9
|
|
|
10
10
|
export class LayoutCompatibilityReport {
|
|
11
|
-
readonly
|
|
11
|
+
constructor(readonly ops: StorageOperation<StorageItem>[]) {}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
this.pass
|
|
13
|
+
get ok(): boolean {
|
|
14
|
+
return this.pass;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
get pass(): boolean {
|
|
18
|
+
return this.ops.length === 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
explain(color = true): string {
|
|
22
|
+
const chalk = new _chalk.Instance({ level: color && _chalk.supportsColor ? _chalk.supportsColor.level : 0 });
|
|
18
23
|
const res = [];
|
|
19
24
|
|
|
20
25
|
for (const op of this.ops) {
|