@openzeppelin/upgrades-core 1.26.1 → 1.27.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/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json +126 -0
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +9 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/cli.test.d.ts +2 -0
- package/dist/cli/cli.test.d.ts.map +1 -0
- package/dist/cli/cli.test.js +52 -0
- package/dist/cli/cli.test.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/validate/build-info-file.d.ts +22 -0
- package/dist/cli/validate/build-info-file.d.ts.map +1 -0
- package/dist/cli/validate/build-info-file.js +110 -0
- package/dist/cli/validate/build-info-file.js.map +1 -0
- package/dist/cli/validate/build-info-file.test.d.ts +2 -0
- package/dist/cli/validate/build-info-file.test.d.ts.map +1 -0
- package/dist/cli/validate/build-info-file.test.js +175 -0
- package/dist/cli/validate/build-info-file.test.js.map +1 -0
- package/dist/cli/validate/contract-report.d.ts +31 -0
- package/dist/cli/validate/contract-report.d.ts.map +1 -0
- package/dist/cli/validate/contract-report.js +107 -0
- package/dist/cli/validate/contract-report.js.map +1 -0
- package/dist/cli/validate/error.d.ts +4 -0
- package/dist/cli/validate/error.d.ts.map +1 -0
- package/dist/cli/validate/error.js +8 -0
- package/dist/cli/validate/error.js.map +1 -0
- package/dist/cli/validate/project-report.d.ts +18 -0
- package/dist/cli/validate/project-report.d.ts.map +1 -0
- package/dist/cli/validate/project-report.js +42 -0
- package/dist/cli/validate/project-report.js.map +1 -0
- package/dist/cli/validate/project-report.test.d.ts +2 -0
- package/dist/cli/validate/project-report.test.d.ts.map +1 -0
- package/dist/cli/validate/project-report.test.js +90 -0
- package/dist/cli/validate/project-report.test.js.map +1 -0
- package/dist/cli/validate/upgradeability-assessment.d.ts +19 -0
- package/dist/cli/validate/upgradeability-assessment.d.ts.map +1 -0
- package/dist/cli/validate/upgradeability-assessment.js +103 -0
- package/dist/cli/validate/upgradeability-assessment.js.map +1 -0
- package/dist/cli/validate/validate-upgrade-safety.d.ts +16 -0
- package/dist/cli/validate/validate-upgrade-safety.d.ts.map +1 -0
- package/dist/cli/validate/validate-upgrade-safety.js +23 -0
- package/dist/cli/validate/validate-upgrade-safety.js.map +1 -0
- package/dist/cli/validate/validate-upgrade-safety.test.d.ts +2 -0
- package/dist/cli/validate/validate-upgrade-safety.test.d.ts.map +1 -0
- package/dist/cli/validate/validate-upgrade-safety.test.js +74 -0
- package/dist/cli/validate/validate-upgrade-safety.test.js.map +1 -0
- package/dist/cli/validate/validations.d.ts +11 -0
- package/dist/cli/validate/validations.d.ts.map +1 -0
- package/dist/cli/validate/validations.js +40 -0
- package/dist/cli/validate/validations.js.map +1 -0
- package/dist/cli/validate/validations.test.d.ts +2 -0
- package/dist/cli/validate/validations.test.d.ts.map +1 -0
- package/dist/cli/validate/validations.test.js +64 -0
- package/dist/cli/validate/validations.test.js.map +1 -0
- package/dist/cli/validate.d.ts +16 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +119 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/cli/validate.test.d.ts +2 -0
- package/dist/cli/validate.test.d.ts.map +1 -0
- package/dist/cli/validate.test.js +108 -0
- package/dist/cli/validate.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.test.js +2 -1
- package/dist/manifest.test.js.map +1 -1
- package/dist/utils/annotations.d.ts +16 -0
- package/dist/utils/annotations.d.ts.map +1 -0
- package/dist/utils/annotations.js +46 -0
- package/dist/utils/annotations.js.map +1 -0
- package/dist/utils/contract-name.d.ts +2 -0
- package/dist/utils/contract-name.d.ts.map +1 -0
- package/dist/utils/contract-name.js +8 -0
- package/dist/utils/contract-name.js.map +1 -0
- package/dist/validate/index.d.ts +1 -1
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +2 -1
- package/dist/validate/index.js.map +1 -1
- package/dist/validate/query.d.ts +8 -1
- package/dist/validate/query.d.ts.map +1 -1
- package/dist/validate/query.js +16 -3
- package/dist/validate/query.js.map +1 -1
- package/dist/validate/run.d.ts +1 -4
- package/dist/validate/run.d.ts.map +1 -1
- package/dist/validate/run.js +8 -38
- package/dist/validate/run.js.map +1 -1
- package/package.json +9 -3
- package/src/cli/cli.test.ts +57 -0
- package/src/cli/cli.test.ts.md +138 -0
- package/src/cli/cli.test.ts.snap +0 -0
- package/src/cli/cli.ts +9 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/validate/build-info-file.test.ts +211 -0
- package/src/cli/validate/build-info-file.ts +145 -0
- package/src/cli/validate/contract-report.ts +130 -0
- package/src/cli/validate/error.ts +3 -0
- package/src/cli/validate/project-report.test.ts +124 -0
- package/src/cli/validate/project-report.test.ts.md +40 -0
- package/src/cli/validate/project-report.test.ts.snap +0 -0
- package/src/cli/validate/project-report.ts +44 -0
- package/src/cli/validate/upgradeability-assessment.ts +137 -0
- package/src/cli/validate/validate-upgrade-safety.test.ts +102 -0
- package/src/cli/validate/validate-upgrade-safety.test.ts.md +98 -0
- package/src/cli/validate/validate-upgrade-safety.test.ts.snap +0 -0
- package/src/cli/validate/validate-upgrade-safety.ts +29 -0
- package/src/cli/validate/validations.test.ts +72 -0
- package/src/cli/validate/validations.test.ts.md +87 -0
- package/src/cli/validate/validations.test.ts.snap +0 -0
- package/src/cli/validate/validations.ts +53 -0
- package/src/cli/validate.test.ts +112 -0
- package/src/cli/validate.ts +134 -0
- package/src/index.ts +2 -0
- package/src/manifest.test.ts +2 -1
- package/src/utils/annotations.ts +47 -0
- package/src/utils/contract-name.ts +3 -0
- package/src/validate/index.ts +1 -0
- package/src/validate/query.ts +16 -3
- package/src/validate/run.ts +5 -41
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
|
|
3
|
+
import minimist from 'minimist';
|
|
4
|
+
import { getFunctionArgs, withDefaults } from './validate';
|
|
5
|
+
import sinon from 'sinon';
|
|
6
|
+
import { errorKinds } from '../validate/run';
|
|
7
|
+
|
|
8
|
+
test.afterEach.always(() => {
|
|
9
|
+
sinon.restore();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('getFunctionArgs - invalid command', t => {
|
|
13
|
+
const parsedArgs = minimist(['invalid']);
|
|
14
|
+
const extraArgs = parsedArgs._;
|
|
15
|
+
t.throws(() => getFunctionArgs(parsedArgs, extraArgs), {
|
|
16
|
+
message: `Invalid command: invalid. Supported commands are: validate`,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('getFunctionArgs - invalid options', async t => {
|
|
21
|
+
const parsedArgs = minimist(['validate', 'build-info.json', '--foo', '--bar', 'xyz']);
|
|
22
|
+
const extraArgs = parsedArgs._;
|
|
23
|
+
t.throws(() => getFunctionArgs(parsedArgs, extraArgs), {
|
|
24
|
+
message: `Invalid options: foo, bar`,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('getFunctionArgs - command only', t => {
|
|
29
|
+
const parsedArgs = minimist(['validate']);
|
|
30
|
+
const extraArgs = parsedArgs._;
|
|
31
|
+
const functionArgs = getFunctionArgs(parsedArgs, extraArgs);
|
|
32
|
+
if (functionArgs === undefined) {
|
|
33
|
+
t.fail();
|
|
34
|
+
} else {
|
|
35
|
+
t.is(functionArgs.buildInfoDir, undefined);
|
|
36
|
+
t.is(functionArgs.opts.unsafeAllowRenames, false);
|
|
37
|
+
t.is(functionArgs.opts.unsafeSkipStorageCheck, false);
|
|
38
|
+
t.is(functionArgs.opts.unsafeAllowCustomTypes, false);
|
|
39
|
+
t.is(functionArgs.opts.unsafeAllowLinkedLibraries, false);
|
|
40
|
+
t.deepEqual(functionArgs.opts.unsafeAllow, []);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('getFunctionArgs - command with arg', t => {
|
|
45
|
+
const parsedArgs = minimist(['validate', 'build-info.json']);
|
|
46
|
+
const extraArgs = parsedArgs._;
|
|
47
|
+
const functionArgs = getFunctionArgs(parsedArgs, extraArgs);
|
|
48
|
+
if (functionArgs === undefined) {
|
|
49
|
+
t.fail();
|
|
50
|
+
} else {
|
|
51
|
+
t.is(functionArgs.buildInfoDir, 'build-info.json');
|
|
52
|
+
t.is(functionArgs.opts.unsafeAllowRenames, false);
|
|
53
|
+
t.is(functionArgs.opts.unsafeSkipStorageCheck, false);
|
|
54
|
+
t.is(functionArgs.opts.unsafeAllowCustomTypes, false);
|
|
55
|
+
t.is(functionArgs.opts.unsafeAllowLinkedLibraries, false);
|
|
56
|
+
t.deepEqual(functionArgs.opts.unsafeAllow, []);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('withDefaults - empty', t => {
|
|
61
|
+
const parsedArgs = minimist(['validate', 'build-info.json']);
|
|
62
|
+
const opts = withDefaults(parsedArgs);
|
|
63
|
+
t.is(opts.unsafeAllowRenames, false);
|
|
64
|
+
t.is(opts.unsafeSkipStorageCheck, false);
|
|
65
|
+
t.is(opts.unsafeAllowCustomTypes, false);
|
|
66
|
+
t.is(opts.unsafeAllowLinkedLibraries, false);
|
|
67
|
+
t.deepEqual(opts.unsafeAllow, []);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('withDefaults - some', t => {
|
|
71
|
+
const parsedArgs = minimist([
|
|
72
|
+
'validate',
|
|
73
|
+
'build-info.json',
|
|
74
|
+
'--unsafeAllowRenames',
|
|
75
|
+
'--unsafeAllow',
|
|
76
|
+
'selfdestruct, delegatecall,constructor',
|
|
77
|
+
]);
|
|
78
|
+
const opts = withDefaults(parsedArgs);
|
|
79
|
+
t.is(opts.unsafeAllowRenames, true);
|
|
80
|
+
t.is(opts.unsafeSkipStorageCheck, false);
|
|
81
|
+
t.is(opts.unsafeAllowCustomTypes, false);
|
|
82
|
+
t.is(opts.unsafeAllowLinkedLibraries, false);
|
|
83
|
+
t.deepEqual(opts.unsafeAllow, ['selfdestruct', 'delegatecall', 'constructor']);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('withDefaults - all', t => {
|
|
87
|
+
const parsedArgs = minimist([
|
|
88
|
+
'validate',
|
|
89
|
+
'build-info.json',
|
|
90
|
+
'--unsafeAllowRenames',
|
|
91
|
+
'--unsafeSkipStorageCheck',
|
|
92
|
+
'--unsafeAllowCustomTypes',
|
|
93
|
+
'--unsafeAllowLinkedLibraries',
|
|
94
|
+
'--unsafeAllow',
|
|
95
|
+
...errorKinds,
|
|
96
|
+
]);
|
|
97
|
+
const opts = withDefaults(parsedArgs);
|
|
98
|
+
t.is(opts.unsafeAllowRenames, true);
|
|
99
|
+
t.is(opts.unsafeSkipStorageCheck, true);
|
|
100
|
+
t.is(opts.unsafeAllowCustomTypes, true);
|
|
101
|
+
t.is(opts.unsafeAllowLinkedLibraries, true);
|
|
102
|
+
t.true(opts.unsafeAllow.every((kind: string) => errorKinds.includes(kind as typeof errorKinds[number])));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('withDefaults - invalid unsafeAllow', t => {
|
|
106
|
+
const parsedArgs = minimist(['validate', 'build-info.json', '--unsafeAllow', 'foo']);
|
|
107
|
+
t.throws(() => withDefaults(parsedArgs), {
|
|
108
|
+
message: `Invalid option: --unsafeAllow "foo". Supported values for the --unsafeAllow option are: ${errorKinds.join(
|
|
109
|
+
', ',
|
|
110
|
+
)}`,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import minimist from 'minimist';
|
|
2
|
+
|
|
3
|
+
import { ValidateUpgradeSafetyOptions, validateUpgradeSafety } from '.';
|
|
4
|
+
import { withValidationDefaults } from '..';
|
|
5
|
+
import { ValidationError, errorKinds } from '../validate/run';
|
|
6
|
+
import debug from '../utils/debug';
|
|
7
|
+
|
|
8
|
+
const USAGE = 'Usage: npx @openzeppelin/upgrades-core validate [<BUILD_INFO_DIR>] [<OPTIONS>]';
|
|
9
|
+
const DETAILS = `
|
|
10
|
+
Detects upgradeable contracts from a build info directory and validates whether they are upgrade safe.
|
|
11
|
+
|
|
12
|
+
Arguments:
|
|
13
|
+
<BUILD_INFO_DIR> Optional path to the build info directory which contains JSON files with Solidity compiler input and output. Defaults to 'artifacts/build-info' for Hardhat projects or 'out/build-info' for Foundry projects. If your project uses a custom output directory, you must specify its build info directory here.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--unsafeAllow "<VALIDATION_ERRORS>" Selectively disable one or more validation errors. Comma-separated list with one or more of the following: ${errorKinds.join(
|
|
17
|
+
', ',
|
|
18
|
+
)}
|
|
19
|
+
--unsafeAllowRenames Configure storage layout check to allow variable renaming.
|
|
20
|
+
--unsafeSkipStorageCheck Skips checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.`;
|
|
21
|
+
|
|
22
|
+
export async function main(args: string[]): Promise<void> {
|
|
23
|
+
const { parsedArgs, extraArgs } = parseArgs(args);
|
|
24
|
+
|
|
25
|
+
if (!help(parsedArgs, extraArgs)) {
|
|
26
|
+
const functionArgs = getFunctionArgs(parsedArgs, extraArgs);
|
|
27
|
+
const result = await validateUpgradeSafety(functionArgs.buildInfoDir, functionArgs.opts);
|
|
28
|
+
console.log(result.explain());
|
|
29
|
+
process.exitCode = result.ok ? 0 : 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseArgs(args: string[]) {
|
|
34
|
+
const parsedArgs = minimist(args, {
|
|
35
|
+
boolean: [
|
|
36
|
+
'help',
|
|
37
|
+
'unsafeAllowRenames',
|
|
38
|
+
'unsafeSkipStorageCheck',
|
|
39
|
+
'unsafeAllowCustomTypes',
|
|
40
|
+
'unsafeAllowLinkedLibraries',
|
|
41
|
+
],
|
|
42
|
+
string: ['unsafeAllow'],
|
|
43
|
+
alias: { h: 'help' },
|
|
44
|
+
});
|
|
45
|
+
const extraArgs = parsedArgs._;
|
|
46
|
+
debug('parsedArgs', parsedArgs);
|
|
47
|
+
return { parsedArgs, extraArgs };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function help(parsedArgs: minimist.ParsedArgs, extraArgs: string[]): boolean {
|
|
51
|
+
if (extraArgs.length === 0 || parsedArgs['help']) {
|
|
52
|
+
console.log(USAGE);
|
|
53
|
+
console.log(DETAILS);
|
|
54
|
+
return true;
|
|
55
|
+
} else {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface FunctionArgs {
|
|
61
|
+
buildInfoDir?: string;
|
|
62
|
+
opts: Required<ValidateUpgradeSafetyOptions>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets and validates function arguments and options.
|
|
67
|
+
* @returns Function arguments
|
|
68
|
+
* @throws Error if any arguments or options are invalid.
|
|
69
|
+
*/
|
|
70
|
+
export function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: string[]): FunctionArgs {
|
|
71
|
+
if (extraArgs.length === 0) {
|
|
72
|
+
throw new Error('Missing command. Supported commands are: validate');
|
|
73
|
+
} else if (extraArgs[0] !== 'validate') {
|
|
74
|
+
throw new Error(`Invalid command: ${extraArgs[0]}. Supported commands are: validate`);
|
|
75
|
+
} else if (extraArgs.length > 2) {
|
|
76
|
+
throw new Error('The validate command takes only one argument: the build info directory.');
|
|
77
|
+
} else {
|
|
78
|
+
const buildInfoDir = extraArgs.length === 1 ? undefined : extraArgs[1];
|
|
79
|
+
const opts = withDefaults(parsedArgs);
|
|
80
|
+
return { buildInfoDir, opts };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateOptions(parsedArgs: minimist.ParsedArgs) {
|
|
85
|
+
const invalidArgs = Object.keys(parsedArgs).filter(
|
|
86
|
+
key =>
|
|
87
|
+
![
|
|
88
|
+
'help',
|
|
89
|
+
'h',
|
|
90
|
+
'_',
|
|
91
|
+
'unsafeAllowRenames',
|
|
92
|
+
'unsafeSkipStorageCheck',
|
|
93
|
+
'unsafeAllowCustomTypes',
|
|
94
|
+
'unsafeAllowLinkedLibraries',
|
|
95
|
+
'unsafeAllow',
|
|
96
|
+
].includes(key),
|
|
97
|
+
);
|
|
98
|
+
if (invalidArgs.length > 0) {
|
|
99
|
+
throw new Error(`Invalid options: ${invalidArgs.join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getUnsafeAllowKinds(unsafeAllow: string | undefined): ValidationError['kind'][] {
|
|
104
|
+
type errorKindsType = typeof errorKinds[number];
|
|
105
|
+
|
|
106
|
+
if (unsafeAllow === undefined) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const unsafeAllowTokens: string[] = unsafeAllow.split(/[\s,]+/);
|
|
111
|
+
if (unsafeAllowTokens.some(token => !errorKinds.includes(token as errorKindsType))) {
|
|
112
|
+
// This includes empty strings
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Invalid option: --unsafeAllow "${unsafeAllow}". Supported values for the --unsafeAllow option are: ${errorKinds.join(
|
|
115
|
+
', ',
|
|
116
|
+
)}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return unsafeAllowTokens as errorKindsType[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function withDefaults(parsedArgs: minimist.ParsedArgs): Required<ValidateUpgradeSafetyOptions> {
|
|
123
|
+
validateOptions(parsedArgs);
|
|
124
|
+
|
|
125
|
+
const allOpts: Required<ValidateUpgradeSafetyOptions> = {
|
|
126
|
+
unsafeAllowRenames: parsedArgs['unsafeAllowRenames'],
|
|
127
|
+
unsafeSkipStorageCheck: parsedArgs['unsafeSkipStorageCheck'],
|
|
128
|
+
unsafeAllowCustomTypes: parsedArgs['unsafeAllowCustomTypes'],
|
|
129
|
+
unsafeAllowLinkedLibraries: parsedArgs['unsafeAllowLinkedLibraries'],
|
|
130
|
+
unsafeAllow: getUnsafeAllowKinds(parsedArgs['unsafeAllow']),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return withValidationDefaults(allOpts);
|
|
134
|
+
}
|
package/src/index.ts
CHANGED
package/src/manifest.test.ts
CHANGED
|
@@ -104,7 +104,8 @@ test.serial('rename manifest', async t => {
|
|
|
104
104
|
await deleteManifests(t, id);
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
test
|
|
107
|
+
// This test is not working on some Linux configurations ('EXDEV: cross-device link not permitted')
|
|
108
|
+
test.skip('rename hardhat from unknown to dev manifest', async t => {
|
|
108
109
|
const id = 31337;
|
|
109
110
|
|
|
110
111
|
await deleteFile(t, `.openzeppelin/unknown-${id}.json`);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { execall } from './execall';
|
|
2
|
+
import { Node } from 'solidity-ast/node';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get args from the doc string matching the given tag.
|
|
6
|
+
*
|
|
7
|
+
* @param doc The doc string to parse
|
|
8
|
+
* @param tag The tag to match
|
|
9
|
+
* @param supportedArgs The list of supported args, or undefined if all args are supported
|
|
10
|
+
*/
|
|
11
|
+
export function getAnnotationArgs(doc: string, tag: string, supportedArgs?: readonly string[]) {
|
|
12
|
+
const result: string[] = [];
|
|
13
|
+
for (const { groups } of execall(
|
|
14
|
+
/^\s*(?:@(?<title>\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s*@\w+)[^])*)/m,
|
|
15
|
+
doc,
|
|
16
|
+
)) {
|
|
17
|
+
if (groups && groups.title === 'custom' && groups.tag === tag) {
|
|
18
|
+
const trimmedArgs = groups.args.trim();
|
|
19
|
+
if (trimmedArgs.length > 0) {
|
|
20
|
+
result.push(...trimmedArgs.split(/\s+/));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (supportedArgs !== undefined) {
|
|
26
|
+
result.forEach(arg => {
|
|
27
|
+
if (!supportedArgs.includes(arg)) {
|
|
28
|
+
throw new Error(`NatSpec: ${tag} argument not recognized: ${arg}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the documentation string for the given node.
|
|
38
|
+
* @param node The node
|
|
39
|
+
* @returns The documentation string, or an empty string if the node has no documentation
|
|
40
|
+
*/
|
|
41
|
+
export function getDocumentation(node: Node) {
|
|
42
|
+
if ('documentation' in node) {
|
|
43
|
+
return typeof node.documentation === 'string' ? node.documentation : node.documentation?.text ?? '';
|
|
44
|
+
} else {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/validate/index.ts
CHANGED
package/src/validate/query.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Version, getVersion } from '../version';
|
|
2
|
-
import { ValidationRunData, ValidationError, isOpcodeError } from './run';
|
|
2
|
+
import { ValidationRunData, ValidationError, isOpcodeError, ContractValidation } from './run';
|
|
3
3
|
import { StorageLayout } from '../storage/layout';
|
|
4
4
|
import { unlinkBytecode } from '../link-refs';
|
|
5
5
|
import { ValidationOptions, processExceptions } from './overrides';
|
|
@@ -186,13 +186,26 @@ export function isUpgradeSafe(data: ValidationData, version: Version): boolean {
|
|
|
186
186
|
return getErrors(dataV3, version).length == 0;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
export function inferUUPS(runValidation: ValidationRunData, fullContractName: string): boolean {
|
|
190
|
+
const methods = getAllMethods(runValidation, fullContractName);
|
|
191
|
+
return methods.includes(upgradeToSignature);
|
|
192
|
+
}
|
|
193
|
+
|
|
189
194
|
export function inferProxyKind(data: ValidationData, version: Version): ProxyDeployment['kind'] {
|
|
190
195
|
const dataV3 = normalizeValidationData(data);
|
|
191
196
|
const [fullContractName, runValidation] = getContractNameAndRunValidation(dataV3, version);
|
|
192
|
-
|
|
193
|
-
if (methods.includes(upgradeToSignature)) {
|
|
197
|
+
if (inferUUPS(runValidation, fullContractName)) {
|
|
194
198
|
return 'uups';
|
|
195
199
|
} else {
|
|
196
200
|
return 'transparent';
|
|
197
201
|
}
|
|
198
202
|
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Whether the contract inherits any contract named "Initializable"
|
|
206
|
+
* @param contractValidation The validation result for the contract
|
|
207
|
+
* @return true if the contract inheritss any contract whose fully qualified name ends with ":Initializable"
|
|
208
|
+
*/
|
|
209
|
+
export function inferInitializable(contractValidation: ContractValidation) {
|
|
210
|
+
return contractValidation.inherit.some(c => c.endsWith(':Initializable'));
|
|
211
|
+
}
|
package/src/validate/run.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { Version, getVersion } from '../version';
|
|
|
11
11
|
import { extractLinkReferences, LinkReference } from '../link-refs';
|
|
12
12
|
import { extractStorageLayout } from '../storage/extract';
|
|
13
13
|
import { StorageLayout } from '../storage/layout';
|
|
14
|
+
import { getFullyQualifiedName } from '../utils/contract-name';
|
|
15
|
+
import { getAnnotationArgs as getSupportedAnnotationArgs, getDocumentation } from '../utils/annotations';
|
|
14
16
|
|
|
15
17
|
export type ValidationRunData = Record<string, ContractValidation>;
|
|
16
18
|
|
|
@@ -26,7 +28,7 @@ export interface ContractValidation {
|
|
|
26
28
|
solcVersion?: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
const errorKinds = [
|
|
31
|
+
export const errorKinds = [
|
|
30
32
|
'state-variable-assignment',
|
|
31
33
|
'state-variable-immutable',
|
|
32
34
|
'external-library-linking',
|
|
@@ -92,52 +94,18 @@ interface ValidationErrorUpgradeability extends ValidationErrorBase {
|
|
|
92
94
|
kind: 'missing-public-upgradeto';
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
function* execall(re: RegExp, text: string) {
|
|
96
|
-
re = new RegExp(re, re.flags + (re.sticky ? '' : 'y'));
|
|
97
|
-
while (true) {
|
|
98
|
-
const match = re.exec(text);
|
|
99
|
-
if (match && match[0] !== '') {
|
|
100
|
-
yield match;
|
|
101
|
-
} else {
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
97
|
function getAllowed(node: Node, reachable: boolean): string[] {
|
|
108
98
|
if ('documentation' in node) {
|
|
109
99
|
const tag = `oz-upgrades-unsafe-allow${reachable ? '-reachable' : ''}`;
|
|
110
|
-
const doc =
|
|
100
|
+
const doc = getDocumentation(node);
|
|
111
101
|
return getAnnotationArgs(doc, tag);
|
|
112
102
|
} else {
|
|
113
103
|
return [];
|
|
114
104
|
}
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
/**
|
|
118
|
-
* Get args from the doc string matching the given tag
|
|
119
|
-
*/
|
|
120
107
|
export function getAnnotationArgs(doc: string, tag: string) {
|
|
121
|
-
|
|
122
|
-
for (const { groups } of execall(
|
|
123
|
-
/^\s*(?:@(?<title>\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s*@\w+)[^])*)/m,
|
|
124
|
-
doc,
|
|
125
|
-
)) {
|
|
126
|
-
if (groups && groups.title === 'custom' && groups.tag === tag) {
|
|
127
|
-
const trimmedArgs = groups.args.trim();
|
|
128
|
-
if (trimmedArgs.length > 0) {
|
|
129
|
-
result.push(...trimmedArgs.split(/\s+/));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
result.forEach(arg => {
|
|
135
|
-
if (!(errorKinds as readonly string[]).includes(arg)) {
|
|
136
|
-
throw new Error(`NatSpec: ${tag} argument not recognized: ${arg}`);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return result;
|
|
108
|
+
return getSupportedAnnotationArgs(doc, tag, errorKinds);
|
|
141
109
|
}
|
|
142
110
|
|
|
143
111
|
function skipCheckReachable(error: string, node: Node): boolean {
|
|
@@ -149,10 +117,6 @@ function skipCheck(error: string, node: Node): boolean {
|
|
|
149
117
|
return getAllowed(node, false).includes(error) || getAllowed(node, true).includes(error);
|
|
150
118
|
}
|
|
151
119
|
|
|
152
|
-
function getFullyQualifiedName(source: string, contractName: string) {
|
|
153
|
-
return `${source}:${contractName}`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
120
|
export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVersion?: string): ValidationRunData {
|
|
157
121
|
const validation: ValidationRunData = {};
|
|
158
122
|
const fromId: Record<number, string> = {};
|