sol2uml 2.5.7 → 2.5.9
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/README.md +3 -2
- package/lib/converterAST2Classes.js +1 -1
- package/lib/converterClasses2Storage.d.ts +2 -2
- package/lib/converterClasses2Storage.js +13 -11
- package/lib/sol2uml.js +5 -4
- package/lib/utils/regEx.d.ts +1 -0
- package/lib/utils/regEx.js +3 -1
- package/lib/utils/validators.d.ts +1 -1
- package/lib/utils/validators.js +14 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,6 +151,7 @@ Options:
|
|
|
151
151
|
-u, --url <url> URL of the Ethereum node to get storage values if the `data` option is used. (default: "http://localhost:8545", env: NODE_URL)
|
|
152
152
|
-bn, --block <number> Block number to get the contract storage values from. (default: "latest")
|
|
153
153
|
-a, --array <number> Number of slots to display at the start and end of arrays. (default: "2")
|
|
154
|
+
-hx, --hideExpand <variables> Comma-separated list of storage variables to not expand. That's arrays, structs, strings or bytes.
|
|
154
155
|
-hv, --hideValue Hide storage slot value column. (default: false)
|
|
155
156
|
-h, --help display help for command
|
|
156
157
|
```
|
|
@@ -196,8 +197,8 @@ Options:
|
|
|
196
197
|
-l, --lineBuffer <value> Minimum number of lines before and after changes (default: 4)
|
|
197
198
|
-af --aFile <value> Contract A source code filename without the .sol extension (default: compares all source files)
|
|
198
199
|
-bf --bFile <value> Contract B source code filename without the .sol extension (default: aFile if specified)
|
|
199
|
-
-bn, --bNetwork <network> Ethereum network which maps to a blockchain explorer for contract B if on a different blockchain to contract A. Contract A uses the `network` option (default: value of `network` option) (choices: "mainnet",
|
|
200
|
-
"arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis", "celo")
|
|
200
|
+
-bn, --bNetwork <network> Ethereum network which maps to a blockchain explorer for contract B if on a different blockchain to contract A. Contract A uses the `network` option (default: value of `network` option) (choices: "mainnet",
|
|
201
|
+
"goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis", "celo")
|
|
201
202
|
-be, --bExplorerUrl <url> Override the `bNetwork` option with custom blockchain explorer API URL for contract B if on a different blockchain to contract A. Contract A uses the `explorerUrl` (default: value of `explorerUrl` option)
|
|
202
203
|
-bk, --bApiKey <key> Blockchain explorer API key for contract B if on a different blockchain to contract A. Contract A uses the `apiKey` option (default: value of `apiKey` option)
|
|
203
204
|
-s, --summary Only show a summary of the file differences. (default: false)
|
|
@@ -665,7 +665,7 @@ function parseParameters(params) {
|
|
|
665
665
|
function parseContractKind(kind) {
|
|
666
666
|
switch (kind) {
|
|
667
667
|
case 'contract':
|
|
668
|
-
return umlClass_1.ClassStereotype.
|
|
668
|
+
return umlClass_1.ClassStereotype.Contract;
|
|
669
669
|
case 'interface':
|
|
670
670
|
return umlClass_1.ClassStereotype.Interface;
|
|
671
671
|
case 'library':
|
|
@@ -44,7 +44,7 @@ export interface StorageSection {
|
|
|
44
44
|
* @param contractFilename relative path of the contract in the file system
|
|
45
45
|
* @return storageSections array of storageSection objects
|
|
46
46
|
*/
|
|
47
|
-
export declare const convertClasses2StorageSections: (contractName: string, umlClasses: UmlClass[], arrayItems: number, contractFilename?: string) => StorageSection[];
|
|
47
|
+
export declare const convertClasses2StorageSections: (contractName: string, umlClasses: UmlClass[], arrayItems: number, contractFilename?: string, noExpandVariables?: string[]) => StorageSection[];
|
|
48
48
|
/**
|
|
49
49
|
* Recursively adds new storage sections under a class attribute.
|
|
50
50
|
* also returns the allowed enum values
|
|
@@ -57,7 +57,7 @@ export declare const convertClasses2StorageSections: (contractName: string, umlC
|
|
|
57
57
|
* @return storageSection new storage section that was added or undefined if none was added.
|
|
58
58
|
* @return enumValues array of allowed enum values. undefined if attribute is not an enum
|
|
59
59
|
*/
|
|
60
|
-
export declare const parseStorageSectionFromAttribute: (attribute: Attribute, umlClass: UmlClass, otherClasses: readonly UmlClass[], storageSections: StorageSection[], mapping: boolean, arrayItems: number) => {
|
|
60
|
+
export declare const parseStorageSectionFromAttribute: (attribute: Attribute, umlClass: UmlClass, otherClasses: readonly UmlClass[], storageSections: StorageSection[], mapping: boolean, arrayItems: number, noExpandVariables: string[]) => {
|
|
61
61
|
storageSection: StorageSection;
|
|
62
62
|
enumValues?: string[];
|
|
63
63
|
};
|
|
@@ -29,7 +29,7 @@ let variableId = 1;
|
|
|
29
29
|
* @param contractFilename relative path of the contract in the file system
|
|
30
30
|
* @return storageSections array of storageSection objects
|
|
31
31
|
*/
|
|
32
|
-
const convertClasses2StorageSections = (contractName, umlClasses, arrayItems, contractFilename) => {
|
|
32
|
+
const convertClasses2StorageSections = (contractName, umlClasses, arrayItems, contractFilename, noExpandVariables = []) => {
|
|
33
33
|
// Find the base UML Class from the base contract name
|
|
34
34
|
const umlClass = umlClasses.find(({ name, relativePath }) => {
|
|
35
35
|
if (!contractFilename) {
|
|
@@ -48,7 +48,7 @@ const convertClasses2StorageSections = (contractName, umlClasses, arrayItems, co
|
|
|
48
48
|
}
|
|
49
49
|
debug(`Found contract "${contractName}" in ${umlClass.absolutePath}`);
|
|
50
50
|
const storageSections = [];
|
|
51
|
-
const variables = parseVariables(umlClass, umlClasses, [], storageSections, [], false, arrayItems);
|
|
51
|
+
const variables = parseVariables(umlClass, umlClasses, [], storageSections, [], false, arrayItems, noExpandVariables);
|
|
52
52
|
// Add new storage section to the beginning of the array
|
|
53
53
|
storageSections.unshift({
|
|
54
54
|
id: storageId++,
|
|
@@ -72,7 +72,7 @@ exports.convertClasses2StorageSections = convertClasses2StorageSections;
|
|
|
72
72
|
* @param arrayItems the number of items to display at the start and end of an array
|
|
73
73
|
* @return variables array of storage variables in the `umlClass`
|
|
74
74
|
*/
|
|
75
|
-
const parseVariables = (umlClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems) => {
|
|
75
|
+
const parseVariables = (umlClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems, noExpandVariables) => {
|
|
76
76
|
// Add storage slots from inherited contracts first.
|
|
77
77
|
// Get immediate parent contracts that the class inherits from
|
|
78
78
|
const parentContracts = umlClass.getParentContracts();
|
|
@@ -87,7 +87,7 @@ const parseVariables = (umlClass, umlClasses, variables, storageSections, inheri
|
|
|
87
87
|
throw Error(`Failed to find inherited contract "${parent.targetUmlClassName}" of "${umlClass.absolutePath}"`);
|
|
88
88
|
}
|
|
89
89
|
// recursively parse inherited contract
|
|
90
|
-
parseVariables(parentClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems);
|
|
90
|
+
parseVariables(parentClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems, noExpandVariables);
|
|
91
91
|
});
|
|
92
92
|
// Parse storage for each attribute
|
|
93
93
|
umlClass.attributes.forEach((attribute) => {
|
|
@@ -96,7 +96,9 @@ const parseVariables = (umlClass, umlClasses, variables, storageSections, inheri
|
|
|
96
96
|
return;
|
|
97
97
|
const { size: byteSize, dynamic } = (0, exports.calcStorageByteSize)(attribute, umlClass, umlClasses);
|
|
98
98
|
// parse any dependent storage sections or enums
|
|
99
|
-
const references =
|
|
99
|
+
const references = noExpandVariables.includes(attribute.name)
|
|
100
|
+
? undefined
|
|
101
|
+
: (0, exports.parseStorageSectionFromAttribute)(attribute, umlClass, umlClasses, storageSections, mapping || attribute.attributeType === umlClass_1.AttributeType.Mapping, arrayItems, noExpandVariables);
|
|
100
102
|
// should this new variable get the slot value
|
|
101
103
|
const displayValue = calcDisplayValue(attribute.attributeType, dynamic, mapping, references?.storageSection?.type);
|
|
102
104
|
const getValue = calcGetValue(attribute.attributeType, mapping);
|
|
@@ -179,7 +181,7 @@ const adjustSlots = (storageSection, slotOffset, storageSections) => {
|
|
|
179
181
|
* @return storageSection new storage section that was added or undefined if none was added.
|
|
180
182
|
* @return enumValues array of allowed enum values. undefined if attribute is not an enum
|
|
181
183
|
*/
|
|
182
|
-
const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, storageSections, mapping, arrayItems) => {
|
|
184
|
+
const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, storageSections, mapping, arrayItems, noExpandVariables) => {
|
|
183
185
|
if (attribute.attributeType === umlClass_1.AttributeType.Array) {
|
|
184
186
|
// storage is dynamic if the attribute type ends in []
|
|
185
187
|
const result = attribute.type.match(/\[([\w$.]*)]$/);
|
|
@@ -216,7 +218,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
216
218
|
let references;
|
|
217
219
|
if (baseAttributeType !== umlClass_1.AttributeType.Elementary) {
|
|
218
220
|
// recursively add storage section for Array and UserDefined types
|
|
219
|
-
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems);
|
|
221
|
+
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems, noExpandVariables);
|
|
220
222
|
}
|
|
221
223
|
const displayValue = calcDisplayValue(baseAttribute.attributeType, dynamicBase, mapping, references?.storageSection?.type);
|
|
222
224
|
const getValue = calcGetValue(attribute.attributeType, mapping);
|
|
@@ -247,7 +249,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
247
249
|
variable.type !== '----' // ignore any filler variables
|
|
248
250
|
) {
|
|
249
251
|
// recursively add storage section for Array and UserDefined types
|
|
250
|
-
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems);
|
|
252
|
+
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems, noExpandVariables);
|
|
251
253
|
variable.referenceSectionId = references?.storageSection?.id;
|
|
252
254
|
variable.enumValues = references?.enumValues;
|
|
253
255
|
}
|
|
@@ -269,7 +271,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
269
271
|
// Is the user defined type linked to another Contract, Struct or Enum?
|
|
270
272
|
const typeClass = findTypeClass(attribute.type, attribute, umlClass, otherClasses);
|
|
271
273
|
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
272
|
-
const variables = parseVariables(typeClass, otherClasses, [], storageSections, [], mapping, arrayItems);
|
|
274
|
+
const variables = parseVariables(typeClass, otherClasses, [], storageSections, [], mapping, arrayItems, noExpandVariables);
|
|
273
275
|
const storageSection = {
|
|
274
276
|
id: storageId++,
|
|
275
277
|
name: attribute.type,
|
|
@@ -298,7 +300,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
298
300
|
// Find UserDefined type can be a contract, struct or enum
|
|
299
301
|
const typeClass = findTypeClass(result[1], attribute, umlClass, otherClasses);
|
|
300
302
|
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
301
|
-
let variables = parseVariables(typeClass, otherClasses, [], storageSections, [], true, arrayItems);
|
|
303
|
+
let variables = parseVariables(typeClass, otherClasses, [], storageSections, [], true, arrayItems, noExpandVariables);
|
|
302
304
|
const storageSection = {
|
|
303
305
|
id: storageId++,
|
|
304
306
|
name: typeClass.name,
|
|
@@ -520,7 +522,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
520
522
|
dynamic: false,
|
|
521
523
|
};
|
|
522
524
|
default:
|
|
523
|
-
return { size:
|
|
525
|
+
return { size: 20, dynamic: false };
|
|
524
526
|
}
|
|
525
527
|
}
|
|
526
528
|
if (attribute.attributeType === umlClass_1.AttributeType.Elementary) {
|
package/lib/sol2uml.js
CHANGED
|
@@ -29,7 +29,7 @@ Can also flatten or compare verified source files on Etherscan-like explorers.`)
|
|
|
29
29
|
.choices(['svg', 'png', 'dot', 'all'])
|
|
30
30
|
.default('svg'))
|
|
31
31
|
.option('-o, --outputFileName <value>', 'output file name')
|
|
32
|
-
.option('-i, --ignoreFilesOrFolders <filesOrFolders>', 'comma
|
|
32
|
+
.option('-i, --ignoreFilesOrFolders <filesOrFolders>', 'comma-separated list of files or folders to ignore')
|
|
33
33
|
.addOption(new commander_1.Option('-n, --network <network>', 'Ethereum network which maps to a blockchain explorer')
|
|
34
34
|
.choices(parserEtherscan_1.networks)
|
|
35
35
|
.default('mainnet')
|
|
@@ -56,7 +56,7 @@ program
|
|
|
56
56
|
.usage('[options] <fileFolderAddress>')
|
|
57
57
|
.description('Generates a UML class diagram from Solidity source code.')
|
|
58
58
|
.argument('fileFolderAddress', argumentText)
|
|
59
|
-
.option('-b, --baseContractNames <value>', 'only output contracts connected to these comma
|
|
59
|
+
.option('-b, --baseContractNames <value>', 'only output contracts connected to these comma-separated base contract names')
|
|
60
60
|
.addOption(new commander_1.Option('-d, --depth <value>', 'depth of connected classes to the base contracts. 1 will only show directly connected contracts, interfaces, libraries, structs and enums.').default('100', 'all'))
|
|
61
61
|
.option('-c, --clusterFolders', 'cluster contracts into source folders', false)
|
|
62
62
|
.option('-hv, --hideVariables', 'hide variables from contracts, interfaces, structs and enums', false)
|
|
@@ -132,6 +132,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
|
|
|
132
132
|
.default('http://localhost:8545'))
|
|
133
133
|
.option('-bn, --block <number>', 'Block number to get the contract storage values from.', 'latest')
|
|
134
134
|
.option('-a, --array <number>', 'Number of slots to display at the start and end of arrays.', '2')
|
|
135
|
+
.option('-hx, --hideExpand <variables>', "Comma-separated list of storage variables to not expand. That's arrays, structs, strings or bytes.", validators_1.validateVariables)
|
|
135
136
|
.option('-hv, --hideValue', 'Hide storage slot value column.', false)
|
|
136
137
|
.action(async (fileFolderAddress, options, command) => {
|
|
137
138
|
try {
|
|
@@ -146,7 +147,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
|
|
|
146
147
|
let { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions);
|
|
147
148
|
contractName = combinedOptions.contract || contractName;
|
|
148
149
|
const arrayItems = parseInt(combinedOptions.array);
|
|
149
|
-
const storageSections = (0, converterClasses2Storage_1.convertClasses2StorageSections)(contractName, umlClasses, arrayItems, combinedOptions.contractFile);
|
|
150
|
+
const storageSections = (0, converterClasses2Storage_1.convertClasses2StorageSections)(contractName, umlClasses, arrayItems, combinedOptions.contractFile, options.hideExpand);
|
|
150
151
|
if ((0, regEx_1.isAddress)(fileFolderAddress)) {
|
|
151
152
|
// The first storage is the contract
|
|
152
153
|
storageSections[0].address = fileFolderAddress;
|
|
@@ -232,7 +233,7 @@ The line numbers are from contract B. There are no line numbers for the red sect
|
|
|
232
233
|
.addOption(new commander_1.Option('-bn, --bNetwork <network>', 'Ethereum network which maps to a blockchain explorer for contract B if on a different blockchain to contract A. Contract A uses the `network` option (default: value of `network` option)').choices(parserEtherscan_1.networks))
|
|
233
234
|
.option('-be, --bExplorerUrl <url>', 'Override the `bNetwork` option with custom blockchain explorer API URL for contract B if on a different blockchain to contract A. Contract A uses the `explorerUrl` (default: value of `explorerUrl` option)')
|
|
234
235
|
.option('-bk, --bApiKey <key>', 'Blockchain explorer API key for contract B if on a different blockchain to contract A. Contract A uses the `apiKey` option (default: value of `apiKey` option)')
|
|
235
|
-
.option('-s, --summary', 'Only show a summary of the file differences
|
|
236
|
+
.option('-s, --summary', 'Only show a summary of the file differences', false)
|
|
236
237
|
.option('--flatten', 'Flatten into a single file before comparing', false)
|
|
237
238
|
.option('--saveFiles', 'Save the flattened contract code to the filesystem when using the `flatten` option. The file names will be the contract address with a .sol extension', false)
|
|
238
239
|
.action(async (addressA, addressB, options, command) => {
|
package/lib/utils/regEx.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const ethereumAddress: RegExp;
|
|
2
2
|
export declare const ethereumAddresses: RegExp;
|
|
3
|
+
export declare const commaSeparatedList: RegExp;
|
|
3
4
|
export declare const isAddress: (input: string) => boolean;
|
|
4
5
|
export declare const parseSolidityVersion: (compilerVersion: string) => string;
|
package/lib/utils/regEx.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseSolidityVersion = exports.isAddress = exports.ethereumAddresses = exports.ethereumAddress = void 0;
|
|
3
|
+
exports.parseSolidityVersion = exports.isAddress = exports.commaSeparatedList = exports.ethereumAddresses = exports.ethereumAddress = void 0;
|
|
4
4
|
exports.ethereumAddress = /^0x([A-Fa-f0-9]{40})$/;
|
|
5
5
|
// comma-separated list of addresses with no whitespace
|
|
6
6
|
exports.ethereumAddresses = /^(0x[A-Fa-f0-9]{40},?)+$/;
|
|
7
|
+
// comma-separated list of names with no whitespace
|
|
8
|
+
exports.commaSeparatedList = /^[^,\s]+(,[^,\s]+)*$/;
|
|
7
9
|
const isAddress = (input) => {
|
|
8
10
|
return input.match(/^0x([A-Fa-f0-9]{40})$/) !== null;
|
|
9
11
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const validateAddress: (address: string) => string;
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const validateVariables: (variables: string) => string[];
|
|
3
3
|
export declare const validateLineBuffer: (lineBufferParam: string) => number;
|
package/lib/utils/validators.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateLineBuffer = exports.
|
|
3
|
+
exports.validateLineBuffer = exports.validateVariables = exports.validateAddress = void 0;
|
|
4
4
|
const regEx_1 = require("./regEx");
|
|
5
5
|
const commander_1 = require("commander");
|
|
6
6
|
const utils_1 = require("ethers/lib/utils");
|
|
@@ -13,29 +13,24 @@ const validateAddress = (address) => {
|
|
|
13
13
|
throw new commander_1.InvalidArgumentError(`Address must be in hexadecimal format with a 0x prefix.`);
|
|
14
14
|
};
|
|
15
15
|
exports.validateAddress = validateAddress;
|
|
16
|
-
const
|
|
16
|
+
const validateVariables = (variables) => {
|
|
17
17
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return
|
|
18
|
+
if (typeof variables === 'string' &&
|
|
19
|
+
variables.match(regEx_1.commaSeparatedList))
|
|
20
|
+
return variables.split(',');
|
|
21
21
|
}
|
|
22
22
|
catch (err) { }
|
|
23
|
-
throw new commander_1.InvalidArgumentError(`Must be
|
|
24
|
-
If running for multiple addresses, the comma-separated list of addresses must not have white spaces.`);
|
|
25
|
-
};
|
|
26
|
-
exports.validateAddresses = validateAddresses;
|
|
27
|
-
const convertAddresses = (addresses) => {
|
|
28
|
-
if (typeof addresses === 'string' && addresses?.match(regEx_1.ethereumAddress))
|
|
29
|
-
return [(0, utils_1.getAddress)(addresses).toLowerCase()];
|
|
30
|
-
if (typeof addresses === 'string' && addresses?.match(regEx_1.ethereumAddresses))
|
|
31
|
-
return addresses.split(',').map((a) => (0, utils_1.getAddress)(a).toLowerCase());
|
|
32
|
-
return undefined;
|
|
23
|
+
throw new commander_1.InvalidArgumentError(`Must be a comma-separate list of storage variable names with no white spaces.`);
|
|
33
24
|
};
|
|
25
|
+
exports.validateVariables = validateVariables;
|
|
34
26
|
const validateLineBuffer = (lineBufferParam) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
try {
|
|
28
|
+
const lineBuffer = parseInt(lineBufferParam, 10);
|
|
29
|
+
if (lineBuffer >= 0)
|
|
30
|
+
return lineBuffer;
|
|
31
|
+
}
|
|
32
|
+
catch (err) { }
|
|
33
|
+
throw new commander_1.InvalidOptionArgumentError(`Must be a zero or a positive integer.`);
|
|
39
34
|
};
|
|
40
35
|
exports.validateLineBuffer = validateLineBuffer;
|
|
41
36
|
//# sourceMappingURL=validators.js.map
|