sol2uml 2.4.3 → 2.5.0
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 +24 -4
- package/lib/SlotValueCache.d.ts +31 -0
- package/lib/SlotValueCache.js +65 -0
- package/lib/associations.d.ts +1 -1
- package/lib/associations.js +1 -21
- package/lib/converterAST2Classes.d.ts +12 -1
- package/lib/converterAST2Classes.js +33 -7
- package/lib/converterClass2Dot.d.ts +4 -0
- package/lib/converterClasses2Dot.js +3 -2
- package/lib/converterClasses2Storage.d.ts +49 -27
- package/lib/converterClasses2Storage.js +448 -186
- package/lib/converterStorage2Dot.d.ts +7 -3
- package/lib/converterStorage2Dot.js +53 -28
- package/lib/filterClasses.d.ts +4 -4
- package/lib/filterClasses.js +2 -1
- package/lib/parserEtherscan.d.ts +20 -2
- package/lib/parserEtherscan.js +38 -5
- package/lib/parserFiles.d.ts +3 -3
- package/lib/parserFiles.js +2 -2
- package/lib/slotValues.d.ts +34 -4
- package/lib/slotValues.js +245 -24
- package/lib/sol2uml.js +28 -13
- package/lib/squashClasses.d.ts +3 -3
- package/lib/squashClasses.js +2 -2
- package/lib/writerFiles.js +0 -1
- package/package.json +1 -1
|
@@ -3,47 +3,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.addDynamicVariables = exports.findDimensionLength = exports.calcSectionOffset = exports.isElementary = exports.calcStorageByteSize = exports.parseStorageSectionFromAttribute = exports.convertClasses2StorageSections = exports.StorageSectionType = void 0;
|
|
7
7
|
const umlClass_1 = require("./umlClass");
|
|
8
8
|
const associations_1 = require("./associations");
|
|
9
|
-
const slotValues_1 = require("./slotValues");
|
|
10
9
|
const utils_1 = require("ethers/lib/utils");
|
|
11
10
|
const ethers_1 = require("ethers");
|
|
12
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const slotValues_1 = require("./slotValues");
|
|
13
13
|
const debug = require('debug')('sol2uml');
|
|
14
|
-
var
|
|
15
|
-
(function (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
var StorageSectionType;
|
|
15
|
+
(function (StorageSectionType) {
|
|
16
|
+
StorageSectionType["Contract"] = "Contract";
|
|
17
|
+
StorageSectionType["Struct"] = "Struct";
|
|
18
|
+
StorageSectionType["Array"] = "Array";
|
|
19
|
+
StorageSectionType["Bytes"] = "Bytes";
|
|
20
|
+
StorageSectionType["String"] = "String";
|
|
21
|
+
})(StorageSectionType = exports.StorageSectionType || (exports.StorageSectionType = {}));
|
|
20
22
|
let storageId = 1;
|
|
21
23
|
let variableId = 1;
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
* @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy
|
|
25
|
-
* @param contractAddress Contract address to get the storage slot values from.
|
|
26
|
-
* If proxied, use proxy and not the implementation contract.
|
|
27
|
-
* @param storage is mutated with the storage values
|
|
28
|
-
* @param blockTag block number or `latest`
|
|
29
|
-
*/
|
|
30
|
-
const addStorageValues = async (url, contractAddress, storage, blockTag) => {
|
|
31
|
-
const valueVariables = storage.variables.filter((s) => !s.noValue);
|
|
32
|
-
const slots = valueVariables.map((s) => s.fromSlot);
|
|
33
|
-
const values = await (0, slotValues_1.getStorageValues)(url, contractAddress, slots, blockTag);
|
|
34
|
-
valueVariables.forEach((valueVariable, i) => {
|
|
35
|
-
valueVariable.value = values[i];
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
exports.addStorageValues = addStorageValues;
|
|
39
24
|
/**
|
|
40
25
|
*
|
|
41
26
|
* @param contractName name of the contract to get storage layout.
|
|
42
27
|
* @param umlClasses array of UML classes of type `UMLClass`
|
|
28
|
+
* @param arrayItems the number of items to display at the start and end of an array
|
|
43
29
|
* @param contractFilename relative path of the contract in the file system
|
|
44
|
-
* @return array of
|
|
30
|
+
* @return storageSections array of storageSection objects
|
|
45
31
|
*/
|
|
46
|
-
const
|
|
32
|
+
const convertClasses2StorageSections = (contractName, umlClasses, arrayItems, contractFilename) => {
|
|
47
33
|
// Find the base UML Class from the base contract name
|
|
48
34
|
const umlClass = umlClasses.find(({ name, relativePath }) => {
|
|
49
35
|
if (!contractFilename) {
|
|
@@ -61,25 +47,32 @@ const convertClasses2Storages = (contractName, umlClasses, contractFilename) =>
|
|
|
61
47
|
throw Error(`Failed to find contract with name "${contractName}"${contractFilenameError}.\nIs the \`-c --contract <name>\` option correct?`);
|
|
62
48
|
}
|
|
63
49
|
debug(`Found contract "${contractName}" in ${umlClass.absolutePath}`);
|
|
64
|
-
const
|
|
65
|
-
const variables = parseVariables(umlClass, umlClasses, [],
|
|
66
|
-
|
|
50
|
+
const storageSections = [];
|
|
51
|
+
const variables = parseVariables(umlClass, umlClasses, [], storageSections, [], false, arrayItems);
|
|
52
|
+
// Add new storage section to the beginning of the array
|
|
53
|
+
storageSections.unshift({
|
|
67
54
|
id: storageId++,
|
|
68
55
|
name: contractName,
|
|
69
|
-
type:
|
|
56
|
+
type: StorageSectionType.Contract,
|
|
70
57
|
variables: variables,
|
|
58
|
+
mapping: false,
|
|
71
59
|
});
|
|
72
|
-
|
|
60
|
+
adjustSlots(storageSections[0], 0, storageSections);
|
|
61
|
+
return storageSections;
|
|
73
62
|
};
|
|
74
|
-
exports.
|
|
63
|
+
exports.convertClasses2StorageSections = convertClasses2StorageSections;
|
|
75
64
|
/**
|
|
76
|
-
* Recursively
|
|
65
|
+
* Recursively parse the storage variables for a given contract or struct.
|
|
77
66
|
* @param umlClass contract or file level struct
|
|
78
67
|
* @param umlClasses other contracts, structs and enums that may be a type of a storage variable.
|
|
79
|
-
* @param variables mutable array of storage
|
|
80
|
-
* @param
|
|
68
|
+
* @param variables mutable array of storage variables that are appended to
|
|
69
|
+
* @param storageSections mutable array of storageSection objects
|
|
70
|
+
* @param inheritedContracts mutable array of contracts that have been inherited already
|
|
71
|
+
* @param mapping flags that the storage section is under a mapping
|
|
72
|
+
* @param arrayItems the number of items to display at the start and end of an array
|
|
73
|
+
* @return variables array of storage variables in the `umlClass`
|
|
81
74
|
*/
|
|
82
|
-
const parseVariables = (umlClass, umlClasses, variables,
|
|
75
|
+
const parseVariables = (umlClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems) => {
|
|
83
76
|
// Add storage slots from inherited contracts first.
|
|
84
77
|
// Get immediate parent contracts that the class inherits from
|
|
85
78
|
const parentContracts = umlClass.getParentContracts();
|
|
@@ -94,7 +87,7 @@ const parseVariables = (umlClass, umlClasses, variables, storages, inheritedCont
|
|
|
94
87
|
throw Error(`Failed to find inherited contract "${parent.targetUmlClassName}" of "${umlClass.absolutePath}"`);
|
|
95
88
|
}
|
|
96
89
|
// recursively parse inherited contract
|
|
97
|
-
parseVariables(parentClass, umlClasses, variables,
|
|
90
|
+
parseVariables(parentClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems);
|
|
98
91
|
});
|
|
99
92
|
// Parse storage for each attribute
|
|
100
93
|
umlClass.attributes.forEach((attribute) => {
|
|
@@ -102,69 +95,97 @@ const parseVariables = (umlClass, umlClasses, variables, storages, inheritedCont
|
|
|
102
95
|
if (attribute.compiled)
|
|
103
96
|
return;
|
|
104
97
|
const { size: byteSize, dynamic } = (0, exports.calcStorageByteSize)(attribute, umlClass, umlClasses);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
const
|
|
98
|
+
// parse any dependent storage sections or enums
|
|
99
|
+
const references = (0, exports.parseStorageSectionFromAttribute)(attribute, umlClass, umlClasses, storageSections, mapping || attribute.attributeType === umlClass_1.AttributeType.Mapping, arrayItems);
|
|
100
|
+
// should this new variable get the slot value
|
|
101
|
+
const displayValue = calcDisplayValue(attribute.attributeType, dynamic, mapping, references?.storageSection?.type);
|
|
102
|
+
const getValue = calcGetValue(attribute.attributeType, mapping);
|
|
109
103
|
// Get the toSlot of the last storage item
|
|
110
|
-
|
|
111
|
-
let
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let
|
|
104
|
+
const lastVariable = variables[variables.length - 1];
|
|
105
|
+
let lastToSlot = lastVariable ? lastVariable.toSlot : 0;
|
|
106
|
+
let nextOffset = lastVariable
|
|
107
|
+
? lastVariable.byteOffset + lastVariable.byteSize
|
|
108
|
+
: 0;
|
|
109
|
+
let fromSlot;
|
|
110
|
+
let toSlot;
|
|
111
|
+
let byteOffset;
|
|
118
112
|
if (nextOffset + byteSize > 32) {
|
|
119
113
|
const nextFromSlot = variables.length > 0 ? lastToSlot + 1 : 0;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
toSlot: nextFromSlot + Math.floor((byteSize - 1) / 32),
|
|
124
|
-
byteSize,
|
|
125
|
-
byteOffset: 0,
|
|
126
|
-
type: attribute.type,
|
|
127
|
-
dynamic,
|
|
128
|
-
noValue,
|
|
129
|
-
variable: attribute.name,
|
|
130
|
-
contractName: umlClass.name,
|
|
131
|
-
referenceStorageId: referenceStorage?.id,
|
|
132
|
-
};
|
|
114
|
+
fromSlot = nextFromSlot;
|
|
115
|
+
toSlot = nextFromSlot + Math.floor((byteSize - 1) / 32);
|
|
116
|
+
byteOffset = 0;
|
|
133
117
|
}
|
|
134
118
|
else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
toSlot: lastToSlot,
|
|
139
|
-
byteSize,
|
|
140
|
-
byteOffset: nextOffset,
|
|
141
|
-
type: attribute.type,
|
|
142
|
-
dynamic,
|
|
143
|
-
noValue,
|
|
144
|
-
variable: attribute.name,
|
|
145
|
-
contractName: umlClass.name,
|
|
146
|
-
referenceStorageId: referenceStorage?.id,
|
|
147
|
-
};
|
|
119
|
+
fromSlot = lastToSlot;
|
|
120
|
+
toSlot = lastToSlot;
|
|
121
|
+
byteOffset = nextOffset;
|
|
148
122
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
123
|
+
variables.push({
|
|
124
|
+
id: variableId++,
|
|
125
|
+
fromSlot,
|
|
126
|
+
toSlot,
|
|
127
|
+
byteSize,
|
|
128
|
+
byteOffset,
|
|
129
|
+
type: attribute.type,
|
|
130
|
+
attributeType: attribute.attributeType,
|
|
131
|
+
dynamic,
|
|
132
|
+
getValue,
|
|
133
|
+
displayValue,
|
|
134
|
+
name: attribute.name,
|
|
135
|
+
contractName: umlClass.name,
|
|
136
|
+
referenceSectionId: references?.storageSection?.id,
|
|
137
|
+
enumValues: references?.enumValues,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
return variables;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Recursively adjusts the fromSlot and toSlot properties of any storage variables
|
|
144
|
+
* that are referenced by a static array or struct.
|
|
145
|
+
* Also sets the storage slot offset for dynamic arrays, strings and bytes.
|
|
146
|
+
* @param storageSection
|
|
147
|
+
* @param slotOffset
|
|
148
|
+
* @param storageSections
|
|
149
|
+
*/
|
|
150
|
+
const adjustSlots = (storageSection, slotOffset, storageSections) => {
|
|
151
|
+
storageSection.variables.forEach((variable) => {
|
|
152
|
+
// offset storage slots
|
|
153
|
+
variable.fromSlot += slotOffset;
|
|
154
|
+
variable.toSlot += slotOffset;
|
|
155
|
+
// find storage section that the variable is referencing
|
|
156
|
+
const referenceStorageSection = storageSections.find((ss) => ss.id === variable.referenceSectionId);
|
|
157
|
+
if (referenceStorageSection) {
|
|
158
|
+
referenceStorageSection.offset = storageSection.offset;
|
|
159
|
+
if (!variable.dynamic) {
|
|
160
|
+
adjustSlots(referenceStorageSection, variable.fromSlot, storageSections);
|
|
152
161
|
}
|
|
153
|
-
else if (
|
|
154
|
-
|
|
162
|
+
else if (variable.attributeType === umlClass_1.AttributeType.Array) {
|
|
163
|
+
// attribute is a dynamic array
|
|
164
|
+
referenceStorageSection.offset = (0, exports.calcSectionOffset)(variable, storageSection.offset);
|
|
165
|
+
adjustSlots(referenceStorageSection, 0, storageSections);
|
|
155
166
|
}
|
|
156
167
|
}
|
|
157
|
-
variables.push(newVariable);
|
|
158
168
|
});
|
|
159
|
-
return variables;
|
|
160
169
|
};
|
|
161
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Recursively adds new storage sections under a class attribute.
|
|
172
|
+
* also returns the allowed enum values
|
|
173
|
+
* @param attribute the attribute that is referencing a storage section
|
|
174
|
+
* @param umlClass contract or file level struct
|
|
175
|
+
* @param otherClasses array of all the UML Classes
|
|
176
|
+
* @param storageSections mutable array of storageSection objects
|
|
177
|
+
* @param mapping flags that the storage section is under a mapping
|
|
178
|
+
* @param arrayItems the number of items to display at the start and end of an array
|
|
179
|
+
* @return storageSection new storage section that was added or undefined if none was added.
|
|
180
|
+
* @return enumValues array of allowed enum values. undefined if attribute is not an enum
|
|
181
|
+
*/
|
|
182
|
+
const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, storageSections, mapping, arrayItems) => {
|
|
162
183
|
if (attribute.attributeType === umlClass_1.AttributeType.Array) {
|
|
163
184
|
// storage is dynamic if the attribute type ends in []
|
|
164
|
-
const result = attribute.type.match(/\[(\w*)]$/);
|
|
185
|
+
const result = attribute.type.match(/\[([\w$.]*)]$/);
|
|
165
186
|
const dynamic = result[1] === '';
|
|
166
187
|
const arrayLength = !dynamic
|
|
167
|
-
? (0, exports.findDimensionLength)(umlClass, result[1])
|
|
188
|
+
? (0, exports.findDimensionLength)(umlClass, result[1], otherClasses)
|
|
168
189
|
: undefined;
|
|
169
190
|
// get the type of the array items. eg
|
|
170
191
|
// address[][4][2] will have base type address[][4]
|
|
@@ -181,14 +202,24 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => {
|
|
|
181
202
|
}
|
|
182
203
|
const baseAttribute = {
|
|
183
204
|
visibility: attribute.visibility,
|
|
184
|
-
name:
|
|
205
|
+
name: attribute.name,
|
|
185
206
|
type: baseType,
|
|
186
207
|
attributeType: baseAttributeType,
|
|
187
208
|
};
|
|
188
|
-
const { size: arrayItemSize } = (0, exports.calcStorageByteSize)(baseAttribute, umlClass, otherClasses);
|
|
209
|
+
const { size: arrayItemSize, dynamic: dynamicBase } = (0, exports.calcStorageByteSize)(baseAttribute, umlClass, otherClasses);
|
|
210
|
+
// If more than 16 bytes, then round up in 32 bytes increments
|
|
189
211
|
const arraySlotSize = arrayItemSize > 16
|
|
190
212
|
? 32 * Math.ceil(arrayItemSize / 32)
|
|
191
213
|
: arrayItemSize;
|
|
214
|
+
// If base type is not an Elementary type
|
|
215
|
+
// This can only be Array and UserDefined for base types of arrays.
|
|
216
|
+
let references;
|
|
217
|
+
if (baseAttributeType !== umlClass_1.AttributeType.Elementary) {
|
|
218
|
+
// recursively add storage section for Array and UserDefined types
|
|
219
|
+
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems);
|
|
220
|
+
}
|
|
221
|
+
const displayValue = calcDisplayValue(baseAttribute.attributeType, dynamicBase, mapping, references?.storageSection?.type);
|
|
222
|
+
const getValue = calcGetValue(attribute.attributeType, mapping);
|
|
192
223
|
const variables = [];
|
|
193
224
|
variables[0] = {
|
|
194
225
|
id: variableId++,
|
|
@@ -197,58 +228,63 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => {
|
|
|
197
228
|
byteSize: arrayItemSize,
|
|
198
229
|
byteOffset: 0,
|
|
199
230
|
type: baseType,
|
|
200
|
-
|
|
201
|
-
|
|
231
|
+
attributeType: baseAttributeType,
|
|
232
|
+
dynamic: dynamicBase,
|
|
233
|
+
getValue,
|
|
234
|
+
displayValue,
|
|
235
|
+
referenceSectionId: references?.storageSection?.id,
|
|
236
|
+
enumValues: references?.enumValues,
|
|
202
237
|
};
|
|
238
|
+
// If a fixed size array.
|
|
239
|
+
// Note dynamic arrays will have undefined arrayLength
|
|
203
240
|
if (arrayLength > 1) {
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// recursively add storage
|
|
219
|
-
if (baseAttributeType !== umlClass_1.AttributeType.Elementary) {
|
|
220
|
-
const referenceStorage = (0, exports.parseReferenceStorage)(baseAttribute, umlClass, otherClasses, storages);
|
|
221
|
-
variables[0].referenceStorageId = referenceStorage?.id;
|
|
241
|
+
// Add missing fixed array variables from index 1
|
|
242
|
+
addArrayVariables(arrayLength, arrayItems, variables);
|
|
243
|
+
// For the newly added variables
|
|
244
|
+
variables.forEach((variable, i) => {
|
|
245
|
+
if (i > 0 &&
|
|
246
|
+
baseAttributeType !== umlClass_1.AttributeType.Elementary &&
|
|
247
|
+
variable.type !== '----' // ignore any filler variables
|
|
248
|
+
) {
|
|
249
|
+
// recursively add storage section for Array and UserDefined types
|
|
250
|
+
references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems);
|
|
251
|
+
variable.referenceSectionId = references?.storageSection?.id;
|
|
252
|
+
variable.enumValues = references?.enumValues;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
222
255
|
}
|
|
223
|
-
const
|
|
256
|
+
const storageSection = {
|
|
224
257
|
id: storageId++,
|
|
225
258
|
name: `${attribute.type}: ${attribute.name}`,
|
|
226
|
-
type:
|
|
259
|
+
type: StorageSectionType.Array,
|
|
227
260
|
arrayDynamic: dynamic,
|
|
228
261
|
arrayLength,
|
|
229
262
|
variables,
|
|
263
|
+
mapping,
|
|
230
264
|
};
|
|
231
|
-
|
|
232
|
-
return
|
|
265
|
+
storageSections.push(storageSection);
|
|
266
|
+
return { storageSection };
|
|
233
267
|
}
|
|
234
268
|
if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) {
|
|
235
269
|
// Is the user defined type linked to another Contract, Struct or Enum?
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
throw Error(`Failed to find user defined type "${attribute.type}"`);
|
|
241
|
-
}
|
|
242
|
-
if (dependentClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
243
|
-
const variables = parseVariables(dependentClass, otherClasses, [], storages, []);
|
|
244
|
-
const newStorage = {
|
|
270
|
+
const typeClass = findTypeClass(attribute.type, attribute, otherClasses);
|
|
271
|
+
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
272
|
+
const variables = parseVariables(typeClass, otherClasses, [], storageSections, [], mapping, arrayItems);
|
|
273
|
+
const storageSection = {
|
|
245
274
|
id: storageId++,
|
|
246
275
|
name: attribute.type,
|
|
247
|
-
type:
|
|
276
|
+
type: StorageSectionType.Struct,
|
|
248
277
|
variables,
|
|
278
|
+
mapping,
|
|
279
|
+
};
|
|
280
|
+
storageSections.push(storageSection);
|
|
281
|
+
return { storageSection };
|
|
282
|
+
}
|
|
283
|
+
else if (typeClass.stereotype === umlClass_1.ClassStereotype.Enum) {
|
|
284
|
+
return {
|
|
285
|
+
storageSection: undefined,
|
|
286
|
+
enumValues: typeClass.attributes.map((a) => a.name),
|
|
249
287
|
};
|
|
250
|
-
storages.push(newStorage);
|
|
251
|
-
return newStorage;
|
|
252
288
|
}
|
|
253
289
|
return undefined;
|
|
254
290
|
}
|
|
@@ -256,31 +292,105 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => {
|
|
|
256
292
|
// get the UserDefined type from the mapping
|
|
257
293
|
// note the mapping could be an array of Structs
|
|
258
294
|
// Could also be a mapping of a mapping
|
|
259
|
-
const result = attribute.type.match(/=\\>((?!mapping)\w*)[\\[]/);
|
|
295
|
+
const result = attribute.type.match(/=\\>((?!mapping)[\w$.]*)[\\[]/);
|
|
260
296
|
// If mapping of user defined type
|
|
261
297
|
if (result !== null && result[1] && !(0, exports.isElementary)(result[1])) {
|
|
262
|
-
// Find UserDefined type
|
|
263
|
-
const typeClass =
|
|
264
|
-
if (!typeClass) {
|
|
265
|
-
throw Error(`Failed to find user defined type "${result[1]}" in attribute type "${attribute.type}"`);
|
|
266
|
-
}
|
|
298
|
+
// Find UserDefined type can be a contract, struct or enum
|
|
299
|
+
const typeClass = findTypeClass(result[1], attribute, otherClasses);
|
|
267
300
|
if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) {
|
|
268
|
-
|
|
269
|
-
const
|
|
301
|
+
let variables = parseVariables(typeClass, otherClasses, [], storageSections, [], true, arrayItems);
|
|
302
|
+
const storageSection = {
|
|
270
303
|
id: storageId++,
|
|
271
304
|
name: typeClass.name,
|
|
272
|
-
type:
|
|
305
|
+
type: StorageSectionType.Struct,
|
|
306
|
+
mapping: true,
|
|
273
307
|
variables,
|
|
274
308
|
};
|
|
275
|
-
|
|
276
|
-
return
|
|
309
|
+
storageSections.push(storageSection);
|
|
310
|
+
return { storageSection };
|
|
277
311
|
}
|
|
278
312
|
}
|
|
279
313
|
return undefined;
|
|
280
314
|
}
|
|
281
315
|
return undefined;
|
|
282
316
|
};
|
|
283
|
-
exports.
|
|
317
|
+
exports.parseStorageSectionFromAttribute = parseStorageSectionFromAttribute;
|
|
318
|
+
/**
|
|
319
|
+
* Adds missing storage variables to a fixed-size or dynamic array by cloning them from the first variable.
|
|
320
|
+
* @param arrayLength the length of the array
|
|
321
|
+
* @param arrayItems the number of items to display at the start and end of an array
|
|
322
|
+
* @param variables mutable array of storage variables that are appended to
|
|
323
|
+
*/
|
|
324
|
+
const addArrayVariables = (arrayLength, arrayItems, variables) => {
|
|
325
|
+
const arraySlotSize = variables[0].byteSize;
|
|
326
|
+
const itemsPerSlot = Math.floor(32 / arraySlotSize);
|
|
327
|
+
const slotsPerItem = Math.ceil(arraySlotSize / 32);
|
|
328
|
+
const firstFillerItem = itemsPerSlot > 0 ? arrayItems * itemsPerSlot : arrayItems;
|
|
329
|
+
const lastFillerItem = itemsPerSlot > 0
|
|
330
|
+
? arrayLength -
|
|
331
|
+
(arrayItems - 1) * itemsPerSlot - // the number of items in all but the last row
|
|
332
|
+
(arrayLength % itemsPerSlot || itemsPerSlot) - // the remaining items in the last row or all the items in a slot
|
|
333
|
+
1 // need the items before the last three rows
|
|
334
|
+
: arrayLength - arrayItems - 1;
|
|
335
|
+
// Add variable from index 1 for each item in the array
|
|
336
|
+
for (let i = 1; i < arrayLength; i++) {
|
|
337
|
+
const fromSlot = itemsPerSlot > 0 ? Math.floor(i / itemsPerSlot) : i * slotsPerItem;
|
|
338
|
+
const toSlot = itemsPerSlot > 0 ? fromSlot : fromSlot + slotsPerItem;
|
|
339
|
+
// add filler variable before adding the first of the last items of the array
|
|
340
|
+
if (i === lastFillerItem && firstFillerItem < lastFillerItem) {
|
|
341
|
+
const fillerFromSlot = itemsPerSlot > 0
|
|
342
|
+
? Math.floor(firstFillerItem / itemsPerSlot)
|
|
343
|
+
: firstFillerItem * slotsPerItem;
|
|
344
|
+
variables.push({
|
|
345
|
+
id: variableId++,
|
|
346
|
+
attributeType: umlClass_1.AttributeType.UserDefined,
|
|
347
|
+
type: '----',
|
|
348
|
+
fromSlot: fillerFromSlot,
|
|
349
|
+
toSlot: toSlot,
|
|
350
|
+
byteOffset: 0,
|
|
351
|
+
byteSize: (toSlot - fillerFromSlot + 1) * 32,
|
|
352
|
+
getValue: false,
|
|
353
|
+
displayValue: false,
|
|
354
|
+
dynamic: false,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
// Add variables for the first arrayItems and last arrayItems
|
|
358
|
+
if (i < firstFillerItem || i > lastFillerItem) {
|
|
359
|
+
const byteOffset = itemsPerSlot > 0 ? (i % itemsPerSlot) * arraySlotSize : 0;
|
|
360
|
+
const slotValue = fromSlot === 0 ? variables[0].slotValue : undefined;
|
|
361
|
+
// add array variable
|
|
362
|
+
const newVariable = {
|
|
363
|
+
...variables[0],
|
|
364
|
+
id: variableId++,
|
|
365
|
+
fromSlot,
|
|
366
|
+
toSlot,
|
|
367
|
+
byteOffset,
|
|
368
|
+
slotValue,
|
|
369
|
+
// These will be added in a separate step
|
|
370
|
+
parsedValue: undefined,
|
|
371
|
+
referenceSectionId: undefined,
|
|
372
|
+
enumValues: undefined,
|
|
373
|
+
};
|
|
374
|
+
newVariable.parsedValue = (0, slotValues_1.parseValue)(newVariable);
|
|
375
|
+
variables.push(newVariable);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Finds an attribute's user defined type that can be a Contract, Struct or Enum
|
|
381
|
+
* @param userType User defined type that is being looked for. This can be the base type of an attribute.
|
|
382
|
+
* @param attribute the attribute in the class that is user defined. This is just used for logging purposes
|
|
383
|
+
* @param otherClasses
|
|
384
|
+
*/
|
|
385
|
+
const findTypeClass = (userType, attribute, otherClasses) => {
|
|
386
|
+
// Find associated UserDefined type
|
|
387
|
+
// TODO this just matches on name and doesn't take into account imports
|
|
388
|
+
const typeClass = otherClasses.find(({ name }) => name === userType || name === userType.split('.')[1]);
|
|
389
|
+
if (!typeClass) {
|
|
390
|
+
throw Error(`Failed to find user defined type "${userType}" in attribute "${attribute.name}" of type "${attribute.attributeType}""`);
|
|
391
|
+
}
|
|
392
|
+
return typeClass;
|
|
393
|
+
};
|
|
284
394
|
// Calculates the storage size of an attribute in bytes
|
|
285
395
|
const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
286
396
|
if (attribute.attributeType === umlClass_1.AttributeType.Mapping ||
|
|
@@ -291,7 +401,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
291
401
|
// Fixed sized arrays are read from right to left until there is a dynamic dimension
|
|
292
402
|
// eg address[][3][2] is a fixed size array that uses 6 slots.
|
|
293
403
|
// while address [2][] is a dynamic sized array.
|
|
294
|
-
const arrayDimensions = attribute.type.match(/\[\w*]/g);
|
|
404
|
+
const arrayDimensions = attribute.type.match(/\[[\w$.]*]/g);
|
|
295
405
|
// Remove first [ and last ] from each arrayDimensions
|
|
296
406
|
const dimensionsStr = arrayDimensions.map((a) => a.slice(1, -1));
|
|
297
407
|
// fixed-sized arrays are read from right to left so reverse the dimensions
|
|
@@ -300,7 +410,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
300
410
|
let dimension = dimensionsStrReversed.shift();
|
|
301
411
|
const fixedDimensions = [];
|
|
302
412
|
while (dimension && dimension !== '') {
|
|
303
|
-
const dimensionNum = (0, exports.findDimensionLength)(umlClass, dimension);
|
|
413
|
+
const dimensionNum = (0, exports.findDimensionLength)(umlClass, dimension, otherClasses);
|
|
304
414
|
fixedDimensions.push(dimensionNum);
|
|
305
415
|
// read the next dimension for the next loop
|
|
306
416
|
dimension = dimensionsStrReversed.shift();
|
|
@@ -311,9 +421,9 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
311
421
|
// the array length is stored in the 32 byte slot
|
|
312
422
|
return { size: 32, dynamic: true };
|
|
313
423
|
}
|
|
424
|
+
// If a fixed sized array
|
|
314
425
|
let elementSize;
|
|
315
426
|
const type = attribute.type.substring(0, attribute.type.indexOf('['));
|
|
316
|
-
// If a fixed sized array
|
|
317
427
|
if ((0, exports.isElementary)(type)) {
|
|
318
428
|
const elementAttribute = {
|
|
319
429
|
attributeType: umlClass_1.AttributeType.Elementary,
|
|
@@ -343,7 +453,11 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
343
453
|
};
|
|
344
454
|
}
|
|
345
455
|
const lastItem = fixedDimensions.length - 1;
|
|
346
|
-
const
|
|
456
|
+
const lastArrayLength = fixedDimensions[lastItem];
|
|
457
|
+
const itemsPerSlot = Math.floor(32 / elementSize);
|
|
458
|
+
const lastDimensionBytes = itemsPerSlot > 0 // if one or more array items in a slot
|
|
459
|
+
? Math.ceil(lastArrayLength / itemsPerSlot) * 32 // round up to include unallocated slot space
|
|
460
|
+
: elementSize * fixedDimensions[lastItem];
|
|
347
461
|
const lastDimensionSlotBytes = Math.ceil(lastDimensionBytes / 32) * 32;
|
|
348
462
|
const remainingDimensions = fixedDimensions
|
|
349
463
|
.slice(0, lastItem)
|
|
@@ -353,16 +467,12 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
353
467
|
dynamic: false,
|
|
354
468
|
};
|
|
355
469
|
}
|
|
356
|
-
// If a Struct or
|
|
470
|
+
// If a Struct, Enum or Contract reference
|
|
471
|
+
// TODO need to handle User Defined Value Types when they are added to Solidity
|
|
357
472
|
if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) {
|
|
358
473
|
// Is the user defined type linked to another Contract, Struct or Enum?
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
});
|
|
362
|
-
if (!attributeClass) {
|
|
363
|
-
throw Error(`Failed to find user defined struct or enum "${attribute.type}"`);
|
|
364
|
-
}
|
|
365
|
-
switch (attributeClass.stereotype) {
|
|
474
|
+
const attributeTypeClass = findTypeClass(attribute.type, attribute, otherClasses);
|
|
475
|
+
switch (attributeTypeClass.stereotype) {
|
|
366
476
|
case umlClass_1.ClassStereotype.Enum:
|
|
367
477
|
return { size: 1, dynamic: false };
|
|
368
478
|
case umlClass_1.ClassStereotype.Contract:
|
|
@@ -372,7 +482,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
372
482
|
return { size: 20, dynamic: false };
|
|
373
483
|
case umlClass_1.ClassStereotype.Struct:
|
|
374
484
|
let structByteSize = 0;
|
|
375
|
-
|
|
485
|
+
attributeTypeClass.attributes.forEach((structAttribute) => {
|
|
376
486
|
// If next attribute is an array, then we need to start in a new slot
|
|
377
487
|
if (structAttribute.attributeType === umlClass_1.AttributeType.Array) {
|
|
378
488
|
structByteSize = Math.ceil(structByteSize / 32) * 32;
|
|
@@ -381,13 +491,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
381
491
|
else if (structAttribute.attributeType ===
|
|
382
492
|
umlClass_1.AttributeType.UserDefined) {
|
|
383
493
|
// UserDefined types can be a struct or enum, so we need to check if it's a struct
|
|
384
|
-
const userDefinedClass =
|
|
385
|
-
return (name === structAttribute.type ||
|
|
386
|
-
name === structAttribute.type.split('.')[1]);
|
|
387
|
-
});
|
|
388
|
-
if (!userDefinedClass) {
|
|
389
|
-
throw Error(`Failed to find user defined type "${structAttribute.type}" in struct ${attributeClass.name}`);
|
|
390
|
-
}
|
|
494
|
+
const userDefinedClass = findTypeClass(structAttribute.type, structAttribute, otherClasses);
|
|
391
495
|
// If a struct
|
|
392
496
|
if (userDefinedClass.stereotype ===
|
|
393
497
|
umlClass_1.ClassStereotype.Struct) {
|
|
@@ -422,6 +526,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => {
|
|
|
422
526
|
return { size: 20, dynamic: false };
|
|
423
527
|
case 'string':
|
|
424
528
|
case 'bytes':
|
|
529
|
+
return { size: 32, dynamic: true };
|
|
425
530
|
case 'uint':
|
|
426
531
|
case 'int':
|
|
427
532
|
case 'ufixed':
|
|
@@ -457,49 +562,206 @@ const isElementary = (type) => {
|
|
|
457
562
|
case 'fixed':
|
|
458
563
|
return true;
|
|
459
564
|
default:
|
|
460
|
-
const result = type.match(
|
|
565
|
+
const result = type.match(/^[u]?(int|fixed|bytes)([0-9]+)$/);
|
|
461
566
|
return result !== null;
|
|
462
567
|
}
|
|
463
568
|
};
|
|
464
569
|
exports.isElementary = isElementary;
|
|
465
|
-
const
|
|
570
|
+
const calcSectionOffset = (variable, sectionOffset = '0') => {
|
|
466
571
|
if (variable.dynamic) {
|
|
467
|
-
|
|
572
|
+
const hexStringOf32Bytes = (0, utils_1.hexZeroPad)(ethers_1.BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString(), 32);
|
|
573
|
+
return (0, utils_1.keccak256)(hexStringOf32Bytes);
|
|
468
574
|
}
|
|
469
|
-
return ethers_1.BigNumber.from(variable.fromSlot).toHexString();
|
|
470
|
-
};
|
|
471
|
-
exports.calcSlotKey = calcSlotKey;
|
|
472
|
-
// recursively offset the slots numbers of a storage item
|
|
473
|
-
const offsetStorageSlots = (storage, slots, storages) => {
|
|
474
|
-
storage.variables.forEach((variable) => {
|
|
475
|
-
variable.fromSlot += slots;
|
|
476
|
-
variable.toSlot += slots;
|
|
477
|
-
if (variable.referenceStorageId) {
|
|
478
|
-
// recursively offset the referenced storage
|
|
479
|
-
const referenceStorage = storages.find((s) => s.id === variable.referenceStorageId);
|
|
480
|
-
if (!referenceStorage.arrayDynamic) {
|
|
481
|
-
(0, exports.offsetStorageSlots)(referenceStorage, slots, storages);
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
referenceStorage.slotKey = (0, exports.calcSlotKey)(variable);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
});
|
|
575
|
+
return ethers_1.BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString();
|
|
488
576
|
};
|
|
489
|
-
exports.
|
|
490
|
-
const findDimensionLength = (umlClass, dimension) => {
|
|
577
|
+
exports.calcSectionOffset = calcSectionOffset;
|
|
578
|
+
const findDimensionLength = (umlClass, dimension, otherClasses) => {
|
|
491
579
|
const dimensionNum = parseInt(dimension);
|
|
492
580
|
if (Number.isInteger(dimensionNum)) {
|
|
493
581
|
return dimensionNum;
|
|
494
582
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (!constant) {
|
|
499
|
-
throw Error(`Could not size fixed sized array with dimension "${dimension}"`);
|
|
500
|
-
}
|
|
583
|
+
// Try and size array dimension from declared constants
|
|
584
|
+
const constant = umlClass.constants.find((constant) => constant.name === dimension);
|
|
585
|
+
if (constant) {
|
|
501
586
|
return constant.value;
|
|
502
587
|
}
|
|
588
|
+
// Try and size array dimension from file constants
|
|
589
|
+
const fileConstant = otherClasses.find((umlClass) => umlClass.name === dimension &&
|
|
590
|
+
umlClass.stereotype === umlClass_1.ClassStereotype.Constant);
|
|
591
|
+
if (fileConstant?.constants[0]?.value) {
|
|
592
|
+
return fileConstant.constants[0].value;
|
|
593
|
+
}
|
|
594
|
+
throw Error(`Could not size fixed sized array with dimension "${dimension}"`);
|
|
503
595
|
};
|
|
504
596
|
exports.findDimensionLength = findDimensionLength;
|
|
597
|
+
/**
|
|
598
|
+
* Calculate if the storage slot value for the attribute should be displayed in the storage section.
|
|
599
|
+
*
|
|
600
|
+
* Storage sections with true mapping should return false.
|
|
601
|
+
* Mapping types should return false.
|
|
602
|
+
* Elementary types should return true.
|
|
603
|
+
* Dynamic Array types should return true.
|
|
604
|
+
* Static Array types should return false.
|
|
605
|
+
* UserDefined types that are Structs should return false.
|
|
606
|
+
* UserDefined types that are Enums or alias to Elementary type or contract should return true.
|
|
607
|
+
*
|
|
608
|
+
* @param attributeType
|
|
609
|
+
* @param dynamic flags if the variable is of dynamic size
|
|
610
|
+
* @param mapping flags if the storage section is referenced by a mapping
|
|
611
|
+
* @param storageSectionType
|
|
612
|
+
* @return displayValue true if the slot value should be displayed.
|
|
613
|
+
*/
|
|
614
|
+
const calcDisplayValue = (attributeType, dynamic, mapping, storageSectionType) => mapping === false &&
|
|
615
|
+
(attributeType === umlClass_1.AttributeType.Elementary ||
|
|
616
|
+
(attributeType === umlClass_1.AttributeType.UserDefined &&
|
|
617
|
+
storageSectionType !== StorageSectionType.Struct) ||
|
|
618
|
+
(attributeType === umlClass_1.AttributeType.Array && dynamic));
|
|
619
|
+
/**
|
|
620
|
+
* Calculate if the storage slot value for the attribute should be retrieved from the chain.
|
|
621
|
+
*
|
|
622
|
+
* Storage sections with true mapping should return false.
|
|
623
|
+
* Mapping types should return false.
|
|
624
|
+
* Elementary types should return true.
|
|
625
|
+
* Array types should return true.
|
|
626
|
+
* UserDefined should return true.
|
|
627
|
+
*
|
|
628
|
+
* @param attributeType the type of attribute the storage variable is for.
|
|
629
|
+
* @param mapping flags if the storage section is referenced by a mapping
|
|
630
|
+
* @return getValue true if the slot value should be retrieved.
|
|
631
|
+
*/
|
|
632
|
+
const calcGetValue = (attributeType, mapping) => mapping === false && attributeType !== umlClass_1.AttributeType.Mapping;
|
|
633
|
+
/**
|
|
634
|
+
* Recursively adds variables for dynamic string, bytes or arrays
|
|
635
|
+
* @param storageSection
|
|
636
|
+
* @param storageSections
|
|
637
|
+
* @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy
|
|
638
|
+
* @param contractAddress Contract address to get the storage slot values from.
|
|
639
|
+
* @param arrayItems the number of items to display at the start and end of an array
|
|
640
|
+
* @param blockTag block number or `latest`
|
|
641
|
+
*/
|
|
642
|
+
const addDynamicVariables = async (storageSection, storageSections, url, contractAddress, arrayItems, blockTag) => {
|
|
643
|
+
for (const variable of storageSection.variables) {
|
|
644
|
+
try {
|
|
645
|
+
if (!variable.dynamic)
|
|
646
|
+
continue;
|
|
647
|
+
// STEP 1 - add slots for dynamic string and bytes
|
|
648
|
+
if (variable.type === 'string' || variable.type === 'bytes') {
|
|
649
|
+
if (!variable.slotValue) {
|
|
650
|
+
debug(`WARNING: Variable "${variable.name}" of type "${variable.type}" has no slot value`);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const size = (0, slotValues_1.dynamicSlotSize)(variable);
|
|
654
|
+
if (size > 31) {
|
|
655
|
+
const maxSlotNumber = Math.floor((size - 1) / 32);
|
|
656
|
+
const variables = [];
|
|
657
|
+
// For each dynamic slot
|
|
658
|
+
for (let i = 0; i <= maxSlotNumber; i++) {
|
|
659
|
+
// If the last slot then get the remaining bytes
|
|
660
|
+
const byteSize = i === maxSlotNumber ? size - 32 * maxSlotNumber : 32;
|
|
661
|
+
// Add variable for the slot
|
|
662
|
+
variables.push({
|
|
663
|
+
id: variableId++,
|
|
664
|
+
fromSlot: i,
|
|
665
|
+
toSlot: i,
|
|
666
|
+
byteSize,
|
|
667
|
+
byteOffset: 0,
|
|
668
|
+
type: variable.type,
|
|
669
|
+
contractName: variable.contractName,
|
|
670
|
+
attributeType: umlClass_1.AttributeType.Elementary,
|
|
671
|
+
dynamic: false,
|
|
672
|
+
getValue: true,
|
|
673
|
+
displayValue: true,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
// add unallocated variable
|
|
677
|
+
const unusedBytes = 32 - (size - 32 * maxSlotNumber);
|
|
678
|
+
if (unusedBytes > 0) {
|
|
679
|
+
const lastVariable = variables[variables.length - 1];
|
|
680
|
+
variables.push({
|
|
681
|
+
...lastVariable,
|
|
682
|
+
byteOffset: unusedBytes,
|
|
683
|
+
});
|
|
684
|
+
variables[maxSlotNumber] = {
|
|
685
|
+
id: variableId++,
|
|
686
|
+
fromSlot: maxSlotNumber,
|
|
687
|
+
toSlot: maxSlotNumber,
|
|
688
|
+
byteSize: unusedBytes,
|
|
689
|
+
byteOffset: 0,
|
|
690
|
+
type: 'unallocated',
|
|
691
|
+
attributeType: umlClass_1.AttributeType.UserDefined,
|
|
692
|
+
contractName: variable.contractName,
|
|
693
|
+
name: '',
|
|
694
|
+
dynamic: false,
|
|
695
|
+
getValue: true,
|
|
696
|
+
displayValue: false,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const newStorageSection = {
|
|
700
|
+
id: storageId++,
|
|
701
|
+
name: `${variable.type}: ${variable.name}`,
|
|
702
|
+
offset: (0, exports.calcSectionOffset)(variable, storageSection.offset),
|
|
703
|
+
type: variable.type === 'string'
|
|
704
|
+
? StorageSectionType.String
|
|
705
|
+
: StorageSectionType.Bytes,
|
|
706
|
+
arrayDynamic: true,
|
|
707
|
+
arrayLength: size,
|
|
708
|
+
variables,
|
|
709
|
+
mapping: false,
|
|
710
|
+
};
|
|
711
|
+
variable.referenceSectionId = newStorageSection.id;
|
|
712
|
+
// get slot values for new referenced dynamic string or bytes
|
|
713
|
+
await (0, slotValues_1.addSlotValues)(url, contractAddress, newStorageSection, arrayItems, blockTag);
|
|
714
|
+
storageSections.push(newStorageSection);
|
|
715
|
+
}
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (variable.attributeType !== umlClass_1.AttributeType.Array)
|
|
719
|
+
continue;
|
|
720
|
+
// STEP 2 - add slots for dynamic arrays
|
|
721
|
+
// find storage section that the variable is referencing
|
|
722
|
+
const referenceStorageSection = storageSections.find((ss) => ss.id === variable.referenceSectionId);
|
|
723
|
+
if (!referenceStorageSection)
|
|
724
|
+
continue;
|
|
725
|
+
// recursively add dynamic variables to referenced array.
|
|
726
|
+
// this could be a fixed-size or dynamic array
|
|
727
|
+
await (0, exports.addDynamicVariables)(referenceStorageSection, storageSections, url, contractAddress, arrayItems, blockTag);
|
|
728
|
+
if (!variable.slotValue) {
|
|
729
|
+
debug(`WARNING: Dynamic array variable "${variable.name}" of type "${variable.type}" has no slot value`);
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
// Add missing dynamic array variables
|
|
733
|
+
const arrayLength = ethers_1.BigNumber.from(variable.slotValue).toNumber();
|
|
734
|
+
if (arrayLength > 1) {
|
|
735
|
+
// Add missing array variables to the referenced dynamic array
|
|
736
|
+
addArrayVariables(arrayLength, arrayItems, referenceStorageSection.variables);
|
|
737
|
+
// // For the newly added variables
|
|
738
|
+
// referenceStorageSection.variables.forEach((variable, i) => {
|
|
739
|
+
// if (
|
|
740
|
+
// referenceStorageSection.variables[0].attributeType !==
|
|
741
|
+
// AttributeType.Elementary &&
|
|
742
|
+
// i > 0
|
|
743
|
+
// ) {
|
|
744
|
+
// // recursively add storage section for Array and UserDefined types
|
|
745
|
+
// const references = parseStorageSectionFromAttribute(
|
|
746
|
+
// baseAttribute,
|
|
747
|
+
// umlClass,
|
|
748
|
+
// otherClasses,
|
|
749
|
+
// storageSections,
|
|
750
|
+
// mapping,
|
|
751
|
+
// arrayItems
|
|
752
|
+
// )
|
|
753
|
+
// variable.referenceSectionId = references.storageSection?.id
|
|
754
|
+
// variable.enumValues = references?.enumValues
|
|
755
|
+
// }
|
|
756
|
+
// })
|
|
757
|
+
}
|
|
758
|
+
// Get missing slot values to the referenced dynamic array
|
|
759
|
+
await (0, slotValues_1.addSlotValues)(url, contractAddress, referenceStorageSection, arrayItems, blockTag);
|
|
760
|
+
}
|
|
761
|
+
catch (err) {
|
|
762
|
+
throw Error(`Failed to add dynamic vars for section "${storageSection.name}", var type "${variable.type}" with value "${variable.slotValue}" from slot ${variable.fromSlot} and section offset ${storageSection.offset}`, { cause: err });
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
exports.addDynamicVariables = addDynamicVariables;
|
|
505
767
|
//# sourceMappingURL=converterClasses2Storage.js.map
|