sol2uml 2.0.2 → 2.0.5
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 +36 -23
- package/lib/converterAST2Classes.js +10 -0
- package/lib/converterClasses2Storage.js +13 -3
- package/lib/parserEtherscan.d.ts +1 -1
- package/lib/parserEtherscan.js +1 -0
- package/lib/slotValues.d.ts +0 -0
- package/lib/slotValues.js +21 -0
- package/lib/sol2uml.js +6 -4
- package/lib/umlClass.d.ts +6 -0
- package/lib/umlClass.js +1 -0
- package/lib/writerFiles.d.ts +1 -1
- package/lib/writerFiles.js +14 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -20,16 +20,19 @@ The following installation assumes [Node.js](https://nodejs.org/en/download/) ha
|
|
|
20
20
|
`sol2uml` works with node 14 or above.
|
|
21
21
|
|
|
22
22
|
To install globally so you can run `sol2uml` from anywhere
|
|
23
|
+
|
|
23
24
|
```bash
|
|
24
25
|
npm link sol2uml --only=production
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
To upgrade run
|
|
29
|
+
|
|
28
30
|
```bash
|
|
29
31
|
npm upgrade sol2uml -g
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
To see which version you are using
|
|
35
|
+
|
|
33
36
|
```bash
|
|
34
37
|
npm ls sol2uml -g
|
|
35
38
|
```
|
|
@@ -53,7 +56,7 @@ Options:
|
|
|
53
56
|
-f, --outputFormat <value> output file format. (choices: "svg", "png", "dot", "all", default: "svg")
|
|
54
57
|
-o, --outputFileName <value> output file name
|
|
55
58
|
-i, --ignoreFilesOrFolders <filesOrFolders> comma separated list of files or folders to ignore
|
|
56
|
-
-n, --network <network> Ethereum network (choices: "mainnet", "polygon", "bsc", "arbitrum", "ropsten", "kovan", "rinkeby", "goerli", default: "mainnet")
|
|
59
|
+
-n, --network <network> Ethereum network (choices: "mainnet", "polygon", "bsc", "arbitrum", "ropsten", "kovan", "rinkeby", "goerli", "sepolia", default: "mainnet")
|
|
57
60
|
-k, --apiKey <key> Etherscan, Polygonscan or BscScan API key
|
|
58
61
|
-v, --verbose run with debugging statements (default: false)
|
|
59
62
|
-h, --help display help for command
|
|
@@ -109,7 +112,7 @@ Usage: sol2uml storage [options] <fileFolderAddress>
|
|
|
109
112
|
|
|
110
113
|
Visually display a contract's storage slots.
|
|
111
114
|
|
|
112
|
-
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is
|
|
115
|
+
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.
|
|
113
116
|
|
|
114
117
|
Arguments:
|
|
115
118
|
fileFolderAddress file name, base folder or contract address
|
|
@@ -139,36 +142,43 @@ Options:
|
|
|
139
142
|
## UML Class diagram examples
|
|
140
143
|
|
|
141
144
|
To generate a diagram of all contracts under the contracts folder and its sub folders
|
|
145
|
+
|
|
142
146
|
```bash
|
|
143
147
|
sol2uml class ./contracts
|
|
144
148
|
```
|
|
145
149
|
|
|
146
150
|
To generate a diagram of EtherDelta's contract from the verified source code on [Etherscan](https://etherscan.io/address/0x8d12A197cB00D4747a1fe03395095ce2A5CC6819#code). The output wil be a svg file `0x8d12A197cB00D4747a1fe03395095ce2A5CC6819.svg` in the working folder.
|
|
151
|
+
|
|
147
152
|
```bash
|
|
148
153
|
sol2uml class 0x8d12A197cB00D4747a1fe03395095ce2A5CC6819
|
|
149
154
|
```
|
|
150
155
|
|
|
151
156
|
To generate a diagram of EtherDelta's contract from the verified source code on [Etherscan Ropsten](https://ropsten.etherscan.io/address/0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190#code). The output wil be a svg file `0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190.svg` in the working folder.
|
|
157
|
+
|
|
152
158
|
```bash
|
|
153
159
|
sol2uml class 0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190 -n ropsten
|
|
154
160
|
```
|
|
155
161
|
|
|
156
162
|
To generate all Solidity files under some root folder and output the svg file to a specific location
|
|
163
|
+
|
|
157
164
|
```bash
|
|
158
165
|
sol2uml class path/to/contracts/root/folder -o ./outputFile.svg
|
|
159
166
|
```
|
|
160
167
|
|
|
161
168
|
To generate a diagram of all contracts in a single Solidity file, the output file in png format to output file `./someFile.png`
|
|
169
|
+
|
|
162
170
|
```bash
|
|
163
171
|
sol2uml class path/to/contracts/root/folder/solidity/file.sol -f png -o ./someFile.png
|
|
164
172
|
```
|
|
165
173
|
|
|
166
|
-
To generate a diagram of all Solidity files under the `contracts` and `node_modules/openzeppelin-solidity` folders.
|
|
174
|
+
To generate a diagram of all Solidity files under the `contracts` and `node_modules/openzeppelin-solidity` folders. The output will be `contracts.svg` and `contracts.png` files in the working folder.
|
|
175
|
+
|
|
167
176
|
```bash
|
|
168
177
|
sol2uml class ./contracts,node_modules/openzeppelin-solidity -f all -v
|
|
169
178
|
```
|
|
170
179
|
|
|
171
180
|
To generate a diagram of all Solidity files under the working folder ignoring and files under the `solparse`, `@solidity-parser` and `ethlint` folders, which will be under the `node_modules` folder.
|
|
181
|
+
|
|
172
182
|
```bash
|
|
173
183
|
sol2uml class -i solparse,@solidity-parser,ethlint
|
|
174
184
|
```
|
|
@@ -176,8 +186,9 @@ sol2uml class -i solparse,@solidity-parser,ethlint
|
|
|
176
186
|
# UML Class Diagram Syntax
|
|
177
187
|
|
|
178
188
|
Good online resources for learning UML
|
|
179
|
-
|
|
180
|
-
|
|
189
|
+
|
|
190
|
+
- [UML 2 Class Diagramming Guidelines](http://www.agilemodeling.com/style/classDiagram.htm)
|
|
191
|
+
- [Creating class diagrams with UML](https://www.ionos.com/digitalguide/websites/web-development/class-diagrams-with-uml/)
|
|
181
192
|
|
|
182
193
|
## Terminology differences
|
|
183
194
|
|
|
@@ -187,33 +198,35 @@ A Solidity variable becomes an attribute in UML and a Solidity function becomes
|
|
|
187
198
|
|
|
188
199
|
### Class stereotypes
|
|
189
200
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
201
|
+
- Interface
|
|
202
|
+
- Abstract - if any of the contract's functions are abstract, the class will have an Abstract stereotype. Child contracts of abstract contracts that do not implement all the abstract functions are currently not marked as Abstract.
|
|
203
|
+
- Library
|
|
193
204
|
|
|
194
205
|
### Operator stereotypes
|
|
195
206
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
207
|
+
- event
|
|
208
|
+
- modifier
|
|
209
|
+
- abstract - if there is no function body on a contract, the operator is marked as abstract. Operators on an Interface do not have an abstract stereotype as all operators are abstract.
|
|
210
|
+
- fallback - abstract fallback functions will just have an abstract stereotype.
|
|
211
|
+
- payable - payable fallback functions will just have a fallback stereotype.
|
|
201
212
|
|
|
202
213
|
## UML Associations
|
|
203
214
|
|
|
204
215
|
Lines:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
|
|
211
|
-
-
|
|
216
|
+
|
|
217
|
+
- Solid lines for
|
|
218
|
+
- link the contract types of storage (state) variables. This can be linked to contracts, interfaces, libraries or file level structs and enums.
|
|
219
|
+
- generalisations of contracts and abstract contracts.
|
|
220
|
+
- aggregated contract level structs and enums.
|
|
221
|
+
- Dashed lines for
|
|
222
|
+
- generalisations of interfaces.
|
|
223
|
+
- types of memory variables.
|
|
212
224
|
|
|
213
225
|
Heads/Tails:
|
|
214
|
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
226
|
+
|
|
227
|
+
- An empty triangle head for generalisations of contracts, interfaces and abstract contracts.
|
|
228
|
+
- An open arrow head for storage or memory variable dependencies
|
|
229
|
+
- A diamond tail for aggregations of contract level structs and enums
|
|
217
230
|
|
|
218
231
|
## Storage diagram
|
|
219
232
|
|
|
@@ -172,6 +172,16 @@ function parseContractDefinition(umlClass, node) {
|
|
|
172
172
|
attributeType,
|
|
173
173
|
compiled: valueStore,
|
|
174
174
|
});
|
|
175
|
+
// Is the variable a constant that could be used in declaring fixed sized arrays
|
|
176
|
+
if (variable.isDeclaredConst) {
|
|
177
|
+
if (variable?.expression.type === 'NumberLiteral') {
|
|
178
|
+
umlClass.constants.push({
|
|
179
|
+
name: variable.name,
|
|
180
|
+
value: parseInt(variable.expression.number),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// TODO handle expressions. eg N_COINS * 2
|
|
184
|
+
}
|
|
175
185
|
});
|
|
176
186
|
// Recursively parse variables for associations
|
|
177
187
|
umlClass = addAssociations(subNode.variables, umlClass);
|
|
@@ -170,7 +170,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
170
170
|
}
|
|
171
171
|
if (attribute.attributeType === umlClass_1.AttributeType.Array) {
|
|
172
172
|
// All array dimensions must be fixed. eg [2][3][8].
|
|
173
|
-
const result = attribute.type.match(/(\w+)(\[([
|
|
173
|
+
const result = attribute.type.match(/(\w+)(\[([\w][\w]*)\])+$/);
|
|
174
174
|
// The above will not match any dynamic array dimensions, eg [],
|
|
175
175
|
// as there needs to be one or more [0-9]+ in the square brackets
|
|
176
176
|
if (result === null) {
|
|
@@ -180,9 +180,19 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
180
180
|
}
|
|
181
181
|
// All array dimensions are fixes so we now need to multiply all the dimensions
|
|
182
182
|
// to get a total number of array elements
|
|
183
|
-
const arrayDimensions = attribute.type.match(/\[\
|
|
183
|
+
const arrayDimensions = attribute.type.match(/\[\w+/g);
|
|
184
184
|
const dimensionsStr = arrayDimensions.map((d) => d.slice(1));
|
|
185
|
-
const dimensions = dimensionsStr.map((
|
|
185
|
+
const dimensions = dimensionsStr.map((dimension) => {
|
|
186
|
+
const dimensionNum = parseInt(dimension);
|
|
187
|
+
if (!isNaN(dimensionNum))
|
|
188
|
+
return dimensionNum;
|
|
189
|
+
// Try and size array dimension from declared constants
|
|
190
|
+
const constant = umlClass.constants.find((constant) => constant.name === dimension);
|
|
191
|
+
if (constant) {
|
|
192
|
+
return constant.value;
|
|
193
|
+
}
|
|
194
|
+
throw Error(`Could not size fixed sized array with dimension "${dimension}"`);
|
|
195
|
+
});
|
|
186
196
|
let elementSize;
|
|
187
197
|
// If a fixed sized array
|
|
188
198
|
if ((0, exports.isElementary)(result[1])) {
|
package/lib/parserEtherscan.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ASTNode } from '@solidity-parser/parser/dist/src/ast-types';
|
|
2
2
|
import { UmlClass } from './umlClass';
|
|
3
|
-
declare const networks: readonly ["mainnet", "ropsten", "kovan", "rinkeby", "goerli", "polygon", "bsc", "arbitrum"];
|
|
3
|
+
declare const networks: readonly ["mainnet", "ropsten", "kovan", "rinkeby", "goerli", "sepolia", "polygon", "bsc", "arbitrum"];
|
|
4
4
|
declare type Network = typeof networks[number];
|
|
5
5
|
export declare class EtherscanParser {
|
|
6
6
|
protected apikey: string;
|
package/lib/parserEtherscan.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// import { providers } from 'ethers'
|
|
2
|
+
// import { BigNumberish } from '@ethersproject/bignumber'
|
|
3
|
+
// import { BlockTag } from '@ethersproject/abstract-provider'
|
|
4
|
+
//
|
|
5
|
+
// export const getStorageValue = async (
|
|
6
|
+
// contractAddress: string,
|
|
7
|
+
// slot: BigNumberish,
|
|
8
|
+
// blockTag: BlockTag = 'latest'
|
|
9
|
+
// ) => {
|
|
10
|
+
// const provider = new providers.JsonRpcProvider(
|
|
11
|
+
// 'https://eth-mainnet.alchemyapi.io/v2/iRsTnBFfuK96dHeFnEJ0wg56wdzioYPg',
|
|
12
|
+
// 'mainnet'
|
|
13
|
+
// )
|
|
14
|
+
// const slotValue = await provider.getStorageAt(
|
|
15
|
+
// contractAddress,
|
|
16
|
+
// slot,
|
|
17
|
+
// blockTag
|
|
18
|
+
// )
|
|
19
|
+
// console.log(`Slot ${slot}: ${slotValue}`)
|
|
20
|
+
// }
|
|
21
|
+
//# sourceMappingURL=slotValues.js.map
|
package/lib/sol2uml.js
CHANGED
|
@@ -37,6 +37,7 @@ The Solidity code can be pulled from verified source code on Blockchain explorer
|
|
|
37
37
|
'kovan',
|
|
38
38
|
'rinkeby',
|
|
39
39
|
'goerli',
|
|
40
|
+
'sepolia',
|
|
40
41
|
])
|
|
41
42
|
.default('mainnet'))
|
|
42
43
|
.option('-k, --apiKey <key>', 'Etherscan, Polygonscan, BscScan or Arbiscan API key')
|
|
@@ -90,7 +91,7 @@ If an Ethereum address with a 0x prefix is passed, the verified source code from
|
|
|
90
91
|
});
|
|
91
92
|
program
|
|
92
93
|
.command('storage')
|
|
93
|
-
.description("Visually display a contract's storage slots.\n\nWARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is
|
|
94
|
+
.description("Visually display a contract's storage slots.\n\nWARNING: 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.")
|
|
94
95
|
.argument('<fileFolderAddress>', 'file name, base folder or contract address')
|
|
95
96
|
.option('-c, --contractName <value>', 'Contract name in local Solidity files. Not needed when using an address as the first argument.')
|
|
96
97
|
// .option('-d, --data', 'gets the data in the storage slots')
|
|
@@ -100,15 +101,16 @@ program
|
|
|
100
101
|
...command.parent._optionValues,
|
|
101
102
|
...options,
|
|
102
103
|
};
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
let { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions);
|
|
105
|
+
contractName = combinedOptions.contractName || contractName;
|
|
106
|
+
const storageObjects = (0, converterClasses2Storage_1.convertClasses2StorageObjects)(contractName, umlClasses);
|
|
105
107
|
if ((0, regEx_1.isAddress)(fileFolderAddress)) {
|
|
106
108
|
// The first object is the contract
|
|
107
109
|
storageObjects[0].address = fileFolderAddress;
|
|
108
110
|
}
|
|
109
111
|
debug(storageObjects);
|
|
110
112
|
const dotString = (0, converterStorage2Dot_1.convertStorage2Dot)(storageObjects);
|
|
111
|
-
await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, combinedOptions.outputFormat, combinedOptions.outputFileName);
|
|
113
|
+
await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, contractName, combinedOptions.outputFormat, combinedOptions.outputFileName);
|
|
112
114
|
}
|
|
113
115
|
catch (err) {
|
|
114
116
|
console.error(`Failed to generate storage diagram ${err}`);
|
package/lib/umlClass.d.ts
CHANGED
|
@@ -63,6 +63,10 @@ export interface Association {
|
|
|
63
63
|
targetUmlClassStereotype?: ClassStereotype;
|
|
64
64
|
realization?: boolean;
|
|
65
65
|
}
|
|
66
|
+
export interface Constants {
|
|
67
|
+
name: string;
|
|
68
|
+
value: number;
|
|
69
|
+
}
|
|
66
70
|
export interface ClassProperties {
|
|
67
71
|
name: string;
|
|
68
72
|
absolutePath: string;
|
|
@@ -76,6 +80,7 @@ export interface ClassProperties {
|
|
|
76
80
|
associations?: {
|
|
77
81
|
[name: string]: Association;
|
|
78
82
|
};
|
|
83
|
+
constants?: Constants[];
|
|
79
84
|
}
|
|
80
85
|
export declare class UmlClass implements ClassProperties {
|
|
81
86
|
static idCounter: number;
|
|
@@ -85,6 +90,7 @@ export declare class UmlClass implements ClassProperties {
|
|
|
85
90
|
relativePath: string;
|
|
86
91
|
imports?: Import[];
|
|
87
92
|
stereotype?: ClassStereotype;
|
|
93
|
+
constants: Constants[];
|
|
88
94
|
attributes: Attribute[];
|
|
89
95
|
operators: Operator[];
|
|
90
96
|
enums: number[];
|
package/lib/umlClass.js
CHANGED
package/lib/writerFiles.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare type OutputFormats = 'svg' | 'png' | 'dot' | 'all';
|
|
2
|
-
export declare const writeOutputFiles: (dot: string,
|
|
2
|
+
export declare const writeOutputFiles: (dot: string, fileFolderAddress: string, contractName: string, outputFormat?: OutputFormats, outputFilename?: string) => Promise<void>;
|
|
3
3
|
export declare function convertDot2Svg(dot: string): any;
|
|
4
4
|
export declare function writeSolidity(code: string, filename?: string): void;
|
|
5
5
|
export declare function writeDot(dot: string, filename?: string): void;
|
package/lib/writerFiles.js
CHANGED
|
@@ -9,7 +9,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const sync_1 = __importDefault(require("@aduh95/viz.js/sync"));
|
|
10
10
|
const { convert } = require('convert-svg-to-png');
|
|
11
11
|
const debug = require('debug')('sol2uml');
|
|
12
|
-
const writeOutputFiles = async (dot,
|
|
12
|
+
const writeOutputFiles = async (dot, fileFolderAddress, contractName, outputFormat = 'svg', outputFilename) => {
|
|
13
13
|
if (outputFormat === 'dot' || outputFormat === 'all') {
|
|
14
14
|
writeDot(dot, outputFilename);
|
|
15
15
|
// No need to continue if only generating a dot file
|
|
@@ -17,19 +17,24 @@ const writeOutputFiles = async (dot, outputBaseName, outputFormat = 'svg', outpu
|
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
// If all output then extension is svg
|
|
21
|
+
const outputExt = outputFormat === 'all' ? 'svg' : outputFormat;
|
|
20
22
|
if (!outputFilename) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
outputFilename =
|
|
24
|
+
path_1.default.join(process.cwd(), contractName) + '.' + outputExt;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// check if outputFilename is a folder
|
|
24
28
|
try {
|
|
25
|
-
const folderOrFile = (0, fs_1.lstatSync)(
|
|
29
|
+
const folderOrFile = (0, fs_1.lstatSync)(outputFilename);
|
|
26
30
|
if (folderOrFile.isDirectory()) {
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
outputFilename =
|
|
32
|
+
path_1.default.join(process.cwd(), outputFilename, contractName) +
|
|
33
|
+
'.' +
|
|
34
|
+
outputExt;
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
|
-
catch (err) { } // we can ignore errors as it just means
|
|
32
|
-
outputFilename = outputBaseName + '.' + outputExt;
|
|
37
|
+
catch (err) { } // we can ignore errors as it just means outputFilename does not exist yet
|
|
33
38
|
}
|
|
34
39
|
const svg = convertDot2Svg(dot);
|
|
35
40
|
if (outputFormat === 'svg' || outputFormat === 'all') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sol2uml",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Unified Modeling Language (UML) class diagram generator for Solidity contracts",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"buildSol": "cd ./src/contracts && solc **/*.sol",
|
|
9
9
|
"build": "tsc --build ./tsconfig.json",
|
|
10
10
|
"clean": "tsc --build --clean ./tsconfig.json",
|
|
11
|
-
"prettier": "prettier --write
|
|
12
|
-
"prettier:check": "prettier --check
|
|
11
|
+
"prettier": "prettier --write src/**/*.ts **/*.md",
|
|
12
|
+
"prettier:check": "prettier --check src/**/*.ts **/*.md",
|
|
13
13
|
"test": "npx jest"
|
|
14
14
|
},
|
|
15
15
|
"preferGlobal": true,
|