sol2uml 2.5.6 → 2.5.7

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
@@ -189,15 +189,21 @@ The red sections are removals from contract A that are not in contract B.
189
189
  The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B.
190
190
 
191
191
  Arguments:
192
- addressA Contract address in hexadecimal format with a 0x prefix of the first contract.
193
- addressB Contract address in hexadecimal format with a 0x prefix of the second contract.
192
+ addressA Contract address in hexadecimal format with a 0x prefix of the first contract
193
+ addressB Contract address in hexadecimal format with a 0x prefix of the second contract
194
194
 
195
195
  Options:
196
- -l, --lineBuffer <value> Minimum number of lines before and after changes (default: "4")
197
- --aFile <value> Contract A source code filename without the .sol extension. (default: compares all source files)
198
- --bFile <value> Contract B source code filename without the .sol extension. (default: aFile if specified)
199
- -s, --saveFiles Save the flattened contract code to the filesystem. The file names will be the contract address with a .sol extension. (default: false)
200
- -h, --help display help for command
196
+ -l, --lineBuffer <value> Minimum number of lines before and after changes (default: 4)
197
+ -af --aFile <value> Contract A source code filename without the .sol extension (default: compares all source files)
198
+ -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")
201
+ -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
+ -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
+ -s, --summary Only show a summary of the file differences. (default: false)
204
+ --flatten Flatten into a single file before comparing (default: false)
205
+ --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 (default: false)
206
+ -h, --help display help for command
201
207
  ```
202
208
 
203
209
  ## UML Class diagram examples
@@ -18,7 +18,7 @@ var StorageSectionType;
18
18
  StorageSectionType["Array"] = "Array";
19
19
  StorageSectionType["Bytes"] = "Bytes";
20
20
  StorageSectionType["String"] = "String";
21
- })(StorageSectionType = exports.StorageSectionType || (exports.StorageSectionType = {}));
21
+ })(StorageSectionType || (exports.StorageSectionType = StorageSectionType = {}));
22
22
  let storageId = 1;
23
23
  let variableId = 1;
24
24
  /**
@@ -0,0 +1,35 @@
1
+ import { EtherscanParser } from './parserEtherscan';
2
+ interface DiffOptions {
3
+ network: string;
4
+ lineBuffer: number;
5
+ }
6
+ interface FlattenAndDiffOptions extends DiffOptions {
7
+ aFile?: string;
8
+ bFile?: string;
9
+ saveFiles?: boolean;
10
+ }
11
+ interface DiffFiles {
12
+ filename?: string;
13
+ aCode?: string;
14
+ bCode?: string;
15
+ result: 'added' | 'removed' | 'match' | 'changed';
16
+ }
17
+ interface CompareContracts {
18
+ files: DiffFiles[];
19
+ contractNameA: string;
20
+ contractNameB: string;
21
+ }
22
+ export declare const compareContracts: (addressA: string, addressB: string, etherscanParserA: EtherscanParser, etherscanParserB: EtherscanParser, options: DiffOptions) => Promise<CompareContracts>;
23
+ export declare const displayContractNames: (addressA: string, addressB: string, contractNameA: string, contractNameB: string, options: {
24
+ network: string;
25
+ bNetwork?: string;
26
+ }) => void;
27
+ export declare const displayFileDiffSummary: (fileDiffs: DiffFiles[]) => void;
28
+ export declare const displayFileDiffs: (fileDiffs: DiffFiles[], options?: {
29
+ lineBuffer?: number;
30
+ }) => void;
31
+ export declare const flattenAndDiff: (addressA: string, addressB: string, aEtherscanParser: EtherscanParser, bEtherscanParser: EtherscanParser, options: FlattenAndDiffOptions) => Promise<{
32
+ contractNameA: string;
33
+ contractNameB: string;
34
+ }>;
35
+ export {};
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flattenAndDiff = exports.displayFileDiffs = exports.displayFileDiffSummary = exports.displayContractNames = exports.compareContracts = void 0;
4
+ const regEx_1 = require("./utils/regEx");
5
+ const clc = require('cli-color');
6
+ const writerFiles_1 = require("./writerFiles");
7
+ const diff_1 = require("./utils/diff");
8
+ const compareContracts = async (addressA, addressB, etherscanParserA, etherscanParserB, options) => {
9
+ const files = [];
10
+ const { files: aFiles, contractName: contractNameA } = await etherscanParserA.getSourceCode(addressA);
11
+ const { files: bFiles, contractName: contractNameB } = await etherscanParserB.getSourceCode(addressB);
12
+ if (aFiles.length === 1 && bFiles.length === 1) {
13
+ if ((0, regEx_1.isAddress)(aFiles[0].filename))
14
+ files.push({
15
+ filename: `${aFiles[0].filename} to ${bFiles[0].filename}`,
16
+ aCode: aFiles[0].code,
17
+ bCode: bFiles[0].code,
18
+ result: aFiles[0].code === bFiles[0].code ? 'match' : 'changed',
19
+ });
20
+ return {
21
+ files,
22
+ contractNameA,
23
+ contractNameB,
24
+ };
25
+ }
26
+ // For each file in the A contract
27
+ for (const aFile of aFiles) {
28
+ // Look for A contract filename in B contract
29
+ const bFile = bFiles.find((bFile) => bFile.filename === aFile.filename);
30
+ if (bFile) {
31
+ // The A contract filename exists in the B contract
32
+ if (aFile.code !== bFile.code) {
33
+ // console.log(`${aFile.filename} ${clc.red('different')}:`)
34
+ files.push({
35
+ filename: aFile.filename,
36
+ aCode: aFile.code,
37
+ bCode: bFile.code,
38
+ result: 'changed',
39
+ });
40
+ }
41
+ else {
42
+ files.push({
43
+ filename: aFile.filename,
44
+ aCode: aFile.code,
45
+ bCode: bFile.code,
46
+ result: 'match',
47
+ });
48
+ }
49
+ }
50
+ else {
51
+ // The A contract filename does not exist in the B contract
52
+ files.push({
53
+ filename: aFile.filename,
54
+ aCode: aFile.code,
55
+ result: 'removed',
56
+ });
57
+ }
58
+ }
59
+ // For each file in the B contract
60
+ for (const bFile of bFiles) {
61
+ // Look for B contract filename in A contract
62
+ const aFile = aFiles.find((aFile) => aFile.filename === bFile.filename);
63
+ if (!aFile) {
64
+ // The B contract filename does not exist in the A contract
65
+ files.push({
66
+ filename: bFile.filename,
67
+ bCode: bFile.code,
68
+ result: 'added',
69
+ });
70
+ }
71
+ }
72
+ // Sort by filename
73
+ return {
74
+ files: files.sort((a, b) => a.filename.localeCompare(b.filename)),
75
+ contractNameA,
76
+ contractNameB,
77
+ };
78
+ };
79
+ exports.compareContracts = compareContracts;
80
+ const displayContractNames = (addressA, addressB, contractNameA, contractNameB, options) => {
81
+ console.log(`Contract A: ${addressA} ${contractNameA} on ${options.network}`);
82
+ console.log(`Contract B: ${addressB} ${contractNameB} on ${options.bNetwork || options.network}\n`);
83
+ };
84
+ exports.displayContractNames = displayContractNames;
85
+ const displayFileDiffSummary = (fileDiffs) => {
86
+ for (const file of fileDiffs) {
87
+ switch (file.result) {
88
+ case 'match':
89
+ console.log(`${file.result.padEnd(7)} ${file.filename}`);
90
+ break;
91
+ case 'added':
92
+ console.log(`${clc.green(file.result.padEnd(7))} ${file.filename}`);
93
+ break;
94
+ case 'changed':
95
+ case 'removed':
96
+ console.log(`${clc.red(file.result)} ${file.filename}`);
97
+ break;
98
+ }
99
+ }
100
+ };
101
+ exports.displayFileDiffSummary = displayFileDiffSummary;
102
+ const displayFileDiffs = (fileDiffs, options = {}) => {
103
+ for (const file of fileDiffs) {
104
+ switch (file.result) {
105
+ case 'added':
106
+ console.log(`Added ${file.filename}`);
107
+ console.log(clc.green(file.bCode));
108
+ break;
109
+ case 'changed':
110
+ console.log(`Changed ${file.filename}`);
111
+ (0, diff_1.diffCode)(file.aCode, file.bCode, options.lineBuffer);
112
+ break;
113
+ case 'removed':
114
+ console.log(`Removed ${file.filename}`);
115
+ console.log(clc.red(file.aCode));
116
+ break;
117
+ }
118
+ }
119
+ };
120
+ exports.displayFileDiffs = displayFileDiffs;
121
+ const flattenAndDiff = async (addressA, addressB, aEtherscanParser, bEtherscanParser, options) => {
122
+ // Get verified Solidity code from Etherscan and flatten
123
+ const { solidityCode: codeA, contractName: contractNameA } = await aEtherscanParser.getSolidityCode(addressA, options.aFile);
124
+ const { solidityCode: codeB, contractName: contractNameB } = await bEtherscanParser.getSolidityCode(addressB, options.bFile || options.aFile);
125
+ (0, exports.displayContractNames)(addressA, addressB, contractNameA, contractNameB, options);
126
+ (0, diff_1.diffCode)(codeA, codeB, options.lineBuffer);
127
+ if (options.saveFiles) {
128
+ await (0, writerFiles_1.writeSolidity)(codeA, addressA);
129
+ await (0, writerFiles_1.writeSolidity)(codeB, addressB);
130
+ }
131
+ (0, exports.displayContractNames)(addressA, addressB, contractNameA, contractNameB, options);
132
+ return { contractNameA, contractNameB };
133
+ };
134
+ exports.flattenAndDiff = flattenAndDiff;
135
+ //# sourceMappingURL=diffContracts.js.map
@@ -42,7 +42,7 @@ export declare class EtherscanParser {
42
42
  * @oaram filename optional, case-sensitive name of the source file without the .sol
43
43
  */
44
44
  getSourceCode(contractAddress: string, filename?: string): Promise<{
45
- files: readonly {
45
+ files: {
46
46
  code: string;
47
47
  filename: string;
48
48
  }[];
package/lib/slotValues.js CHANGED
@@ -63,7 +63,7 @@ const addSlotValues = async (url, contractAddress, storageSection, arrayItems, b
63
63
  }
64
64
  }
65
65
  // if variable is past the slot that has the value
66
- else if (variable.toSlot > fromSlot) {
66
+ else if (bignumber_1.BigNumber.from(variable.toSlot).gt(fromSlot)) {
67
67
  break;
68
68
  }
69
69
  }
package/lib/sol2uml.js CHANGED
@@ -1,20 +1,21 @@
1
1
  #! /usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const converterClasses2Dot_1 = require("./converterClasses2Dot");
5
- const parserGeneral_1 = require("./parserGeneral");
6
- const parserEtherscan_1 = require("./parserEtherscan");
7
- const filterClasses_1 = require("./filterClasses");
8
4
  const commander_1 = require("commander");
5
+ const ethers_1 = require("ethers");
6
+ const path_1 = require("path");
7
+ const converterClasses2Dot_1 = require("./converterClasses2Dot");
9
8
  const converterClasses2Storage_1 = require("./converterClasses2Storage");
10
9
  const converterStorage2Dot_1 = require("./converterStorage2Dot");
11
- const regEx_1 = require("./utils/regEx");
12
- const writerFiles_1 = require("./writerFiles");
13
- const path_1 = require("path");
10
+ const diffContracts_1 = require("./diffContracts");
11
+ const filterClasses_1 = require("./filterClasses");
12
+ const parserEtherscan_1 = require("./parserEtherscan");
13
+ const parserGeneral_1 = require("./parserGeneral");
14
14
  const squashClasses_1 = require("./squashClasses");
15
- const diff_1 = require("./diff");
16
15
  const slotValues_1 = require("./slotValues");
17
- const ethers_1 = require("ethers");
16
+ const regEx_1 = require("./utils/regEx");
17
+ const validators_1 = require("./utils/validators");
18
+ const writerFiles_1 = require("./writerFiles");
18
19
  const clc = require('cli-color');
19
20
  const program = new commander_1.Command();
20
21
  const debugControl = require('debug');
@@ -33,7 +34,7 @@ Can also flatten or compare verified source files on Etherscan-like explorers.`)
33
34
  .choices(parserEtherscan_1.networks)
