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 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", "goerli", "sepolia", "polygon",
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.None;
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 = (0, exports.parseStorageSectionFromAttribute)(attribute, umlClass, umlClasses, storageSections, mapping || attribute.attributeType === umlClass_1.AttributeType.Mapping, arrayItems);
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: 32, dynamic: false };
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 separated list of files or folders to ignore')
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 separated base contract names')
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.', false)
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) => {
@@ -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;
@@ -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 validateAddresses: (addresses: string) => string[];
2
+ export declare const validateVariables: (variables: string) => string[];
3
3
  export declare const validateLineBuffer: (lineBufferParam: string) => number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateLineBuffer = exports.validateAddresses = exports.validateAddress = void 0;
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 validateAddresses = (addresses) => {
16
+ const validateVariables = (variables) => {
17
17
  try {
18
- const addressArray = convertAddresses(addresses);
19
- if (addressArray)
20
- return addressArray;
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 an address or an array of addresses in hexadecimal format with a 0x prefix.
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
- const lineBuffer = parseInt(lineBufferParam);
36
- if (isNaN(lineBuffer))
37
- throw Error(`Invalid line buffer "${lineBuffer}". Must be a number`);
38
- return lineBuffer;
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol2uml",
3
- "version": "2.5.7",
3
+ "version": "2.5.9",
4
4
  "description": "Solidity contract visualisation tool.",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",