sol2uml 2.5.0 → 2.5.2
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 +47 -35
- package/lib/converterClasses2Storage.js +12 -7
- package/lib/converterStorage2Dot.d.ts +2 -0
- package/lib/converterStorage2Dot.js +1 -1
- package/lib/parserEtherscan.d.ts +1 -1
- package/lib/parserEtherscan.js +5 -34
- package/lib/sol2uml.js +31 -38
- package/package.json +12 -9
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Solidity 2 UML
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/sol2uml)
|
|
4
|
+
[](https://twitter.com/naddison)
|
|
4
5
|
|
|
5
6
|
A visualisation tool for [Solidity](https://solidity.readthedocs.io/) contracts featuring:
|
|
6
7
|
1. [Unified Modeling Language (UML)](https://en.wikipedia.org/wiki/Unified_Modeling_Language) [class diagram](https://en.wikipedia.org/wiki/Class_diagram) generator for Solidity contracts.
|
|
@@ -14,7 +15,7 @@ See more contract diagrams [here](./examples/README.md).
|
|
|
14
15
|
Storage layout diagram of USDC's [verified source code](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code) on Etherscan.
|
|
15
16
|

|
|
16
17
|
|
|
17
|
-
See an explanation of how storage diagrams work with lots of examples [here](./examples/storage/README.md).
|
|
18
|
+
See an explanation of how storage diagrams work with lots of examples [here](./examples/storage/README.md#example-storage-diagrams).
|
|
18
19
|
|
|
19
20
|
# Install
|
|
20
21
|
|
|
@@ -44,22 +45,17 @@ npm ls sol2uml -g
|
|
|
44
45
|
## Command Line Interface (CLI)
|
|
45
46
|
|
|
46
47
|
```
|
|
47
|
-
Usage: sol2uml [
|
|
48
|
-
The three subcommands:
|
|
49
|
-
* class: Generates a UML class diagram from Solidity source code. default
|
|
50
|
-
* storage: Generates a diagram of a contract's storage slots.
|
|
51
|
-
* flatten: Merges verified source files from a Blockchain explorer into one local file.
|
|
52
|
-
* diff: Compares the flattened Solidity code from a Blockchain explorer for two contracts.
|
|
48
|
+
Usage: sol2uml [command] <options>
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
Generate UML class or storage diagrams from local Solidity code or verified Solidity code on Etherscan-like explorers.
|
|
51
|
+
Can also flatten or compare verified source files on Etherscan-like explorers.
|
|
55
52
|
|
|
56
53
|
Options:
|
|
57
54
|
-sf, --subfolders <value> number of subfolders that will be recursively searched for Solidity files. (default: all)
|
|
58
55
|
-f, --outputFormat <value> output file format. (choices: "svg", "png", "dot", "all", default: "svg")
|
|
59
56
|
-o, --outputFileName <value> output file name
|
|
60
57
|
-i, --ignoreFilesOrFolders <filesOrFolders> comma separated list of files or folders to ignore
|
|
61
|
-
-n, --network <network> Ethereum network (choices: "mainnet", "
|
|
62
|
-
"testnet.fantom", "moonbeam", "optimistic", "kovan-optimistic", "gnosisscan", default: "mainnet", env: ETH_NETWORK)
|
|
58
|
+
-n, --network <network> Ethereum network (choices: "mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis", default: "mainnet", env: ETH_NETWORK)
|
|
63
59
|
-k, --apiKey <key> Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key (env: SCAN_API_KEY)
|
|
64
60
|
-bc, --backColor <color> Canvas background color. "none" will use a transparent canvas. (default: "white")
|
|
65
61
|
-sc, --shapeColor <color> Basic drawing color for graphics, not text (default: "black")
|
|
@@ -70,9 +66,24 @@ Options:
|
|
|
70
66
|
-h, --help display help for command
|
|
71
67
|
|
|
72
68
|
Commands:
|
|
73
|
-
class [options]
|
|
74
|
-
storage [options] <fileFolderAddress>
|
|
75
|
-
|
|
69
|
+
class [options] <fileFolderAddress> Generates a UML class diagram from Solidity source code.
|
|
70
|
+
storage [options] <fileFolderAddress> Visually display a contract's storage slots.
|
|
71
|
+
|
|
72
|
+
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is fixed-sized arrays declared with an expression will fail to be sized.
|
|
73
|
+
flatten <contractAddress> Merges verified source files for a contract from a Blockchain explorer into one local file.
|
|
74
|
+
|
|
75
|
+
In order for the merged code to compile, the following is done:
|
|
76
|
+
1. pragma solidity is set using the compiler of the verified contract.
|
|
77
|
+
2. All pragma solidity lines in the source files are commented out.
|
|
78
|
+
3. File imports are commented out.
|
|
79
|
+
4. "SPDX-License-Identifier" is renamed to "SPDX--License-Identifier".
|
|
80
|
+
5. Contract dependencies are analysed so the files are merged in an order that will compile.
|
|
81
|
+
diff [options] <addressA> <addressB> Compare verified Solidity code differences between two contracts.
|
|
82
|
+
|
|
83
|
+
The results show the comparison of contract A to B.
|
|
84
|
+
The green sections are additions to contract B that are not in contract A.
|
|
85
|
+
The red sections are removals from contract A that are not in contract B.
|
|
86
|
+
The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B.
|
|
76
87
|
help [command] display help for command
|
|
77
88
|
```
|
|
78
89
|
|
|
@@ -81,20 +92,15 @@ Commands:
|
|
|
81
92
|
```
|
|
82
93
|
Usage: sol2uml class [options] <fileFolderAddress>
|
|
83
94
|
|
|
84
|
-
Generates UML diagrams from Solidity source code.
|
|
85
|
-
|
|
86
|
-
If no file, folder or address is passed as the first argument, the working folder is used.
|
|
87
|
-
When a folder is used, all *.sol files are found in that folder and all sub folders.
|
|
88
|
-
A comma separated list of files and folders can also be used. For example
|
|
89
|
-
sol2uml contracts,node_modules/openzeppelin-solidity
|
|
90
|
-
|
|
91
|
-
If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
92
|
-
sol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9
|
|
93
|
-
|
|
94
95
|
Generates a UML class diagram from Solidity source code.
|
|
95
96
|
|
|
96
97
|
Arguments:
|
|
97
|
-
fileFolderAddress file name,
|
|
98
|
+
fileFolderAddress file name, folder(s) or contract address.
|
|
99
|
+
When a folder is used, all *.sol files in that folder and all sub folders are used.
|
|
100
|
+
A comma-separated list of files and folders can also be used. For example
|
|
101
|
+
sol2uml contracts,node_modules/openzeppelin-solidity
|
|
102
|
+
If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
103
|
+
sol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9
|
|
98
104
|
|
|
99
105
|
Options:
|
|
100
106
|
-b, --baseContractNames <value> only output contracts connected to these comma separated base contract names
|
|
@@ -122,12 +128,17 @@ Options:
|
|
|
122
128
|
```
|
|
123
129
|
Usage: sol2uml storage [options] <fileFolderAddress>
|
|
124
130
|
|
|
125
|
-
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is fixed-sized arrays declared with an expression will fail to be sized.
|
|
126
|
-
|
|
127
131
|
Visually display a contract's storage slots.
|
|
128
132
|
|
|
133
|
+
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is fixed-sized arrays declared with an expression will fail to be sized.
|
|
134
|
+
|
|
129
135
|
Arguments:
|
|
130
|
-
fileFolderAddress
|
|
136
|
+
fileFolderAddress file name, folder(s) or contract address.
|
|
137
|
+
When a folder is used, all *.sol files in that folder and all sub folders are used.
|
|
138
|
+
A comma-separated list of files and folders can also be used. For example
|
|
139
|
+
sol2uml contracts,node_modules/openzeppelin-solidity
|
|
140
|
+
If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
141
|
+
sol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9
|
|
131
142
|
|
|
132
143
|
Options:
|
|
133
144
|
-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.
|
|
@@ -137,6 +148,7 @@ Options:
|
|
|
137
148
|
-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)
|
|
138
149
|
-bn, --block <number> Block number to get the contract storage values from. (default: "latest")
|
|
139
150
|
-a, --array <number> Number of slots to display at the start and end of arrays. (default: "2")
|
|
151
|
+
-hv, --hideValue Hide storage slot value column. (default: false)
|
|
140
152
|
-h, --help display help for command
|
|
141
153
|
```
|
|
142
154
|
|
|
@@ -145,6 +157,8 @@ Options:
|
|
|
145
157
|
```
|
|
146
158
|
Usage: sol2uml flatten <contractAddress>
|
|
147
159
|
|
|
160
|
+
Merges verified source files for a contract from a Blockchain explorer into one local Solidity file.
|
|
161
|
+
|
|
148
162
|
In order for the merged code to compile, the following is done:
|
|
149
163
|
1. pragma solidity is set using the compiler of the verified contract.
|
|
150
164
|
2. All pragma solidity lines in the source files are commented out.
|
|
@@ -152,8 +166,6 @@ In order for the merged code to compile, the following is done:
|
|
|
152
166
|
4. "SPDX-License-Identifier" is renamed to "SPDX--License-Identifier".
|
|
153
167
|
5. Contract dependencies are analysed so the files are merged in an order that will compile.
|
|
154
168
|
|
|
155
|
-
Merges verified source files for a contract from a Blockchain explorer into one local file.
|
|
156
|
-
|
|
157
169
|
Arguments:
|
|
158
170
|
contractAddress Contract address in hexadecimal format with a 0x prefix.
|
|
159
171
|
|
|
@@ -166,16 +178,16 @@ Options:
|
|
|
166
178
|
```
|
|
167
179
|
Usage: sol2uml diff [options] <addressA> <addressB>
|
|
168
180
|
|
|
169
|
-
|
|
181
|
+
Compare verified Solidity code differences between two contracts.
|
|
182
|
+
|
|
183
|
+
The results show the comparison of contract A to B.
|
|
170
184
|
The green sections are additions to contract B that are not in contract A.
|
|
171
185
|
The red sections are removals from contract A that are not in contract B.
|
|
172
186
|
The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B.
|
|
173
187
|
|
|
174
|
-
Compare verified Solidity code differences between two contracts.
|
|
175
|
-
|
|
176
188
|
Arguments:
|
|
177
|
-
addressA Contract address in hexadecimal format with a 0x prefix.
|
|
178
|
-
addressB Contract address in hexadecimal format with a 0x prefix.
|
|
189
|
+
addressA Contract address in hexadecimal format with a 0x prefix of the first contract.
|
|
190
|
+
addressB Contract address in hexadecimal format with a 0x prefix of the second contract.
|
|
179
191
|
|
|
180
192
|
Options:
|
|
181
193
|
-l, --lineBuffer <value> Minimum number of lines before and after changes (default: "4")
|
|
@@ -267,7 +267,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
267
267
|
}
|
|
268
268
|
if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) {
|
|
269
269
|
// Is the user defined type linked to another Contract, Struct or Enum?
|
|
270
|
-
const typeClass = findTypeClass(attribute.type, attribute, otherClasses);
|
|
270
|
+
const typeClass = findTypeClass(attribute.type, attribute, umlClass, otherClasses);
|
|
271
271
|
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
272
272
|
const variables = parseVariables(typeClass, otherClasses, [], storageSections, [], mapping, arrayItems);
|
|
273
273
|
const storageSection = {
|
|
@@ -296,7 +296,7 @@ const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, sto
|
|
|
296
296
|
// If mapping of user defined type
|
|
297
297
|
if (result !== null && result[1] && !(0, exports.isElementary)(result[1])) {
|
|
298
298
|
// Find UserDefined type can be a contract, struct or enum
|
|
299
|
-
const typeClass = findTypeClass(result[1], attribute, otherClasses);
|
|
299
|
+
const typeClass = findTypeClass(result[1], attribute, umlClass, otherClasses);
|
|
300
300
|
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
301
301
|
let variables = parseVariables(typeClass, otherClasses, [], storageSections, [], true, arrayItems);
|
|
302
302
|
const storageSection = {
|
|
@@ -380,12 +380,17 @@ const addArrayVariables = (arrayLength, arrayItems, variables) => {
|
|
|
380
380
|
* Finds an attribute's user defined type that can be a Contract, Struct or Enum
|
|
381
381
|
* @param userType User defined type that is being looked for. This can be the base type of an attribute.
|
|
382
382
|
* @param attribute the attribute in the class that is user defined. This is just used for logging purposes
|
|
383
|
+
* @param umlClass the attribute is part of.
|
|
383
384
|
* @param otherClasses
|
|
384
385
|
*/
|
|
385
|
-
const findTypeClass = (userType, attribute, otherClasses) => {
|
|
386
|
+
const findTypeClass = (userType, attribute, umlClass, otherClasses) => {
|
|
386
387
|
// Find associated UserDefined type
|
|
387
|
-
|
|
388
|
-
const
|
|
388
|
+
const types = userType.split('.');
|
|
389
|
+
const association = {
|
|
390
|
+
referenceType: umlClass_1.ReferenceType.Memory,
|
|
391
|
+
targetUmlClassName: types.length === 1 ? types[0] : types[1],
|
|
392
|
+
};
|
|
393
|
+
const typeClass = (0, associations_1.findAssociatedClass)(association, umlClass, otherClasses);
|
|
389
394
|
if (!typeClass) {
|
|
390
395
|
throw Error(`Failed to find user defined type "${userType}" in attribute "${attribute.name}" of type "${attribute.attributeType}""`);
|
|
391
396
|
}
|
|
@@ -471,7 +476,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
471
476
|
// TODO need to handle User Defined Value Types when they are added to Solidity
|
|
472
477
|
if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) {
|
|
473
478
|
// Is the user defined type linked to another Contract, Struct or Enum?
|
|
474
|
-
const attributeTypeClass = findTypeClass(attribute.type, attribute, otherClasses);
|
|
479
|
+
const attributeTypeClass = findTypeClass(attribute.type, attribute, umlClass, otherClasses);
|
|
475
480
|
switch (attributeTypeClass.stereotype) {
|
|
476
481
|
case umlClass_1.ClassStereotype.Enum:
|
|
477
482
|
return { size: 1, dynamic: false };
|
|
@@ -491,7 +496,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
491
496
|
else if (structAttribute.attributeType ===
|
|
492
497
|
umlClass_1.AttributeType.UserDefined) {
|
|
493
498
|
// UserDefined types can be a struct or enum, so we need to check if it's a struct
|
|
494
|
-
const userDefinedClass = findTypeClass(structAttribute.type, structAttribute, otherClasses);
|
|
499
|
+
const userDefinedClass = findTypeClass(structAttribute.type, structAttribute, umlClass, otherClasses);
|
|
495
500
|
// If a struct
|
|
496
501
|
if (userDefinedClass.stereotype ===
|
|
497
502
|
umlClass_1.ClassStereotype.Struct) {
|
|
@@ -5,7 +5,9 @@ export declare const convertStorages2Dot: (storageSections: readonly StorageSect
|
|
|
5
5
|
shapeColor: string;
|
|
6
6
|
fillColor: string;
|
|
7
7
|
textColor: string;
|
|
8
|
+
hideValues?: boolean;
|
|
8
9
|
}) => string;
|
|
9
10
|
export declare function convertStorage2Dot(storageSection: StorageSection, dotString: string, options: {
|
|
10
11
|
data: boolean;
|
|
12
|
+
hideValues?: boolean;
|
|
11
13
|
}): string;
|
|
@@ -54,7 +54,7 @@ function convertStorage2Dot(storageSection, dotString, options) {
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
// write slot values if available
|
|
57
|
-
if (options.data) {
|
|
57
|
+
if (options.data && !options.hideValues) {
|
|
58
58
|
dotString += `} | {value${dataLine}`;
|
|
59
59
|
startingVariables.forEach((variable, i) => {
|
|
60
60
|
if (displayData[i]) {
|
package/lib/parserEtherscan.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface Remapping {
|
|
|
4
4
|
from: RegExp;
|
|
5
5
|
to: string;
|
|
6
6
|
}
|
|
7
|
-
export declare const networks: readonly ["mainnet", "
|
|
7
|
+
export declare const networks: readonly ["mainnet", "goerli", "sepolia", "polygon", "arbitrum", "avalanche", "bsc", "crono", "fantom", "moonbeam", "optimism", "gnosis"];
|
|
8
8
|
export type Network = (typeof networks)[number];
|
|
9
9
|
export declare class EtherscanParser {
|
|
10
10
|
protected apikey: string;
|
package/lib/parserEtherscan.js
CHANGED
|
@@ -13,26 +13,17 @@ require('axios-debug-log');
|
|
|
13
13
|
const debug = require('debug')('sol2uml');
|
|
14
14
|
exports.networks = [
|
|
15
15
|
'mainnet',
|
|
16
|
-
'ropsten',
|
|
17
|
-
'kovan',
|
|
18
|
-
'rinkeby',
|
|
19
16
|
'goerli',
|
|
20
17
|
'sepolia',
|
|
21
18
|
'polygon',
|
|
22
|
-
'testnet.polygon',
|
|
23
19
|
'arbitrum',
|
|
24
|
-
'testnet.arbitrum',
|
|
25
20
|
'avalanche',
|
|
26
|
-
'testnet.avalanche',
|
|
27
21
|
'bsc',
|
|
28
|
-
'testnet.bsc',
|
|
29
22
|
'crono',
|
|
30
23
|
'fantom',
|
|
31
|
-
'testnet.fantom',
|
|
32
24
|
'moonbeam',
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'gnosisscan',
|
|
25
|
+
'optimism',
|
|
26
|
+
'gnosis',
|
|
36
27
|
];
|
|
37
28
|
class EtherscanParser {
|
|
38
29
|
constructor(apikey = 'ZAD4UI2RCXCQTP38EXS3UY2MPHFU5H9KB1', network = 'mainnet') {
|
|
@@ -48,34 +39,18 @@ class EtherscanParser {
|
|
|
48
39
|
this.url = 'https://api.polygonscan.com/api';
|
|
49
40
|
this.apikey = 'AMHGNTV5A7XYGX2M781JB3RC1DZFVRWQEB';
|
|
50
41
|
}
|
|
51
|
-
else if (network === 'testnet.polygon') {
|
|
52
|
-
this.url = 'https://api-testnet.polygonscan.com/api';
|
|
53
|
-
this.apikey = 'AMHGNTV5A7XYGX2M781JB3RC1DZFVRWQEB';
|
|
54
|
-
}
|
|
55
42
|
else if (network === 'arbitrum') {
|
|
56
43
|
this.url = 'https://api.arbiscan.io/api';
|
|
57
44
|
this.apikey = 'ZGTK2TAGWMAB6IAC12BMK8YYPNCPIM8VDQ';
|
|
58
45
|
}
|
|
59
|
-
else if (network === 'testnet.arbitrum') {
|
|
60
|
-
this.url = 'https://api-testnet.arbiscan.io/api';
|
|
61
|
-
this.apikey = 'ZGTK2TAGWMAB6IAC12BMK8YYPNCPIM8VDQ';
|
|
62
|
-
}
|
|
63
46
|
else if (network === 'avalanche') {
|
|
64
47
|
this.url = 'https://api.snowtrace.io/api';
|
|
65
48
|
this.apikey = 'U5FAN98S5XNH5VI83TI4H35R9I4TDCKEJY';
|
|
66
49
|
}
|
|
67
|
-
else if (network === 'testnet.avalanche') {
|
|
68
|
-
this.url = 'https://api-testnet.snowtrace.io/api';
|
|
69
|
-
this.apikey = 'U5FAN98S5XNH5VI83TI4H35R9I4TDCKEJY';
|
|
70
|
-
}
|
|
71
50
|
else if (network === 'bsc') {
|
|
72
51
|
this.url = 'https://api.bscscan.com/api';
|
|
73
52
|
this.apikey = 'APYH49FXVY9UA3KTDI6F4WP3KPIC86NITN';
|
|
74
53
|
}
|
|
75
|
-
else if (network === 'testnet.bsc') {
|
|
76
|
-
this.url = 'https://api-testnet.bscscan.com/api';
|
|
77
|
-
this.apikey = 'APYH49FXVY9UA3KTDI6F4WP3KPIC86NITN';
|
|
78
|
-
}
|
|
79
54
|
else if (network === 'crono') {
|
|
80
55
|
this.url = 'https://api.cronoscan.com/api';
|
|
81
56
|
this.apikey = '76A3RG5WHTPMMR66E9SFI2EIDT6MP976W2';
|
|
@@ -84,19 +59,15 @@ class EtherscanParser {
|
|
|
84
59
|
this.url = 'https://api.ftmscan.com/api';
|
|
85
60
|
this.apikey = '71KRX13XPZMGR3D1Q85W78G2DSZ4JPMAEX';
|
|
86
61
|
}
|
|
87
|
-
else if (network === '
|
|
88
|
-
this.url =
|
|
89
|
-
this.apikey = '71KRX13XPZMGR3D1Q85W78G2DSZ4JPMAEX';
|
|
90
|
-
}
|
|
91
|
-
else if (network === 'optimistic' || network === 'kovan-optimistic') {
|
|
92
|
-
this.url = `https://api-${network}.etherscan.io/api`;
|
|
62
|
+
else if (network === 'optimism') {
|
|
63
|
+
this.url = `https://api-optimistic.etherscan.io/api`;
|
|
93
64
|
this.apikey = 'FEXS1HXVA4Y2RNTMEA8V1UTK21S4JWHH9U';
|
|
94
65
|
}
|
|
95
66
|
else if (network === 'moonbeam') {
|
|
96
67
|
this.url = 'https://api-moonbeam.moonscan.io/api';
|
|
97
68
|
this.apikey = '5EUFXW6TDC16VERF3D9SCWRRU6AEMTBHNJ';
|
|
98
69
|
}
|
|
99
|
-
else if (network === '
|
|
70
|
+
else if (network === 'gnosis') {
|
|
100
71
|
this.url = 'https://api.gnosisscan.io/api';
|
|
101
72
|
this.apikey = '2RWGXIWK538EJ8XSP9DE2JUINSCG7UCSJB';
|
|
102
73
|
}
|
package/lib/sol2uml.js
CHANGED
|
@@ -20,15 +20,9 @@ const program = new commander_1.Command();
|
|
|
20
20
|
const debugControl = require('debug');
|
|
21
21
|
const debug = require('debug')('sol2uml');
|
|
22
22
|
program
|
|
23
|
-
.usage(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* class: Generates a UML class diagram from Solidity source code. (default)
|
|
27
|
-
* storage: Generates a diagram of a contract's storage slots.
|
|
28
|
-
* flatten: Merges verified Solidity files from a Blockchain explorer into one local file.
|
|
29
|
-
* diff: Compares the flattened Solidity code from a Blockchain explorer for two contracts.
|
|
30
|
-
|
|
31
|
-
The Solidity code can be pulled from verified source code on Blockchain explorers like Etherscan or from local Solidity files.`)
|
|
23
|
+
.usage('[command] <options>')
|
|
24
|
+
.description(`Generate UML class or storage diagrams from local Solidity code or verified Solidity code on Etherscan-like explorers.
|
|
25
|
+
Can also flatten or compare verified source files on Etherscan-like explorers.`)
|
|
32
26
|
.addOption(new commander_1.Option('-sf, --subfolders <value>', 'number of subfolders that will be recursively searched for Solidity files.').default('-1', 'all'))
|
|
33
27
|
.addOption(new commander_1.Option('-f, --outputFormat <value>', 'output file format.')
|
|
34
28
|
.choices(['svg', 'png', 'dot', 'all'])
|
|
@@ -49,21 +43,17 @@ const version = (0, path_1.basename)(__dirname) === 'lib'
|
|
|
49
43
|
? require('../package.json').version // used when run from compile js in /lib
|
|
50
44
|
: require('../../package.json').version; // used when run from TypeScript source files under src/ts via ts-node
|
|
51
45
|
program.version(version);
|
|
46
|
+
const argumentText = `file name, folder(s) or contract address.
|
|
47
|
+
\t\t\t\t When a folder is used, all *.sol files in that folder and all sub folders are used.
|
|
48
|
+
\t\t\t\t A comma-separated list of files and folders can also be used. For example
|
|
49
|
+
\t\t\t\t\tsol2uml contracts,node_modules/openzeppelin-solidity
|
|
50
|
+
\t\t\t\t If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
51
|
+
\t\t\t\t\tsol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9`;
|
|
52
52
|
program
|
|
53
53
|
.command('class', { isDefault: true })
|
|
54
|
+
.usage('[options] <fileFolderAddress>')
|
|
54
55
|
.description('Generates a UML class diagram from Solidity source code.')
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
Generates UML diagrams from Solidity source code.
|
|
58
|
-
|
|
59
|
-
If no file, folder or address is passed as the first argument, the working folder is used.
|
|
60
|
-
When a folder is used, all *.sol files are found in that folder and all sub folders.
|
|
61
|
-
A comma separated list of files and folders can also be used. For example
|
|
62
|
-
sol2uml contracts,node_modules/openzeppelin-solidity
|
|
63
|
-
|
|
64
|
-
If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
65
|
-
sol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9`)
|
|
66
|
-
.argument('[fileFolderAddress]', 'file name, base folder or contract address', process.cwd())
|
|
56
|
+
.argument('fileFolderAddress', argumentText)
|
|
67
57
|
.option('-b, --baseContractNames <value>', 'only output contracts connected to these comma separated base contract names')
|
|
68
58
|
.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'))
|
|
69
59
|
.option('-c, --clusterFolders', 'cluster contracts into source folders', false)
|
|
@@ -94,18 +84,20 @@ If an Ethereum address with a 0x prefix is passed, the verified source code from
|
|
|
94
84
|
!(options.baseContractNames || contractName)) {
|
|
95
85
|
throw Error('Must specify base contract(s) when using the squash option against local Solidity files.');
|
|
96
86
|
}
|
|
97
|
-
// Filter out any class stereotypes that are to be hidden
|
|
98
|
-
let filteredUmlClasses = (0, filterClasses_1.filterHiddenClasses)(umlClasses, options);
|
|
99
87
|
const baseContractNames = options.baseContractNames?.split(',');
|
|
100
88
|
if (baseContractNames) {
|
|
101
|
-
// Find all the classes connected to the base classes
|
|
102
|
-
filteredUmlClasses = (0, filterClasses_1.classesConnectedToBaseContracts)(filteredUmlClasses, baseContractNames, options.depth);
|
|
103
89
|
contractName = baseContractNames[0];
|
|
104
90
|
}
|
|
91
|
+
// Filter out any class stereotypes that are to be hidden
|
|
92
|
+
let filteredUmlClasses = (0, filterClasses_1.filterHiddenClasses)(umlClasses, options);
|
|
105
93
|
// squash contracts
|
|
106
94
|
if (options.squash) {
|
|
107
95
|
filteredUmlClasses = (0, squashClasses_1.squashUmlClasses)(filteredUmlClasses, baseContractNames || [contractName]);
|
|
108
96
|
}
|
|
97
|
+
if (baseContractNames || options.squash) {
|
|
98
|
+
// Find all the classes connected to the base classes after they have been squashed
|
|
99
|
+
filteredUmlClasses = (0, filterClasses_1.classesConnectedToBaseContracts)(filteredUmlClasses, baseContractNames || [contractName], options.depth);
|
|
100
|
+
}
|
|
109
101
|
// Convert UML classes to Graphviz dot format.
|
|
110
102
|
const dotString = (0, converterClasses2Dot_1.convertUmlClasses2Dot)(filteredUmlClasses, combinedOptions.clusterFolders, combinedOptions);
|
|
111
103
|
// Convert Graphviz dot format to file formats. eg svg or png
|
|
@@ -119,11 +111,11 @@ If an Ethereum address with a 0x prefix is passed, the verified source code from
|
|
|
119
111
|
});
|
|
120
112
|
program
|
|
121
113
|
.command('storage')
|
|
122
|
-
.
|
|
123
|
-
.
|
|
114
|
+
.usage('[options] <fileFolderAddress>')
|
|
115
|
+
.description(`Visually display a contract's storage slots.
|
|
124
116
|
|
|
125
|
-
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is fixed-sized arrays declared with an expression will fail to be sized
|
|
126
|
-
.argument('
|
|
117
|
+
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is fixed-sized arrays declared with an expression will fail to be sized.\n`)
|
|
118
|
+
.argument('fileFolderAddress', argumentText)
|
|
127
119
|
.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.')
|
|
128
120
|
.option('-cf, --contractFile <filename>', 'Filename the contract is located in. This can include the relative path to the desired file.')
|
|
129
121
|
.option('-d, --data', 'Gets the values in the storage slots from an Ethereum node.', false)
|
|
@@ -133,6 +125,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
|
|
|
133
125
|
.default('http://localhost:8545'))
|
|
134
126
|
.option('-bn, --block <number>', 'Block number to get the contract storage values from.', 'latest')
|
|
135
127
|
.option('-a, --array <number>', 'Number of slots to display at the start and end of arrays.', '2')
|
|
128
|
+
.option('-hv, --hideValue', 'Hide storage slot value column.', false)
|
|
136
129
|
.action(async (fileFolderAddress, options, command) => {
|
|
137
130
|
try {
|
|
138
131
|
const combinedOptions = {
|
|
@@ -187,15 +180,15 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
|
|
|
187
180
|
});
|
|
188
181
|
program
|
|
189
182
|
.command('flatten')
|
|
190
|
-
.
|
|
191
|
-
.
|
|
183
|
+
.usage('<contractAddress>')
|
|
184
|
+
.description(`Merges verified source files for a contract from a Blockchain explorer into one local Solidity file.
|
|
192
185
|
|
|
193
186
|
In order for the merged code to compile, the following is done:
|
|
194
187
|
1. pragma solidity is set using the compiler of the verified contract.
|
|
195
188
|
2. All pragma solidity lines in the source files are commented out.
|
|
196
189
|
3. File imports are commented out.
|
|
197
190
|
4. "SPDX-License-Identifier" is renamed to "SPDX--License-Identifier".
|
|
198
|
-
5. Contract dependencies are analysed so the files are merged in an order that will compile
|
|
191
|
+
5. Contract dependencies are analysed so the files are merged in an order that will compile.\n`)
|
|
199
192
|
.argument('<contractAddress>', 'Contract address in hexadecimal format with a 0x prefix.')
|
|
200
193
|
.action(async (contractAddress, options, command) => {
|
|
201
194
|
try {
|
|
@@ -217,15 +210,15 @@ In order for the merged code to compile, the following is done:
|
|
|
217
210
|
});
|
|
218
211
|
program
|
|
219
212
|
.command('diff')
|
|
220
|
-
.
|
|
221
|
-
.
|
|
213
|
+
.usage('[options] <addressA> <addressB>')
|
|
214
|
+
.description(`Compare verified Solidity code differences between two contracts.
|
|
222
215
|
|
|
223
|
-
The results show the comparison of
|
|
216
|
+
The results show the comparison of contract A to B.
|
|
224
217
|
The ${clc.green('green')} sections are additions to contract B that are not in contract A.
|
|
225
218
|
The ${clc.red('red')} sections are removals from contract A that are not in contract B.
|
|
226
|
-
The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B
|
|
227
|
-
.argument('<addressA>', 'Contract address in hexadecimal format with a 0x prefix.')
|
|
228
|
-
.argument('<addressB>', 'Contract address in hexadecimal format with a 0x prefix.')
|
|
219
|
+
The line numbers are from contract B. There are no line numbers for the red sections as they are not in contract B.\n`)
|
|
220
|
+
.argument('<addressA>', 'Contract address in hexadecimal format with a 0x prefix of the first contract.')
|
|
221
|
+
.argument('<addressB>', 'Contract address in hexadecimal format with a 0x prefix of the second contract.')
|
|
229
222
|
.addOption(new commander_1.Option('-l, --lineBuffer <value>', 'Minimum number of lines before and after changes').default('4'))
|
|
230
223
|
.option('-s, --saveFiles', 'Save the flattened contract code to the filesystem. The file names will be the contract address with a .sol extension.', false)
|
|
231
224
|
.action(async (addressA, addressB, options, command) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sol2uml",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "Solidity contract visualisation tool.",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
"buildSol": "cd ./src/contracts && solc **/*.sol",
|
|
9
9
|
"build": "tsc --build ./tsconfig.json",
|
|
10
10
|
"clean": "tsc --build --clean ./tsconfig.json",
|
|
11
|
+
"package-lock": "npm i --package-lock-only",
|
|
12
|
+
"permit": " chmod 775 lib/sol2uml.js",
|
|
11
13
|
"prettier": "prettier --write src/**/*.ts **/*.md",
|
|
12
14
|
"prettier:check": "prettier --check src/**/*.ts **/*.md",
|
|
13
|
-
"test": "npx jest"
|
|
15
|
+
"test": "npx jest",
|
|
16
|
+
"prepublishOnly": "npm run clean && npm run package-lock && npm run build && npm run permit"
|
|
14
17
|
},
|
|
15
18
|
"preferGlobal": true,
|
|
16
19
|
"contributors": [
|
|
@@ -20,28 +23,28 @@
|
|
|
20
23
|
"license": "MIT",
|
|
21
24
|
"dependencies": {
|
|
22
25
|
"@aduh95/viz.js": "^3.7.0",
|
|
23
|
-
"@solidity-parser/parser": "^0.
|
|
24
|
-
"axios": "1.
|
|
26
|
+
"@solidity-parser/parser": "^0.15.0",
|
|
27
|
+
"axios": "^1.3.4",
|
|
25
28
|
"axios-debug-log": "^1.0.0",
|
|
26
29
|
"cli-color": "^2.0.3",
|
|
27
|
-
"commander": "^
|
|
30
|
+
"commander": "^10.0.0",
|
|
28
31
|
"convert-svg-to-png": "^0.6.4",
|
|
29
32
|
"debug": "^4.3.4",
|
|
30
33
|
"diff-match-patch": "^1.0.5",
|
|
31
34
|
"ethers": "^5.7.2",
|
|
32
35
|
"js-graph-algorithms": "^1.0.18",
|
|
33
|
-
"klaw": "^4.0
|
|
36
|
+
"klaw": "^4.1.0"
|
|
34
37
|
},
|
|
35
38
|
"devDependencies": {
|
|
36
39
|
"@openzeppelin/contracts": "4.8.0",
|
|
37
40
|
"@types/diff-match-patch": "^1.0.32",
|
|
38
41
|
"@types/jest": "^29.4.0",
|
|
39
42
|
"@types/klaw": "^3.0.3",
|
|
40
|
-
"jest": "^29.4.
|
|
41
|
-
"prettier": "^2.8.
|
|
43
|
+
"jest": "^29.4.3",
|
|
44
|
+
"prettier": "^2.8.4",
|
|
42
45
|
"ts-jest": "^29.0.5",
|
|
43
46
|
"ts-node": "^10.9.1",
|
|
44
|
-
"typescript": "^4.9.
|
|
47
|
+
"typescript": "^4.9.5"
|
|
45
48
|
},
|
|
46
49
|
"files": [
|
|
47
50
|
"lib/*.js",
|