34
35
  .default('mainnet')
35
36
  .env('ETH_NETWORK'))
36
- .addOption(new commander_1.Option('-e, --explorerUrl <url>', 'Override network with custom blockchain explorer API URL. eg Polygon Mumbai testnet https://api-testnet.polygonscan.com/api').env('EXPLORER_URL'))
37
+ .addOption(new commander_1.Option('-e, --explorerUrl <url>', 'Override the `network` option with a custom blockchain explorer API URL. eg Polygon Mumbai testnet https://api-testnet.polygonscan.com/api').env('EXPLORER_URL'))
37
38
  .addOption(new commander_1.Option('-k, --apiKey <key>', 'Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key').env('SCAN_API_KEY'))
38
39
  .option('-bc, --backColor <color>', 'Canvas background color. "none" will use a transparent canvas.', 'white')
39
40
  .option('-sc, --shapeColor <color>', 'Basic drawing color for graphics, not text', 'black')
@@ -125,7 +126,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
125
126
  .option('-c, --contract <name>', 'Contract name in the local Solidity files. Not needed when using an address as the first argument as the contract name can be derived from Etherscan.')
126
127
  .option('-cf, --contractFile <filename>', 'Filename the contract is located in. This can include the relative path to the desired file.')
127
128
  .option('-d, --data', 'Gets the values in the storage slots from an Ethereum node.', false)
