sol2uml 2.5.4 → 2.5.6

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
@@ -55,7 +55,9 @@ Options:
55
55
  -f, --outputFormat <value> output file format. (choices: "svg", "png", "dot", "all", default: "svg")
56
56
  -o, --outputFileName <value> output file name
57
57
  -i, --ignoreFilesOrFolders <filesOrFolders> comma separated list of files or folders to ignore
58
- -n, --network <network> Ethereum network (choices: "mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis", default: "mainnet", env: ETH_NETWORK)
58
+ -n, --network <network> Ethereum network which maps to a blockchain explorer (choices: "mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam",
59
+ "optimism", "gnosis", "celo", default: "mainnet", env: ETH_NETWORK)
60
+ -e, --explorerUrl <url> Override network with custom blockchain explorer API URL. eg Polygon Mumbai testnet https://api-testnet.polygonscan.com/api (env: EXPLORER_URL)
59
61
  -k, --apiKey <key> Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key (env: SCAN_API_KEY)
60
62
  -bc, --backColor <color> Canvas background color. "none" will use a transparent canvas. (default: "white")
61
63
  -sc, --shapeColor <color> Basic drawing color for graphics, not text (default: "black")
@@ -192,6 +194,8 @@ Arguments:
192
194
 
193
195
  Options:
194
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)
195
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)
196
200
  -h, --help display help for command
197
201
  ```
@@ -329,6 +333,25 @@ To access your local changes on your machine globally.
329
333
 
330
334
  `npm link`
331
335
 
336
+ # Publish
337
+
338
+ Commands to publish a new package version.
339
+
340
+ ```bash
341
+ npm run prettier
342
+ npm run clean
343
+ npm run package-lock
344
+ npm run build
345
+ npm run permit
346
+ # make tx2uml globally available for local testing
347
+ npm link
348
+ # check all the files are included in the npm package
349
+ npm pack --dry-run
350
+ npm publish
351
+ ```
352
+
353
+ Then create a new release on GitHub https://github.com/naddison36/sol2uml/releases
354
+
332
355
  # About
333
356
 
334
357
  This is a rewrite of the Richard Ramos's [solidity-diagram-gen](https://github.com/richard-ramos/solidity-diagram-gen) tool which no longer works as it uses [solidity-parser](https://www.npmjs.com/package/solidity-parser/v/0.4.0) which cannot handle newer Solidity syntax like `constructor`.
@@ -102,7 +102,7 @@ function convertAST2UmlClasses(node, relativePath, remappings, filesystem = fals
102
102
  })
103
103
  : [],
104
104
  };
105
- debug(`Added filesystem import ${newImport.absolutePath} with class names ${newImport.classNames}`);
105
+ debug(`Added filesystem import ${newImport.absolutePath} with class names ${newImport.classNames.map((i) => i.className)}`);
106
106
  imports.push(newImport);
107
107
  }
108
108
  catch (err) {
@@ -87,7 +87,7 @@ function loadWeightedDirectedGraph(umlClasses) {
87
87
  continue;
88
88
  }
89
89
  const isTarget = umlClasses.find((u) => u.id === targetUmlClass.id);
90
- debug(`isTarget ${isTarget} Adding edge from ${sourceUmlClass.name} with id ${sourceUmlClass.id} to ${targetUmlClass.name} with id ${targetUmlClass.id} and type ${targetUmlClass.stereotype}`);
90
+ debug(`isTarget ${!!isTarget}: Adding edge from ${sourceUmlClass.name} with id ${sourceUmlClass.id} to ${targetUmlClass.name} with id ${targetUmlClass.id} and type ${targetUmlClass.stereotype}`);
91
91
  weightedDirectedGraph.addEdge(new js_graph_algorithms_1.Edge(sourceUmlClass.id, targetUmlClass.id, 1));
92
92
  }
93
93
  }
@@ -4,13 +4,13 @@ export interface Remapping {
4
4
  from: RegExp;
5
5
  to: string;
6
6
  }
7
- export declare const networks: readonly ["mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis"];
7
+ export declare const networks: readonly ["mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis", "celo"];
8
8
  export type Network = (typeof networks)[number];
9
9
  export declare class EtherscanParser {
10
10
  protected apikey: string;
11
11
  network: Network;
12
12
  readonly url: string;
13
- constructor(apikey?: string, network?: Network);
13
+ constructor(apikey?: string, network?: Network, url?: string);
14
14
  /**
15
15
  * Parses the verified source code files from Etherscan
16
16
  * @param contractAddress Ethereum contract address with a 0x prefix
@@ -26,7 +26,7 @@ export declare class EtherscanParser {
26
26
  * @param contractAddress Ethereum contract address with a 0x prefix
27
27
  * @return Promise string of Solidity code
28
28
  */
29
- getSolidityCode(contractAddress: string): Promise<{
29
+ getSolidityCode(contractAddress: string, filename?: string): Promise<{
30
30
  solidityCode: string;
31
31
  contractName: string;
32
32
  }>;
@@ -39,8 +39,9 @@ export declare class EtherscanParser {
39
39
  /**
40
40
  * Calls Etherscan to get the verified source code for the specified contract address
41
41
  * @param contractAddress Ethereum contract address with a 0x prefix
42
+ * @oaram filename optional, case-sensitive name of the source file without the .sol
42
43
  */
43
- getSourceCode(contractAddress: string): Promise<{
44
+ getSourceCode(contractAddress: string, filename?: string): Promise<{
44
45
  files: readonly {
45
46
  code: string;
46
47
  filename: string;
@@ -9,6 +9,7 @@ const parser_1 = require("@solidity-parser/parser");
9
9
  const converterAST2Classes_1 = require("./converterAST2Classes");
10
10
  const filterClasses_1 = require("./filterClasses");
11
11
  const regEx_1 = require("./utils/regEx");
12
+ const path_1 = __importDefault(require("path"));
12
13
  require('axios-debug-log');
13
14
  const debug = require('debug')('sol2uml');
14
15
  exports.networks = [
@@ -24,11 +25,16 @@ exports.networks = [
24
25
  'moonbeam',
25
26
  'optimism',
26
27
  'gnosis',
28
+ 'celo',
27
29
  ];
28
30
  class EtherscanParser {
29
- constructor(apikey = 'ZAD4UI2RCXCQTP38EXS3UY2MPHFU5H9KB1', network = 'mainnet') {
31
+ constructor(apikey = 'ZAD4UI2RCXCQTP38EXS3UY2MPHFU5H9KB1', network = 'mainnet', url) {
30
32
  this.apikey = apikey;
31
33
  this.network = network;
34
+ if (url) {
35
+ this.url = url;
36
+ return;
37
+ }
32
38
  if (!exports.networks.includes(network)) {
33
39
  throw new Error(`Invalid network "${network}". Must be one of ${exports.networks}`);
34
40
  }
@@ -71,6 +77,10 @@ class EtherscanParser {
71
77
  this.url = 'https://api.gnosisscan.io/api';
72
78
  this.apikey = '2RWGXIWK538EJ8XSP9DE2JUINSCG7UCSJB';
73
79
  }
80
+ else if (network === 'celo') {
81
+ this.url = 'https://api.celoscan.io/api';
82
+ this.apikey = 'JBV78T5KP15W7WKKKD6KC4J8RX2F4PK8AF';
83
+ }
74
84
  else {
75
85
  this.url = `https://api-${network}.etherscan.io/api`;
