Comparing version 2.0.5 to 2.1.0
@@ -101,3 +101,5 @@ "use strict"; | ||
// this has come from Etherscan | ||
const importPath = path.join(codeFolder, childNode.path); | ||
const importPath = childNode.path[0] === '@' | ||
? childNode.path | ||
: path.join(codeFolder, childNode.path); | ||
imports.push({ | ||
@@ -104,0 +106,0 @@ absolutePath: importPath, |
@@ -6,3 +6,3 @@ import { Attribute, UmlClass } from './umlClass'; | ||
} | ||
export interface Storage { | ||
export interface Variable { | ||
id: number; | ||
@@ -16,7 +16,7 @@ fromSlot: number; | ||
contractName?: string; | ||
value?: string; | ||
structObjectId?: number; | ||
values: string[]; | ||
structStorageId?: number; | ||
enumId?: number; | ||
} | ||
export interface StorageObject { | ||
export interface Storage { | ||
id: number; | ||
@@ -26,7 +26,14 @@ name: string; | ||
type: StorageType; | ||
storages: Storage[]; | ||
variables: Variable[]; | ||
} | ||
export declare const convertClasses2StorageObjects: (contractName: string, umlClasses: UmlClass[]) => StorageObject[]; | ||
export declare const parseStructStorageObject: (attribute: Attribute, otherClasses: UmlClass[], storageObjects: StorageObject[]) => StorageObject | undefined; | ||
/** | ||
* | ||
* @param url | ||
* @param contractAddress Contract address to get the storage slot values from | ||
* @param storage is mutated with the storage values | ||
*/ | ||
export declare const addStorageValues: (url: string, contractAddress: string, storage: Storage, blockTag: string) => Promise<void>; | ||
export declare const convertClasses2Storages: (contractName: string, umlClasses: UmlClass[]) => Storage[]; | ||
export declare const parseStructStorage: (attribute: Attribute, otherClasses: UmlClass[], storages: Storage[]) => Storage | undefined; | ||
export declare const calcStorageByteSize: (attribute: Attribute, umlClass: UmlClass, otherClasses: UmlClass[]) => number; | ||
export declare const isElementary: (type: string) => boolean; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isElementary = exports.calcStorageByteSize = exports.parseStructStorageObject = exports.convertClasses2StorageObjects = exports.StorageType = void 0; | ||
exports.isElementary = exports.calcStorageByteSize = exports.parseStructStorage = exports.convertClasses2Storages = exports.addStorageValues = exports.StorageType = void 0; | ||
const umlClass_1 = require("./umlClass"); | ||
const associations_1 = require("./associations"); | ||
const slotValues_1 = require("./slotValues"); | ||
var StorageType; | ||
@@ -11,5 +12,19 @@ (function (StorageType) { | ||
})(StorageType = exports.StorageType || (exports.StorageType = {})); | ||
let storageObjectId = 1; | ||
let storageId = 1; | ||
const convertClasses2StorageObjects = (contractName, umlClasses) => { | ||
let variableId = 1; | ||
/** | ||
* | ||
* @param url | ||
* @param contractAddress Contract address to get the storage slot values from | ||
* @param storage is mutated with the storage values | ||
*/ | ||
const addStorageValues = async (url, contractAddress, storage, blockTag) => { | ||
const slots = storage.variables.map((s) => s.fromSlot); | ||
const values = await (0, slotValues_1.getStorageValues)(url, contractAddress, slots, blockTag); | ||
storage.variables.forEach((storage, i) => { | ||
storage.values = [values[i]]; | ||
}); | ||
}; | ||
exports.addStorageValues = addStorageValues; | ||
const convertClasses2Storages = (contractName, umlClasses) => { | ||
// Find the base UML Class from the base contract name | ||
@@ -22,21 +37,21 @@ const umlClass = umlClasses.find(({ name }) => { | ||
} | ||
const storageObjects = []; | ||
const storages = parseStorage(umlClass, umlClasses, [], storageObjects, []); | ||
storageObjects.unshift({ | ||
id: storageObjectId++, | ||
const storages = []; | ||
const variables = parseVariables(umlClass, umlClasses, [], storages, []); | ||
storages.unshift({ | ||
id: storageId++, | ||
name: contractName, | ||
type: StorageType.Contract, | ||
storages, | ||
variables: variables, | ||
}); | ||
return storageObjects; | ||
return storages; | ||
}; | ||
exports.convertClasses2StorageObjects = convertClasses2StorageObjects; | ||
exports.convertClasses2Storages = convertClasses2Storages; | ||
/** | ||
* Recursively parses the storage for a given contract. | ||
* Recursively parses the storage variables for a given contract. | ||
* @param umlClass contract or file level struct | ||
* @param umlClasses other contracts, structs and enums that may be a type of a storage variable. | ||
* @param storages mutable array of storage slots that is appended to | ||
* @param storageObjects mutable array of StorageObjects that is appended with structs | ||
* @param variables mutable array of storage slots that is appended to | ||
* @param storages mutable array of storages that is appended with structs | ||
*/ | ||
const parseStorage = (umlClass, umlClasses, storages, storageObjects, inheritedContracts) => { | ||
const parseVariables = (umlClass, umlClasses, variables, storages, inheritedContracts) => { | ||
// Add storage slots from inherited contracts first. | ||
@@ -55,3 +70,3 @@ // Get immediate parent contracts that the class inherits from | ||
// recursively parse inherited contract | ||
parseStorage(parentClass, umlClasses, storages, storageObjects, inheritedContracts); | ||
parseVariables(parentClass, umlClasses, variables, storages, inheritedContracts); | ||
}); | ||
@@ -65,9 +80,9 @@ // Parse storage for each attribute | ||
// find any dependent structs | ||
const linkedStruct = (0, exports.parseStructStorageObject)(attribute, umlClasses, storageObjects); | ||
const structObjectId = linkedStruct?.id; | ||
const linkedStruct = (0, exports.parseStructStorage)(attribute, umlClasses, storages); | ||
const structStorageId = linkedStruct?.id; | ||
// Get the toSlot of the last storage item | ||
let lastToSlot = 0; | ||
let nextOffset = 0; | ||
if (storages.length > 0) { | ||
const lastStorage = storages[storages.length - 1]; | ||
if (variables.length > 0) { | ||
const lastStorage = variables[variables.length - 1]; | ||
lastToSlot = lastStorage.toSlot; | ||
@@ -77,5 +92,5 @@ nextOffset = lastStorage.byteOffset + lastStorage.byteSize; | ||
if (nextOffset + byteSize > 32) { | ||
const nextFromSlot = storages.length > 0 ? lastToSlot + 1 : 0; | ||
storages.push({ | ||
id: storageId++, | ||
const nextFromSlot = variables.length > 0 ? lastToSlot + 1 : 0; | ||
variables.push({ | ||
id: variableId++, | ||
fromSlot: nextFromSlot, | ||
@@ -88,8 +103,9 @@ toSlot: nextFromSlot + Math.floor((byteSize - 1) / 32), | ||
contractName: umlClass.name, | ||
structObjectId, | ||
structStorageId, | ||
values: [], | ||
}); | ||
} | ||
else { | ||
storages.push({ | ||
id: storageId++, | ||
variables.push({ | ||
id: variableId++, | ||
fromSlot: lastToSlot, | ||
@@ -102,14 +118,15 @@ toSlot: lastToSlot, | ||
contractName: umlClass.name, | ||
structObjectId, | ||
structStorageId, | ||
values: [], | ||
}); | ||
} | ||
}); | ||
return storages; | ||
return variables; | ||
}; | ||
const parseStructStorageObject = (attribute, otherClasses, storageObjects) => { | ||
const parseStructStorage = (attribute, otherClasses, storages) => { | ||
if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) { | ||
// Have we already created the storageObject? | ||
const existingStorageObject = storageObjects.find((dep) => dep.name === attribute.type); | ||
if (existingStorageObject) { | ||
return existingStorageObject; | ||
// Have we already created the storage? | ||
const existingStorage = storages.find((dep) => dep.name === attribute.type); | ||
if (existingStorage) { | ||
return existingStorage; | ||
} | ||
@@ -124,11 +141,11 @@ // Is the user defined type linked to another Contract, Struct or Enum? | ||
if (dependentClass.stereotype === umlClass_1.ClassStereotype.Struct) { | ||
const storages = parseStorage(dependentClass, otherClasses, [], storageObjects, []); | ||
const newStorageObject = { | ||
id: storageObjectId++, | ||
const variables = parseVariables(dependentClass, otherClasses, [], storages, []); | ||
const newStorage = { | ||
id: storageId++, | ||
name: attribute.type, | ||
type: StorageType.Struct, | ||
storages, | ||
variables, | ||
}; | ||
storageObjects.push(newStorageObject); | ||
return newStorageObject; | ||
storages.push(newStorage); | ||
return newStorage; | ||
} | ||
@@ -146,6 +163,6 @@ return undefined; | ||
if (result !== null && result[1] && !(0, exports.isElementary)(result[1])) { | ||
// Have we already created the storageObject? | ||
const existingStorageObject = storageObjects.find(({ name }) => name === result[1] || name === result[1].split('.')[1]); | ||
if (existingStorageObject) { | ||
return existingStorageObject; | ||
// Have we already created the storage? | ||
const existingStorage = storages.find(({ name }) => name === result[1] || name === result[1].split('.')[1]); | ||
if (existingStorage) { | ||
return existingStorage; | ||
} | ||
@@ -158,11 +175,11 @@ // Find UserDefined type | ||
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) { | ||
const storages = parseStorage(typeClass, otherClasses, [], storageObjects, []); | ||
const newStorageObject = { | ||
id: storageObjectId++, | ||
const variables = parseVariables(typeClass, otherClasses, [], storages, []); | ||
const newStorage = { | ||
id: storageId++, | ||
name: typeClass.name, | ||
type: StorageType.Struct, | ||
storages, | ||
variables, | ||
}; | ||
storageObjects.push(newStorageObject); | ||
return newStorageObject; | ||
storages.push(newStorage); | ||
return newStorage; | ||
} | ||
@@ -174,3 +191,3 @@ } | ||
}; | ||
exports.parseStructStorageObject = parseStructStorageObject; | ||
exports.parseStructStorage = parseStructStorage; | ||
// Calculates the storage size of an attribute in bytes | ||
@@ -177,0 +194,0 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { |
@@ -1,3 +0,3 @@ | ||
import { StorageObject } from './converterClasses2Storage'; | ||
export declare const convertStorage2Dot: (storageObjects: StorageObject[]) => string; | ||
export declare function convertStorageObject2Dot(storageObject: StorageObject, dotString: string): string; | ||
import { Storage } from './converterClasses2Storage'; | ||
export declare const convertStorages2Dot: (storages: Storage[]) => string; | ||
export declare function convertStorage2Dot(storage: Storage, dotString: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.convertStorageObject2Dot = exports.convertStorage2Dot = void 0; | ||
exports.convertStorage2Dot = exports.convertStorages2Dot = void 0; | ||
const converterClasses2Storage_1 = require("./converterClasses2Storage"); | ||
const debug = require('debug')('sol2uml'); | ||
const convertStorage2Dot = (storageObjects) => { | ||
const convertStorages2Dot = (storages) => { | ||
let dotString = ` | ||
@@ -13,11 +13,11 @@ digraph StorageDiagram { | ||
node [shape=record, style=filled, fillcolor=gray95]`; | ||
// process contract and the struct objects | ||
storageObjects.forEach((storageObject) => { | ||
dotString = convertStorageObject2Dot(storageObject, dotString); | ||
// process contract and the struct storages | ||
storages.forEach((storage) => { | ||
dotString = convertStorage2Dot(storage, dotString); | ||
}); | ||
// link contract and structs to structs | ||
storageObjects.forEach((slot) => { | ||
slot.storages.forEach((storage) => { | ||
if (storage.structObjectId) { | ||
dotString += `\n ${slot.id}:${storage.id} -> ${storage.structObjectId}`; | ||
storages.forEach((slot) => { | ||
slot.variables.forEach((storage) => { | ||
if (storage.structStorageId) { | ||
dotString += `\n ${slot.id}:${storage.id} -> ${storage.structStorageId}`; | ||
} | ||
@@ -31,51 +31,57 @@ }); | ||
}; | ||
exports.convertStorage2Dot = convertStorage2Dot; | ||
function convertStorageObject2Dot(storageObject, dotString) { | ||
const steorotype = storageObject.type === converterClasses2Storage_1.StorageType.Struct ? 'Struct' : 'Contract'; | ||
// write object header with name and optional address | ||
dotString += `\n${storageObject.id} [label="{\\<\\<${steorotype}\\>\\>\\n${storageObject.name}\\n${storageObject.address || ''} | `; | ||
exports.convertStorages2Dot = convertStorages2Dot; | ||
function convertStorage2Dot(storage, dotString) { | ||
const steorotype = storage.type === converterClasses2Storage_1.StorageType.Struct ? 'Struct' : 'Contract'; | ||
// write storage header with name and optional address | ||
dotString += `\n${storage.id} [label="${storage.name} \\<\\<${steorotype}\\>\\>\\n${storage.address || ''} | {`; | ||
const startingVariables = storage.variables.filter((s) => s.byteOffset === 0); | ||
// write slot numbers | ||
storageObject.storages.forEach((storage, i) => { | ||
if (i === 0) { | ||
dotString += `{slot | 0`; | ||
dotString += '{ slot'; | ||
startingVariables.forEach((variable, i) => { | ||
if (variable.fromSlot === variable.toSlot) { | ||
dotString += `| ${variable.fromSlot} `; | ||
} | ||
else if (storage.byteOffset === 0) { | ||
if (storage.fromSlot === storage.toSlot) { | ||
dotString += `| ${storage.fromSlot}`; | ||
else { | ||
dotString += `| ${variable.fromSlot}-${variable.toSlot} `; | ||
} | ||
}); | ||
// write slot values if available | ||
if (startingVariables[0]?.values[0]) { | ||
dotString += '} | {value'; | ||
startingVariables.forEach((variable, i) => { | ||
dotString += ` | ${variable.values[0]}`; | ||
}); | ||
} | ||
const contractVariablePrefix = storage.type === converterClasses2Storage_1.StorageType.Contract ? '\\<inherited contract\\>.' : ''; | ||
dotString += `} | { type: ${contractVariablePrefix}variable (bytes)`; | ||
// For each slot | ||
startingVariables.forEach((variable) => { | ||
// Get all the storage variables in this slot | ||
const slotVariables = storage.variables.filter((s) => s.fromSlot === variable.fromSlot); | ||
const usedBytes = slotVariables.reduce((acc, s) => acc + s.byteSize, 0); | ||
if (usedBytes < 32) { | ||
slotVariables.push({ | ||
id: 0, | ||
fromSlot: variable.fromSlot, | ||
toSlot: variable.fromSlot, | ||
byteSize: 32 - usedBytes, | ||
byteOffset: usedBytes, | ||
type: 'unallocated', | ||
contractName: variable.contractName, | ||
variable: '', | ||
values: [], | ||
}); | ||
} | ||
const slotVariablesReversed = slotVariables.reverse(); | ||
// For each variable in the slot | ||
slotVariablesReversed.forEach((variable, i) => { | ||
if (i === 0) { | ||
dotString += ` | { ${dotVariable(variable, storage.name)} `; | ||
} | ||
else { | ||
dotString += `| ${storage.fromSlot}-${storage.toSlot}`; | ||
dotString += ` | ${dotVariable(variable, storage.name)} `; | ||
} | ||
} | ||
}); | ||
dotString += '}'; | ||
}); | ||
// write storage types | ||
storageObject.storages.forEach((storage, i) => { | ||
const lastStorage = i > 0 ? storageObject.storages[i - 1] : undefined; | ||
const nextStorage = i + 1 < storageObject.storages.length | ||
? storageObject.storages[i + 1] | ||
: undefined; | ||
if (i === 0) { | ||
const contractVaraiblePrefix = storageObject.type === converterClasses2Storage_1.StorageType.Contract | ||
? '\\<inherited contract\\>.' | ||
: ''; | ||
dotString += `} | {type: ${contractVaraiblePrefix}variable (bytes) `; | ||
} | ||
// if next storage is in the same slot | ||
// and storage is the first in the slot | ||
if (nextStorage?.fromSlot === storage.fromSlot && | ||
storage.byteOffset === 0) { | ||
dotString += `| { ${dotVariable(storage, storageObject.name)} `; | ||
return; | ||
} | ||
// if last storage was on the same slot | ||
// and the next storage is on a different slot | ||
if (lastStorage?.fromSlot === storage.fromSlot && | ||
(nextStorage?.fromSlot > storage.fromSlot || | ||
nextStorage === undefined)) { | ||
dotString += `| ${dotVariable(storage, storageObject.name)} } `; | ||
return; | ||
} | ||
// If storage covers a whole slot or is not at the start or end of a slot | ||
dotString += `| ${dotVariable(storage, storageObject.name)} `; | ||
}); | ||
// Need to close off the last label | ||
@@ -85,8 +91,11 @@ dotString += '}}"]\n'; | ||
} | ||
exports.convertStorageObject2Dot = convertStorageObject2Dot; | ||
exports.convertStorage2Dot = convertStorage2Dot; | ||
const dotVariable = (storage, contractName) => { | ||
const port = storage.structObjectId !== undefined ? `<${storage.id}>` : ''; | ||
const port = storage.structStorageId !== undefined ? `<${storage.id}>` : ''; | ||
const contractNamePrefix = storage.contractName !== contractName ? `${storage.contractName}.` : ''; | ||
return `${port} ${storage.type}: ${contractNamePrefix}${storage.variable} (${storage.byteSize})`; | ||
const variable = storage.variable | ||
? `: ${contractNamePrefix}${storage.variable}` | ||
: ''; | ||
return `${port} ${storage.type}${variable} (${storage.byteSize})`; | ||
}; | ||
//# sourceMappingURL=converterStorage2Dot.js.map |
@@ -1,21 +0,51 @@ | ||
// import { providers } from 'ethers' | ||
// import { BigNumberish } from '@ethersproject/bignumber' | ||
// import { BlockTag } from '@ethersproject/abstract-provider' | ||
// | ||
// export const getStorageValue = async ( | ||
// contractAddress: string, | ||
// slot: BigNumberish, | ||
// blockTag: BlockTag = 'latest' | ||
// ) => { | ||
// const provider = new providers.JsonRpcProvider( | ||
// 'https://eth-mainnet.alchemyapi.io/v2/iRsTnBFfuK96dHeFnEJ0wg56wdzioYPg', | ||
// 'mainnet' | ||
// ) | ||
// const slotValue = await provider.getStorageAt( | ||
// contractAddress, | ||
// slot, | ||
// blockTag | ||
// ) | ||
// console.log(`Slot ${slot}: ${slotValue}`) | ||
// } | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getStorageValues = exports.getStorageValue = void 0; | ||
const bignumber_1 = require("@ethersproject/bignumber"); | ||
const axios_1 = __importDefault(require("axios")); | ||
const debug = require('debug')('sol2uml'); | ||
const getStorageValue = async (url, contractAddress, slot, blockTag = 'latest') => { | ||
debug(`About to get storage slot ${slot} value for ${contractAddress}`); | ||
const values = await (0, exports.getStorageValues)(url, contractAddress, [slot], blockTag); | ||
debug(`Got slot ${slot} value: ${values[0]}`); | ||
return values[0]; | ||
}; | ||
exports.getStorageValue = getStorageValue; | ||
let jsonRpcId = 0; | ||
const getStorageValues = async (url, contractAddress, slots, blockTag = 'latest') => { | ||
try { | ||
debug(`About to get ${slots.length} storage values for ${contractAddress} at block ${blockTag}`); | ||
const block = blockTag === 'latest' | ||
? blockTag | ||
: bignumber_1.BigNumber.from(blockTag).toHexString(); | ||
const payload = slots.map((slot) => ({ | ||
id: (jsonRpcId++).toString(), | ||
jsonrpc: '2.0', | ||
method: 'eth_getStorageAt', | ||
params: [ | ||
contractAddress, | ||
bignumber_1.BigNumber.from(slot).toHexString(), | ||
block, | ||
], | ||
})); | ||
const response = await axios_1.default.post(url, payload); | ||
console.log(response.data); | ||
if (response.data?.error?.message) { | ||
throw new Error(response.data.error.message); | ||
} | ||
if (response.data.length !== slots.length) { | ||
throw new Error(`Requested ${slots.length} storage slot values but only got ${response.data.length}`); | ||
} | ||
const responseData = response.data; | ||
const sortedResponses = responseData.sort((a, b) => bignumber_1.BigNumber.from(a.id).gt(b.id) ? 1 : -1); | ||
return sortedResponses.map((data) => data.result); | ||
} | ||
catch (err) { | ||
throw new Error(`Failed to get ${slots.length} storage values for ${contractAddress} from ${url}`, { cause: err }); | ||
} | ||
}; | ||
exports.getStorageValues = getStorageValues; | ||
//# sourceMappingURL=slotValues.js.map |
@@ -78,3 +78,3 @@ #! /usr/bin/env node | ||
}; | ||
const { umlClasses } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions); | ||
const { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions); | ||
let filteredUmlClasses = umlClasses; | ||
@@ -86,3 +86,3 @@ if (options.baseContractNames) { | ||
const dotString = (0, converterClasses2Dot_1.convertUmlClasses2Dot)(filteredUmlClasses, combinedOptions.clusterFolders, combinedOptions); | ||
await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, combinedOptions.outputFormat, combinedOptions.outputFileName); | ||
await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, contractName, combinedOptions.outputFormat, combinedOptions.outputFileName); | ||
debug(`Finished generating UML`); | ||
@@ -98,4 +98,9 @@ } | ||
.argument('<fileFolderAddress>', 'file name, base folder or contract address') | ||
.option('-c, --contractName <value>', 'Contract name in local Solidity files. Not needed when using an address as the first argument.') | ||
// .option('-d, --data', 'gets the data in the storage slots') | ||
.option('-c, --contract <name>', 'Contract name in local Solidity files. Not needed when using an address as the first argument as the contract name can be derived from Etherscan.') | ||
.option('-d, --data', 'Gets the values in the storage slots from an Ethereum node.', false) | ||
.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.') | ||
.addOption(new commander_1.Option('-u, --url <url>', 'URL of the Ethereum node to get storage values if the `data` option is used.') | ||
.env('NODE_URL') | ||
.default('http://localhost:8545')) | ||
.option('-bn, --block <number>', 'Block number to get the contract storage values from.', 'latest') | ||
.action(async (fileFolderAddress, options, command) => { | ||
@@ -108,10 +113,28 @@ try { | ||
let { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions); | ||
contractName = combinedOptions.contractName || contractName; | ||
const storageObjects = (0, converterClasses2Storage_1.convertClasses2StorageObjects)(contractName, umlClasses); | ||
contractName = combinedOptions.contract || contractName; | ||
const storages = (0, converterClasses2Storage_1.convertClasses2Storages)(contractName, umlClasses); | ||
if ((0, regEx_1.isAddress)(fileFolderAddress)) { | ||
// The first object is the contract | ||
storageObjects[0].address = fileFolderAddress; | ||
// The first storage is the contract | ||
storages[0].address = fileFolderAddress; | ||
} | ||
debug(storageObjects); | ||
const dotString = (0, converterStorage2Dot_1.convertStorage2Dot)(storageObjects); | ||
debug(storages); | ||
if (combinedOptions.data) { | ||
let storageAddress = combinedOptions.storage; | ||
if (storageAddress) { | ||
if (!(0, regEx_1.isAddress)(storageAddress)) { | ||
throw Error(`Invalid address to get storage data from "${storageAddress}"`); | ||
} | ||
} | ||
else { | ||
if (!(0, regEx_1.isAddress)(fileFolderAddress)) { | ||
throw Error(`Can not get storage slot values if first param is not an address and the \`address\` option is not used.`); | ||
} | ||
storageAddress = fileFolderAddress; | ||
} | ||
const storage = storages.find((so) => so.name === contractName); | ||
if (!storageAddress) | ||
throw Error(`Could not find the "${contractName}" contract in list of parsed storages`); | ||
await (0, converterClasses2Storage_1.addStorageValues)(combinedOptions.url, storageAddress, storage, combinedOptions.blockNumber); | ||
} | ||
const dotString = (0, converterStorage2Dot_1.convertStorages2Dot)(storages); | ||
await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, contractName, combinedOptions.outputFormat, combinedOptions.outputFileName); | ||
@@ -118,0 +141,0 @@ } |
@@ -5,4 +5,4 @@ export declare type OutputFormats = 'svg' | 'png' | 'dot' | 'all'; | ||
export declare function writeSolidity(code: string, filename?: string): void; | ||
export declare function writeDot(dot: string, filename?: string): void; | ||
export declare function writeDot(dot: string, filename: string): void; | ||
export declare function writeSVG(svg: any, svgFilename?: string, outputFormats?: OutputFormats): Promise<void>; | ||
export declare function writePng(svg: any, filename: string): Promise<void>; |
@@ -13,9 +13,2 @@ "use strict"; | ||
const writeOutputFiles = async (dot, fileFolderAddress, contractName, outputFormat = 'svg', outputFilename) => { | ||
if (outputFormat === 'dot' || outputFormat === 'all') { | ||
writeDot(dot, outputFilename); | ||
// No need to continue if only generating a dot file | ||
if (outputFormat === 'dot') { | ||
return; | ||
} | ||
} | ||
// If all output then extension is svg | ||
@@ -40,2 +33,9 @@ const outputExt = outputFormat === 'all' ? 'svg' : outputFormat; | ||
} | ||
if (outputFormat === 'dot' || outputFormat === 'all') { | ||
writeDot(dot, outputFilename); | ||
// No need to continue if only generating a dot file | ||
if (outputFormat === 'dot') { | ||
return; | ||
} | ||
} | ||
const svg = convertDot2Svg(dot); | ||
@@ -78,9 +78,7 @@ if (outputFormat === 'svg' || outputFormat === 'all') { | ||
exports.writeSolidity = writeSolidity; | ||
function writeDot(dot, filename = 'classDiagram.dot') { | ||
const extension = path_1.default.extname(filename); | ||
const outputFile = extension === '.dot' ? filename : filename + '.dot'; | ||
debug(`About to write Dot file to ${outputFile}`); | ||
(0, fs_1.writeFile)(outputFile, dot, (err) => { | ||
function writeDot(dot, filename) { | ||
debug(`About to write Dot file to ${filename}`); | ||
(0, fs_1.writeFile)(filename, dot, (err) => { | ||
if (err) { | ||
throw new Error(`Failed to write Dot file to ${outputFile}`, { | ||
throw new Error(`Failed to write Dot file to ${filename}`, { | ||
cause: err, | ||
@@ -90,3 +88,3 @@ }); | ||
else { | ||
console.log(`Dot file written to ${outputFile}`); | ||
console.log(`Dot file written to ${filename}`); | ||
} | ||
@@ -93,0 +91,0 @@ }); |
{ | ||
"name": "sol2uml", | ||
"version": "2.0.5", | ||
"version": "2.1.0", | ||
"description": "Unified Modeling Language (UML) class diagram generator for Solidity contracts", | ||
@@ -28,2 +28,3 @@ "main": "./lib/index.js", | ||
"debug": "^4.3.4", | ||
"ethers": "^5.6.9", | ||
"js-graph-algorithms": "^1.0.18", | ||
@@ -30,0 +31,0 @@ "klaw": "^4.0.1" |
@@ -118,8 +118,12 @@ # Solidity 2 UML | ||
Arguments: | ||
fileFolderAddress file name, base folder or contract address | ||
fileFolderAddress file name, base folder or contract address | ||
Options: | ||
-c, --contractName <value> Contract name in local Solidity files. Not needed when using an address as the first argument. | ||
-h, --help display help for command | ||
-c, --contract <name> Contract name in local Solidity files. Not needed when using an address as the first argument as the contract name can be derived from Etherscan. | ||
-d, --data Gets the values in the storage slots from an Ethereum node. (default: false) | ||
-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. | ||
-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) | ||
-bn, --block <number> Block number to get the contract storage values from. (default: "latest") | ||
-h, --help display help for command | ||
``` | ||
@@ -126,0 +130,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
124058
2505
274
9
+ Addedethers@^5.6.9
+ Added@ethersproject/abi@5.7.0(transitive)
+ Added@ethersproject/abstract-provider@5.7.0(transitive)
+ Added@ethersproject/abstract-signer@5.7.0(transitive)
+ Added@ethersproject/address@5.7.0(transitive)
+ Added@ethersproject/base64@5.7.0(transitive)
+ Added@ethersproject/basex@5.7.0(transitive)
+ Added@ethersproject/bignumber@5.7.0(transitive)
+ Added@ethersproject/bytes@5.7.0(transitive)
+ Added@ethersproject/constants@5.7.0(transitive)
+ Added@ethersproject/contracts@5.7.0(transitive)
+ Added@ethersproject/hash@5.7.0(transitive)
+ Added@ethersproject/hdnode@5.7.0(transitive)
+ Added@ethersproject/json-wallets@5.7.0(transitive)
+ Added@ethersproject/keccak256@5.7.0(transitive)
+ Added@ethersproject/logger@5.7.0(transitive)
+ Added@ethersproject/networks@5.7.1(transitive)
+ Added@ethersproject/pbkdf2@5.7.0(transitive)
+ Added@ethersproject/properties@5.7.0(transitive)
+ Added@ethersproject/providers@5.7.2(transitive)
+ Added@ethersproject/random@5.7.0(transitive)
+ Added@ethersproject/rlp@5.7.0(transitive)
+ Added@ethersproject/sha2@5.7.0(transitive)
+ Added@ethersproject/signing-key@5.7.0(transitive)
+ Added@ethersproject/solidity@5.7.0(transitive)
+ Added@ethersproject/strings@5.7.0(transitive)
+ Added@ethersproject/transactions@5.7.0(transitive)
+ Added@ethersproject/units@5.7.0(transitive)
+ Added@ethersproject/wallet@5.7.0(transitive)
+ Added@ethersproject/web@5.7.1(transitive)
+ Added@ethersproject/wordlists@5.7.0(transitive)
+ Addedaes-js@3.0.0(transitive)
+ Addedbech32@1.1.4(transitive)
+ Addedbn.js@4.12.15.2.1(transitive)
+ Addedbrorand@1.1.0(transitive)
+ Addedelliptic@6.5.4(transitive)
+ Addedethers@5.7.2(transitive)
+ Addedhash.js@1.1.7(transitive)
+ Addedhmac-drbg@1.0.1(transitive)
+ Addedjs-sha3@0.8.0(transitive)
+ Addedminimalistic-assert@1.0.1(transitive)
+ Addedminimalistic-crypto-utils@1.0.1(transitive)
+ Addedscrypt-js@3.0.1(transitive)
+ Addedws@7.4.6(transitive)