128
- .option('-s, --storage <address>', 'The address of the contract with the storage values. This will be different from the contract with the code if a proxy contract is used. This is not needed if `fileFolderAddress` is an address and the contract is not proxied.')
129
+ .option('-s, --storage <address>', 'The address of the contract with the storage values. This will be different from the contract with the code if a proxy contract is used. This is not needed if `fileFolderAddress` is an address and the contract is not proxied.', validators_1.validateAddress)
129
130
  .addOption(new commander_1.Option('-u, --url <url>', 'URL of the Ethereum node to get storage values if the `data` option is used.')
130
131
  .env('NODE_URL')
131
132
  .default('http://localhost:8545'))
@@ -195,7 +196,7 @@ In order for the merged code to compile, the following is done:
195
196
  3. File imports are commented out.
196
197
  4. "SPDX-License-Identifier" is renamed to "SPDX--License-Identifier".
197
198
  5. Contract dependencies are analysed so the files are merged in an order that will compile.\n`)
198
- .argument('<contractAddress>', 'Contract address in hexadecimal format with a 0x prefix.')
199
+ .argument('<contractAddress>', 'Contract address in hexadecimal format with a 0x prefix.', validators_1.validateAddress)
199
200
  .action(async (contractAddress, options, command) => {
200
201
  try {
201
202
  debug(`About to flatten ${contractAddress}`);
@@ -223,12 +224,17 @@ The results show the comparison of contract A to B.
223
224
  The ${clc.green('green')} sections are additions to contract B that are not in contract A.
224
225
  The ${clc.red('red')} sections are removals from contract A that are not in contract B.
225
226
  The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B.\n`)