76
86
  }
@@ -100,8 +110,8 @@ class EtherscanParser {
100
110
  * @param contractAddress Ethereum contract address with a 0x prefix
101
111
  * @return Promise string of Solidity code
102
112
  */
103
- async getSolidityCode(contractAddress) {
104
- const { files, contractName, compilerVersion, remappings } = await this.getSourceCode(contractAddress);
113
+ async getSolidityCode(contractAddress, filename) {
114
+ const { files, contractName, compilerVersion, remappings } = await this.getSourceCode(contractAddress, filename);
105
115
  // Parse the UmlClasses from the Solidity code in each file
106
116
  let umlClasses = [];
107
117
  for (const file of files) {
@@ -164,8 +174,9 @@ class EtherscanParser {
164
174
  /**
165
175
  * Calls Etherscan to get the verified source code for the specified contract address
166
176
  * @param contractAddress Ethereum contract address with a 0x prefix
177
+ * @oaram filename optional, case-sensitive name of the source file without the .sol
167
178
  */
168
- async getSourceCode(contractAddress) {
179
+ async getSourceCode(contractAddress, filename) {
169
180
  const description = `get verified source code for address ${contractAddress} from Etherscan API.`;
170
181
  try {
171
182
  debug(`About to get Solidity source code for ${contractAddress} from ${this.url}`);
@@ -226,8 +237,16 @@ class EtherscanParser {
226
237
  filename: contractAddress,
227
238
  };
228
239
  });
240
+ let files = results.flat(1);
241
+ const filenameWithExt = filename + '.sol';
242
+ if (filename) {
243
+ files = files.filter((r) => path_1.default.parse(r.filename).base == filenameWithExt);
244
+ if (!files?.length) {
245
+ throw new Error(`Failed to find source file "${filename}" for contract ${contractAddress}`);
246
+ }
247
+ }
229
248
  return {
230
- files: results.flat(1),
249
+ files,
231
250
  contractName: response.data.result[0].ContractName,
232
251
  compilerVersion: response.data.result[0].CompilerVersion,
233
252
  remappings,
@@ -3,6 +3,7 @@ import { UmlClass } from './umlClass';
3
3
  export interface ParserOptions {
4
4
  apiKey?: string;
5
5
  network?: Network;
6
+ explorerUrl?: string;
6
7
  subfolders?: string;
7
8
  ignoreFilesOrFolders?: string;
8
9
  }
@@ -17,7 +17,7 @@ const parserUmlClasses = async (fileFolderAddress, options) => {
17
17
  if ((0, regEx_1.isAddress)(fileFolderAddress)) {
18
18
  debug(`argument ${fileFolderAddress} is an Ethereum address so checking Etherscan for the verified source code`);
19
19
  const etherscanApiKey = options.apiKey || 'ZAD4UI2RCXCQTP38EXS3UY2MPHFU5H9KB1';
20
- const etherscanParser = new parserEtherscan_1.EtherscanParser(etherscanApiKey, options.network);
20
+ const etherscanParser = new parserEtherscan_1.EtherscanParser(etherscanApiKey, options.network, options.explorerUrl);
21
21
  result = await etherscanParser.getUmlClasses(fileFolderAddress);
22
22
  }
23
23
  else {
package/lib/sol2uml.js CHANGED
@@ -29,10 +29,11 @@ Can also flatten or compare verified source files on Etherscan-like explorers.`)
29
29
  .default('svg'))
30
30
  .option('-o, --outputFileName <value>', 'output file name')
31
31
  .option('-i, --ignoreFilesOrFolders <filesOrFolders>', 'comma separated list of files or folders to ignore')
32
- .addOption(new commander_1.Option('-n, --network <network>', 'Ethereum network')
32
+ .addOption(new commander_1.Option('-n, --network <network>', 'Ethereum network which maps to a blockchain explorer')
33
33
  .choices(parserEtherscan_1.networks)
34
34
  .default('mainnet')
35
35
  .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'))
36
37
  .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'))
37
38
  .option('-bc, --backColor <color>', 'Canvas background color. "none" will use a transparent canvas.', 'white')
38
39
  .option('-sc, --shapeColor <color>', 'Basic drawing color for graphics, not text', 'black')
@@ -202,7 +203,7 @@ In order for the merged code to compile, the following is done:
202
203
  ...command.parent._optionValues,
203
204
  ...options,
204
205
  };
205
- const etherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network);
206
+ const etherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network, combinedOptions.explorerUrl);
206
207
  const { solidityCode, contractName } = await etherscanParser.getSolidityCode(contractAddress);
207
208
  // Write Solidity to the contract address
208
209
  const outputFilename = combinedOptions.outputFileName || contractName;
@@ -225,6 +226,8 @@ The line numbers are from contract B. There are no line numbers for the red sect
225
226
  .argument('<addressA>', 'Contract address in hexadecimal format with a 0x prefix of the first contract.')
226
227
  .argument('<addressB>', 'Contract address in hexadecimal format with a 0x prefix of the second contract.')
227
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)'))
228
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)
229
232
  .action(async (addressA, addressB, options, command) => {
230
233
  try {
@@ -237,10 +240,10 @@ The line numbers are from contract B. There are no line numbers for the red sect
237
240
  const lineBuffer = parseInt(options.lineBuffer);
238
241
  if (isNaN(lineBuffer))
239
242
  throw Error(`Invalid line buffer "${options.lineBuffer}". Must be a number`);
240
- const etherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network);
243
+ const etherscanParser = new parserEtherscan_1.EtherscanParser(combinedOptions.apiKey, combinedOptions.network, combinedOptions.explorerUrl);
241
244
  // Get verified Solidity code from Etherscan and flatten
242
- const { solidityCode: codeA, contractName: contractNameA } = await etherscanParser.getSolidityCode(addressA);
243
- const { solidityCode: codeB, contractName: contractNameB } = await etherscanParser.getSolidityCode(addressB);
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);
244
247
  console.log(`Difference between`);
245
248
  console.log(`A. ${addressA} ${contractNameA} on ${combinedOptions.network}`);
246
249
  console.log(`B. ${addressB} ${contractNameB} on ${combinedOptions.network}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol2uml",
3
- "version": "2.5.4",
3
+ "version": "2.5.6",
4
4
  "description": "Solidity contract visualisation tool.",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",