226
- .argument('<addressA>', 'Contract address in hexadecimal format with a 0x prefix of the first contract.')
227
- .argument('<addressB>', 'Contract address in hexadecimal format with a 0x prefix of the second contract.')
228
- .addOption(new commander_1.Option('-l, --lineBuffer <value>', 'Minimum number of lines before and after changes').default('4'))
229
- .addOption(new commander_1.Option('-af --aFile <value>', 'Contract A source code filename without the .sol extension. (default: compares all source files)'))
230
- .addOption(new commander_1.Option('-bf --bFile <value>', 'Contract B source code filename without the .sol extension. (default: aFile if specified)'))
231
- .option('-s, --saveFiles', 'Save the flattened contract code to the filesystem. The file names will be the contract address with a .sol extension.', false)
227
+ .argument('<addressA>', 'Contract address in hexadecimal format with a 0x prefix of the first contract', validators_1.validateAddress)
228
+ .argument('<addressB>', 'Contract address in hexadecimal format with a 0x prefix of the second contract', validators_1.validateAddress)
229
+ .option('-l, --lineBuffer <value>', 'Minimum number of lines before and after changes (default: 4)', validators_1.validateLineBuffer)
230
+ .option('-af --aFile <value>', 'Contract A source code filename without the .sol extension (default: compares all source files)')
231
+ .option('-bf --bFile <value>', 'Contract B source code filename without the .sol extension (default: aFile if specified)')
232
+ .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
+ .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
+ .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('--flatten', 'Flatten into a single file before comparing', false)
237
+ .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)
232
238
  .action(async (addressA, addressB, options, command) => {
233
239
  try {
234
240
  debug(`About to diff ${addressA} and ${addressB}`);
@@ -236,21 +242,25 @@ The line numbers are from contract B. There are no line numbers for the red sect
236
242
  ...command.parent._optionValues,
237
243
  ...options,
238
244
  };
239
- // Diff solidity code
240
- const lineBuffer = parseInt(options.lineBuffer);
241
- if (isNaN(lineBuffer))
242
- throw Error(`Invalid line buffer "${options.lineBuffer}". Must be a number`);
243
- const etherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network, combinedOptions.explorerUrl);
244
- // Get verified Solidity code from Etherscan and flatten
245
- const { solidityCode: codeA, contractName: contractNameA } = await etherscanParser.getSolidityCode(addressA, combinedOptions.aFile);
246
- const { solidityCode: codeB, contractName: contractNameB } = await etherscanParser.getSolidityCode(addressB, combinedOptions.bFile || combinedOptions.aFile);
247
- console.log(`Difference between`);
248
- console.log(`A. ${addressA} ${contractNameA} on ${combinedOptions.network}`);
249
- console.log(`B. ${addressB} ${contractNameB} on ${combinedOptions.network}\n`);
250
- (0, diff_1.diffCode)(codeA, codeB, lineBuffer);
251
- if (options.saveFiles) {
252
- await (0, writerFiles_1.writeSolidity)(codeA, addressA);
253
- await (0, writerFiles_1.writeSolidity)(codeB, addressB);
245
+ const aEtherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network, combinedOptions.explorerUrl);
246
+ const bEtherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.bApiKey || combinedOptions.apiKey, combinedOptions.bNetwork || combinedOptions.network, combinedOptions.bExplorerUrl || combinedOptions.explorerUrl);
247
+ if (options.flatten || options.aFile) {
248
+ await (0, diffContracts_1.flattenAndDiff)(addressA, addressB, aEtherscanParser, bEtherscanParser, combinedOptions);
249
+ }
250
+ else {
251
+ const { contractNameA, contractNameB, files } = await (0, diffContracts_1.compareContracts)(addressA, addressB, aEtherscanParser, bEtherscanParser, combinedOptions);
252
+ (0, diffContracts_1.displayContractNames)(addressA, addressB, contractNameA, contractNameB, combinedOptions);
253
+ (0, diffContracts_1.displayFileDiffSummary)(files);
254
+ if (!options.summary) {
255
+ // Just show the summary if all the files are the same
256
+ const diffFiles = files.filter((f) => f.result !== 'match');
257
+ if (diffFiles.length === 0)
258
+ return;
259
+ console.log();
260
+ (0, diffContracts_1.displayFileDiffs)(files, combinedOptions);
261
+ (0, diffContracts_1.displayContractNames)(addressA, addressB, contractNameA, contractNameB, combinedOptions);
262
+ (0, diffContracts_1.displayFileDiffSummary)(files);
263
+ }
254
264
  }
255
265
  }
256
266
  catch (err) {
package/lib/umlClass.js CHANGED
@@ -8,7 +8,7 @@ var Visibility;
8
8
  Visibility[Visibility["External"] = 2] = "External";
9
9
  Visibility[Visibility["Internal"] = 3] = "Internal";
10
10
  Visibility[Visibility["Private"] = 4] = "Private";
11
- })(Visibility = exports.Visibility || (exports.Visibility = {}));
11
+ })(Visibility || (exports.Visibility = Visibility = {}));
12
12
  var ClassStereotype;
13
13
  (function (ClassStereotype) {
14
14
  ClassStereotype[ClassStereotype["None"] = 0] = "None";
@@ -20,7 +20,7 @@ var ClassStereotype;
20
20
  ClassStereotype[ClassStereotype["Enum"] = 6] = "Enum";
21
21
  ClassStereotype[ClassStereotype["Constant"] = 7] = "Constant";
22
22
  ClassStereotype[ClassStereotype["Import"] = 8] = "Import";
23
- })(ClassStereotype = exports.ClassStereotype || (exports.ClassStereotype = {}));
23
+ })(ClassStereotype || (exports.ClassStereotype = ClassStereotype = {}));
24
24
  var OperatorStereotype;
25
25
  (function (OperatorStereotype) {
26
26
  OperatorStereotype[OperatorStereotype["None"] = 0] = "None";
@@ -29,7 +29,7 @@ var OperatorStereotype;
29
29
  OperatorStereotype[OperatorStereotype["Payable"] = 3] = "Payable";
30
30
  OperatorStereotype[OperatorStereotype["Fallback"] = 4] = "Fallback";
31
31
  OperatorStereotype[OperatorStereotype["Abstract"] = 5] = "Abstract";
32
- })(OperatorStereotype = exports.OperatorStereotype || (exports.OperatorStereotype = {}));
32
+ })(OperatorStereotype || (exports.OperatorStereotype = OperatorStereotype = {}));
33
33
  var AttributeType;
34
34
  (function (AttributeType) {
35
35
  AttributeType[AttributeType["Elementary"] = 0] = "Elementary";
@@ -37,12 +37,12 @@ var AttributeType;
37
37
  AttributeType[AttributeType["Function"] = 2] = "Function";
38
38
  AttributeType[AttributeType["Array"] = 3] = "Array";
39
39
  AttributeType[AttributeType["Mapping"] = 4] = "Mapping";
40
- })(AttributeType = exports.AttributeType || (exports.AttributeType = {}));
40
+ })(AttributeType || (exports.AttributeType = AttributeType = {}));
41
41
  var ReferenceType;
42
42
  (function (ReferenceType) {
43
43
  ReferenceType[ReferenceType["Memory"] = 0] = "Memory";
44
44
  ReferenceType[ReferenceType["Storage"] = 1] = "Storage";
45
- })(ReferenceType = exports.ReferenceType || (exports.ReferenceType = {}));
45
+ })(ReferenceType || (exports.ReferenceType = ReferenceType = {}));
46
46
  class UmlClass {
47
47
  constructor(properties) {
48
48
  this.imports = [];
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Compares code using Google's diff_match_patch and displays the results in the console.
3
+ * @param codeA
4
+ * @param codeB
5
+ * @param lineBuff the number of lines to display before and after each change.
6
+ */
7
+ export declare const diffCode: (codeA: string, codeB: string, lineBuff: number) => void;
package/lib/utils/diff.js CHANGED
@@ -1 +1,188 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.diffCode = void 0;
27
+ const diff_match_patch_1 = __importStar(require("diff-match-patch"));
28
+ const clc = require('cli-color');
29
+ const SkippedLinesMarker = `\n---`;
30
+ /**
31
+ * Compares code using Google's diff_match_patch and displays the results in the console.
32
+ * @param codeA
33
+ * @param codeB
34
+ * @param lineBuff the number of lines to display before and after each change.
35
+ */
36
+ const diffCode = (codeA, codeB, lineBuff) => {
37
+ // @ts-ignore
38
+ const dmp = new diff_match_patch_1.default();
39
+ const diff = dmp.diff_main(codeA, codeB);
40
+ dmp.diff_cleanupSemantic(diff);
41
+ const linesB = countLines(codeB) + 1;
42
+ diff_pretty(diff, linesB, lineBuff);
43
+ };
44
+ exports.diffCode = diffCode;
45
+ /**
46
+ * Convert a diff array into human-readable for the console
47
+ * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
48
+ * @param lines number of a lines in the second contract B
49
+ * @param lineBuff number of a lines to output before and after the change
50
+ */
51
+ const diff_pretty = (diffs, lines, lineBuff = 2) => {
52
+ const linePad = lines.toString().length;
53
+ let output = '';
54
+ let diffIndex = 0;
55
+ let lineCount = 1;
56
+ const firstLineNumber = '1'.padStart(linePad) + ' ';
57
+ for (const diff of diffs) {
58
+ diffIndex++;
59
+ const initialLineNumber = diffIndex <= 1 ? firstLineNumber : '';
60
+ const op = diff[0]; // Operation (insert, delete, equal)
61
+ const text = diff[1]; // Text of change.
62
+ switch (op) {
63
+ case diff_match_patch_1.DIFF_INSERT:
64
+ // If first diff then we need to add the first line number
65
+ const linesInserted = addLineNumbers(text, lineCount, linePad);
66
+ output += initialLineNumber + clc.green(linesInserted);
67
+ lineCount += countLines(text);
68
+ break;
69
+ case diff_match_patch_1.DIFF_DELETE:
70
+ // zero start line means blank line numbers are used
71
+ const linesDeleted = addLineNumbers(text, 0, linePad);
72
+ output += initialLineNumber + clc.red(linesDeleted);
73
+ break;
74
+ case diff_match_patch_1.DIFF_EQUAL:
75
+ const eolPositions = findEOLPositions(text);
76
+ // If no changes yet
77
+ if (diffIndex <= 1) {
78
+ output += lastLines(text, eolPositions, lineBuff, linePad);
79
+ }
80
+ // if no more changes
81
+ else if (diffIndex === diffs.length) {
82
+ output += firstLines(text, eolPositions, lineBuff, lineCount, linePad);
83
+ }
84
+ else {
85
+ // else the first n lines and last n lines
86
+ output += firstAndLastLines(text, eolPositions, lineBuff, lineCount, linePad);
87
+ }
88
+ lineCount += eolPositions.length;
89
+ break;
90
+ }
91
+ }
92
+ output += '\n';
93
+ console.log(output);
94
+ };
95
+ /**
96
+ * Used when there is no more changes left
97
+ */
98
+ const firstLines = (text, eolPositions, lineBuff, lineStart, linePad) => {
99
+ const lines = text.slice(0, eolPositions[lineBuff]);
100
+ return addLineNumbers(lines, lineStart, linePad);
101
+ };
102
+ /**
103
+ * Used before the first change
104
+ */
105
+ const lastLines = (text, eolPositions, lineBuff, linePad) => {
106
+ const eolFrom = eolPositions.length - (lineBuff + 1);
107
+ let lines = text;
108
+ let lineCount = 1;
109
+ if (eolFrom >= 0) {
110
+ lines = eolFrom >= 0 ? text.slice(eolPositions[eolFrom] + 1) : text;
111
+ lineCount = eolFrom + 2;
112
+ }
113
+ const firstLineNumber = lineCount.toString().padStart(linePad) + ' ';
114
+ return firstLineNumber + addLineNumbers(lines, lineCount, linePad);
115
+ };
116
+ /**
117
+ * Used between changes to show the lines after the last change and before the next change.
118
+ * @param text
119
+ * @param eolPositions
120
+ * @param lineBuff
121
+ * @param lineStart
122
+ * @param linePad
123
+ */
124
+ const firstAndLastLines = (text, eolPositions, lineBuff, lineStart, linePad) => {
125
+ if (eolPositions.length <= 2 * lineBuff) {
126
+ return addLineNumbers(text, lineStart, linePad);
127
+ }
128
+ const endFirstLines = eolPositions[lineBuff];
129
+ const eolFrom = eolPositions.length - (lineBuff + 1);
130
+ const startLastLines = eolPositions[eolFrom];
131
+ if (startLastLines <= endFirstLines) {
132
+ return addLineNumbers(text, lineStart, linePad);
133
+ }
134
+ // Lines after the previous change
135
+ let lines = text.slice(0, endFirstLines);
136
+ let output = addLineNumbers(lines, lineStart, linePad);
137
+ output += SkippedLinesMarker;
138
+ // Lines before the next change
139
+ lines = text.slice(startLastLines);
140
+ const lineCount = lineStart + eolFrom;
141
+ output += addLineNumbers(lines, lineCount, linePad);
142
+ return output;
143
+ };
144
+ /**
145
+ * Gets the positions of the end of lines in the string
146
+ * @param text
147
+ */
148
+ const findEOLPositions = (text) => {
149
+ const eolPositions = [];
150
+ text.split('').forEach((c, i) => {
151
+ if (c === '\n') {
152
+ eolPositions.push(i);
153
+ }
154
+ });
155
+ return eolPositions;
156
+ };
157
+ /**
158
+ * Counts the number of carriage returns in a string
159
+ * @param text
160
+ */
161
+ const countLines = (text) => (text.match(/\n/g) || '').length;
162
+ /**
163
+ * Adds left padded line numbers to each line.
164
+ * @param text with the lines of code
165
+ * @param lineStart the line number of the first line in the text. If zero, then no lines numbers are added.
166
+ * @param linePad the width of the largest number which may not be in the text
167
+ */
168
+ const addLineNumbers = (text, lineStart, linePad) => {
169
+ let lineCount = lineStart;
170
+ let textWithLineNumbers = '';
171
+ text.split('').forEach((c, i) => {
172
+ if (c === '\n') {
173
+ if (lineStart > 0) {
174
+ textWithLineNumbers += `\n${(++lineCount)
175
+ .toString()
176
+ .padStart(linePad)} `;
177
+ }
178
+ else {
179
+ textWithLineNumbers += `\n${' '.repeat(linePad)} `;
180
+ }
181
+ }
182
+ else {
183
+ textWithLineNumbers += c;
184
+ }
185
+ });
186
+ return textWithLineNumbers;
187
+ };
1
188
  //# sourceMappingURL=diff.js.map
@@ -1,2 +1,4 @@
1
+ export declare const ethereumAddress: RegExp;
2
+ export declare const ethereumAddresses: RegExp;
1
3
  export declare const isAddress: (input: string) => boolean;
2
4
  export declare const parseSolidityVersion: (compilerVersion: string) => string;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseSolidityVersion = exports.isAddress = void 0;
3
+ exports.parseSolidityVersion = exports.isAddress = exports.ethereumAddresses = exports.ethereumAddress = void 0;
4
+ exports.ethereumAddress = /^0x([A-Fa-f0-9]{40})$/;
5
+ // comma-separated list of addresses with no whitespace
6
+ exports.ethereumAddresses = /^(0x[A-Fa-f0-9]{40},?)+$/;
4
7
  const isAddress = (input) => {
5
8
  return input.match(/^0x([A-Fa-f0-9]{40})$/) !== null;
6
9
  };
@@ -0,0 +1,3 @@
1
+ export declare const validateAddress: (address: string) => string;
2
+ export declare const validateAddresses: (addresses: string) => string[];
3
+ export declare const validateLineBuffer: (lineBufferParam: string) => number;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateLineBuffer = exports.validateAddresses = exports.validateAddress = void 0;
4
+ const regEx_1 = require("./regEx");
5
+ const commander_1 = require("commander");
6
+ const utils_1 = require("ethers/lib/utils");
7
+ const validateAddress = (address) => {
8
+ try {
9
+ if (typeof address === 'string' && address?.match(regEx_1.ethereumAddress))
10
+ return (0, utils_1.getAddress)(address);
11
+ }
12
+ catch (err) { }
13
+ throw new commander_1.InvalidArgumentError(`Address must be in hexadecimal format with a 0x prefix.`);
14
+ };
15
+ exports.validateAddress = validateAddress;
16
+ const validateAddresses = (addresses) => {
17
+ try {
18
+ const addressArray = convertAddresses(addresses);
19
+ if (addressArray)
20
+ return addressArray;
21
+ }
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;
33
+ };
34
+ 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;
39
+ };
40
+ exports.validateLineBuffer = validateLineBuffer;
41
+ //# sourceMappingURL=validators.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol2uml",
3
- "version": "2.5.6",
3
+ "version": "2.5.7",
4
4
  "description": "Solidity contract visualisation tool.",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -23,11 +23,11 @@
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
25
  "@aduh95/viz.js": "^3.7.0",
26
- "@solidity-parser/parser": "^0.15.0",
26
+ "@solidity-parser/parser": "^0.16.1",
27
27
  "axios": "^1.3.4",
28
28
  "axios-debug-log": "^1.0.0",
29
29
  "cli-color": "^2.0.3",
30
- "commander": "^10.0.0",
30
+ "commander": "^11.0.0",
31
31
  "convert-svg-to-png": "^0.6.4",
32
32
  "debug": "^4.3.4",
33
33
  "diff-match-patch": "^1.0.5",
@@ -36,15 +36,15 @@
36
36
  "klaw": "^4.1.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@openzeppelin/contracts": "^4.8.2",
39
+ "@openzeppelin/contracts": "^4.9.3",
40
40
  "@types/diff-match-patch": "^1.0.32",
41
- "@types/jest": "^29.4.0",
41
+ "@types/jest": "^29.5.3",
42
42
  "@types/klaw": "^3.0.3",
43
- "jest": "^29.4.3",
44
- "prettier": "^2.8.4",
45
- "ts-jest": "^29.0.5",
43
+ "jest": "^29.6.2",
44
+ "prettier": "^3.0.1",
45
+ "ts-jest": "^29.1.1",
46
46
  "ts-node": "^10.9.1",
47
- "typescript": "^4.9.5"
47
+ "typescript": "^5.1.6"
48
48
  },
49
49
  "files": [
50
50
  "lib/*.js",