recon-generate 0.0.15 → 0.0.17
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/dist/index.js +18 -0
- package/dist/info.d.ts +40 -0
- package/dist/info.js +1017 -0
- package/dist/z3Solver.d.ts +11 -0
- package/dist/z3Solver.js +425 -0
- package/package.json +3 -2
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FunctionDefinition } from 'solc-typed-ast';
|
|
2
|
+
interface Z3Solution {
|
|
3
|
+
variable: string;
|
|
4
|
+
value: string;
|
|
5
|
+
type: string;
|
|
6
|
+
expression: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const setZ3Silent: (silent: boolean) => void;
|
|
9
|
+
export declare const findZ3Solutions: (fnDef: FunctionDefinition) => Promise<Z3Solution[]>;
|
|
10
|
+
export declare const cleanupZ3: () => Promise<void>;
|
|
11
|
+
export {};
|
package/dist/z3Solver.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cleanupZ3 = exports.findZ3Solutions = exports.setZ3Silent = void 0;
|
|
4
|
+
const z3_solver_1 = require("z3-solver");
|
|
5
|
+
const solc_typed_ast_1 = require("solc-typed-ast");
|
|
6
|
+
let z3Context = null;
|
|
7
|
+
const initZ3 = async () => {
|
|
8
|
+
if (z3Context)
|
|
9
|
+
return z3Context;
|
|
10
|
+
const { Context } = await (0, z3_solver_1.init)();
|
|
11
|
+
const ctx = Context('main');
|
|
12
|
+
const solver = new ctx.Solver();
|
|
13
|
+
z3Context = { ctx, solver };
|
|
14
|
+
return z3Context;
|
|
15
|
+
};
|
|
16
|
+
const getBitSize = (typeString) => {
|
|
17
|
+
// Extract bit size from type like int128, uint256, etc.
|
|
18
|
+
const match = typeString.match(/u?int(\d+)/);
|
|
19
|
+
if (match)
|
|
20
|
+
return parseInt(match[1], 10);
|
|
21
|
+
// Handle bytes types (bytes1 = 8 bits, bytes32 = 256 bits)
|
|
22
|
+
const bytesMatch = typeString.match(/^bytes(\d+)$/);
|
|
23
|
+
if (bytesMatch)
|
|
24
|
+
return parseInt(bytesMatch[1], 10) * 8;
|
|
25
|
+
// Default sizes
|
|
26
|
+
if (typeString.includes('int'))
|
|
27
|
+
return 256;
|
|
28
|
+
if (typeString === 'bool')
|
|
29
|
+
return 8;
|
|
30
|
+
if (typeString.startsWith('bytes'))
|
|
31
|
+
return 256; // dynamic bytes
|
|
32
|
+
return 256;
|
|
33
|
+
};
|
|
34
|
+
const isSigned = (typeString) => {
|
|
35
|
+
return typeString.startsWith('int') && !typeString.startsWith('uint');
|
|
36
|
+
};
|
|
37
|
+
const extractVariables = (expr) => {
|
|
38
|
+
const vars = [];
|
|
39
|
+
const visit = (node) => {
|
|
40
|
+
if (node instanceof solc_typed_ast_1.Identifier) {
|
|
41
|
+
// Skip constants and special identifiers
|
|
42
|
+
if (node.name !== 'true' && node.name !== 'false') {
|
|
43
|
+
vars.push({
|
|
44
|
+
name: node.name,
|
|
45
|
+
type: node.typeString || 'uint256',
|
|
46
|
+
node
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (node instanceof solc_typed_ast_1.IndexAccess) {
|
|
51
|
+
// foo[0] - treat as a variable
|
|
52
|
+
const baseName = getExpressionName(node);
|
|
53
|
+
vars.push({
|
|
54
|
+
name: baseName,
|
|
55
|
+
type: node.typeString || 'uint256',
|
|
56
|
+
node
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (node instanceof solc_typed_ast_1.MemberAccess) {
|
|
60
|
+
// foo.bar - could be a variable access
|
|
61
|
+
// Check if it's not a function call parent
|
|
62
|
+
const baseName = getExpressionName(node);
|
|
63
|
+
vars.push({
|
|
64
|
+
name: baseName,
|
|
65
|
+
type: node.typeString || 'uint256',
|
|
66
|
+
node
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (node instanceof solc_typed_ast_1.FunctionCall) {
|
|
70
|
+
// Handle type conversions - recurse into the argument
|
|
71
|
+
if (node.kind === solc_typed_ast_1.FunctionCallKind.TypeConversion && node.vArguments.length === 1) {
|
|
72
|
+
visit(node.vArguments[0]);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// foo.bar() - treat whole thing as a variable
|
|
76
|
+
const baseName = getExpressionName(node);
|
|
77
|
+
vars.push({
|
|
78
|
+
name: baseName,
|
|
79
|
+
type: node.typeString || 'uint256',
|
|
80
|
+
node
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (node instanceof solc_typed_ast_1.BinaryOperation) {
|
|
85
|
+
visit(node.vLeftExpression);
|
|
86
|
+
visit(node.vRightExpression);
|
|
87
|
+
}
|
|
88
|
+
else if (node instanceof solc_typed_ast_1.UnaryOperation) {
|
|
89
|
+
visit(node.vSubExpression);
|
|
90
|
+
}
|
|
91
|
+
else if (node instanceof solc_typed_ast_1.TupleExpression) {
|
|
92
|
+
// Handle parentheses - visit all components
|
|
93
|
+
for (const comp of node.vComponents) {
|
|
94
|
+
if (comp)
|
|
95
|
+
visit(comp);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
visit(expr);
|
|
100
|
+
return vars;
|
|
101
|
+
};
|
|
102
|
+
const getExpressionName = (expr) => {
|
|
103
|
+
if (expr instanceof solc_typed_ast_1.Identifier) {
|
|
104
|
+
return expr.name;
|
|
105
|
+
}
|
|
106
|
+
else if (expr instanceof solc_typed_ast_1.IndexAccess) {
|
|
107
|
+
const base = getExpressionName(expr.vBaseExpression);
|
|
108
|
+
const index = expr.vIndexExpression ? getExpressionName(expr.vIndexExpression) : '0';
|
|
109
|
+
return `${base}_${index}`.replace(/[^a-zA-Z0-9_]/g, '');
|
|
110
|
+
}
|
|
111
|
+
else if (expr instanceof solc_typed_ast_1.MemberAccess) {
|
|
112
|
+
const base = getExpressionName(expr.vExpression);
|
|
113
|
+
return `${base}_${expr.memberName}`.replace(/[^a-zA-Z0-9_]/g, '');
|
|
114
|
+
}
|
|
115
|
+
else if (expr instanceof solc_typed_ast_1.FunctionCall) {
|
|
116
|
+
if (expr.vExpression instanceof solc_typed_ast_1.MemberAccess) {
|
|
117
|
+
return getExpressionName(expr.vExpression).replace(/[^a-zA-Z0-9_]/g, '');
|
|
118
|
+
}
|
|
119
|
+
return getExpressionName(expr.vExpression).replace(/[^a-zA-Z0-9_]/g, '');
|
|
120
|
+
}
|
|
121
|
+
else if (expr instanceof solc_typed_ast_1.Literal) {
|
|
122
|
+
return expr.value || '0';
|
|
123
|
+
}
|
|
124
|
+
return 'unknown';
|
|
125
|
+
};
|
|
126
|
+
const expressionToZ3 = (ctx, expr, varMap, bitSize, signed) => {
|
|
127
|
+
if (expr instanceof solc_typed_ast_1.Literal) {
|
|
128
|
+
let value = expr.value || '0';
|
|
129
|
+
// Handle hex values
|
|
130
|
+
if (value.startsWith('0x')) {
|
|
131
|
+
value = BigInt(value).toString();
|
|
132
|
+
}
|
|
133
|
+
// Handle negative values for signed types
|
|
134
|
+
if (value.startsWith('-')) {
|
|
135
|
+
const absVal = BigInt(value.slice(1));
|
|
136
|
+
// Two's complement
|
|
137
|
+
const maxVal = BigInt(1) << BigInt(bitSize);
|
|
138
|
+
const signedVal = maxVal - absVal;
|
|
139
|
+
return ctx.BitVec.val(signedVal, bitSize);
|
|
140
|
+
}
|
|
141
|
+
return ctx.BitVec.val(BigInt(value), bitSize);
|
|
142
|
+
}
|
|
143
|
+
if (expr instanceof solc_typed_ast_1.Identifier) {
|
|
144
|
+
const z3Var = varMap.get(expr.name);
|
|
145
|
+
if (z3Var)
|
|
146
|
+
return z3Var;
|
|
147
|
+
// Create new variable
|
|
148
|
+
const newVar = ctx.BitVec.const(expr.name, bitSize);
|
|
149
|
+
varMap.set(expr.name, newVar);
|
|
150
|
+
return newVar;
|
|
151
|
+
}
|
|
152
|
+
if (expr instanceof solc_typed_ast_1.FunctionCall) {
|
|
153
|
+
// Handle type conversions like uint24(...), uint256(...)
|
|
154
|
+
if (expr.kind === solc_typed_ast_1.FunctionCallKind.TypeConversion && expr.vArguments.length === 1) {
|
|
155
|
+
// Recursively process the inner expression
|
|
156
|
+
// The type conversion just changes interpretation, not the bits we're solving for
|
|
157
|
+
return expressionToZ3(ctx, expr.vArguments[0], varMap, bitSize, signed);
|
|
158
|
+
}
|
|
159
|
+
// Other function calls - treat as opaque variable
|
|
160
|
+
const name = getExpressionName(expr);
|
|
161
|
+
const z3Var = varMap.get(name);
|
|
162
|
+
if (z3Var)
|
|
163
|
+
return z3Var;
|
|
164
|
+
const newVar = ctx.BitVec.const(name, bitSize);
|
|
165
|
+
varMap.set(name, newVar);
|
|
166
|
+
return newVar;
|
|
167
|
+
}
|
|
168
|
+
if (expr instanceof solc_typed_ast_1.IndexAccess || expr instanceof solc_typed_ast_1.MemberAccess) {
|
|
169
|
+
const name = getExpressionName(expr);
|
|
170
|
+
const z3Var = varMap.get(name);
|
|
171
|
+
if (z3Var)
|
|
172
|
+
return z3Var;
|
|
173
|
+
const newVar = ctx.BitVec.const(name, bitSize);
|
|
174
|
+
varMap.set(name, newVar);
|
|
175
|
+
return newVar;
|
|
176
|
+
}
|
|
177
|
+
if (expr instanceof solc_typed_ast_1.UnaryOperation) {
|
|
178
|
+
const subExpr = expressionToZ3(ctx, expr.vSubExpression, varMap, bitSize, signed);
|
|
179
|
+
if (!subExpr)
|
|
180
|
+
return null;
|
|
181
|
+
switch (expr.operator) {
|
|
182
|
+
case '-':
|
|
183
|
+
return ctx.BitVec.val(0, bitSize).sub(subExpr);
|
|
184
|
+
case '~':
|
|
185
|
+
return subExpr.neg(); // bitwise not
|
|
186
|
+
case '!':
|
|
187
|
+
// Logical not - treat as comparison with 0
|
|
188
|
+
return null; // Skip for now
|
|
189
|
+
default:
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (expr instanceof solc_typed_ast_1.BinaryOperation) {
|
|
194
|
+
const left = expressionToZ3(ctx, expr.vLeftExpression, varMap, bitSize, signed);
|
|
195
|
+
const right = expressionToZ3(ctx, expr.vRightExpression, varMap, bitSize, signed);
|
|
196
|
+
if (!left || !right)
|
|
197
|
+
return null;
|
|
198
|
+
switch (expr.operator) {
|
|
199
|
+
case '+':
|
|
200
|
+
return left.add(right);
|
|
201
|
+
case '-':
|
|
202
|
+
return left.sub(right);
|
|
203
|
+
case '*':
|
|
204
|
+
return left.mul(right);
|
|
205
|
+
case '/':
|
|
206
|
+
return signed ? left.sdiv(right) : left.udiv(right);
|
|
207
|
+
case '%':
|
|
208
|
+
return signed ? left.smod(right) : left.urem(right);
|
|
209
|
+
case '>>':
|
|
210
|
+
return signed ? left.shr(right) : left.lshr(right);
|
|
211
|
+
case '<<':
|
|
212
|
+
return left.shl(right);
|
|
213
|
+
case '&':
|
|
214
|
+
return left.and(right);
|
|
215
|
+
case '|':
|
|
216
|
+
return left.or(right);
|
|
217
|
+
case '^':
|
|
218
|
+
return left.xor(right);
|
|
219
|
+
default:
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (expr instanceof solc_typed_ast_1.TupleExpression) {
|
|
224
|
+
// Handle parentheses - just unwrap if single component
|
|
225
|
+
if (expr.vComponents.length === 1 && expr.vComponents[0]) {
|
|
226
|
+
return expressionToZ3(ctx, expr.vComponents[0], varMap, bitSize, signed);
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
};
|
|
232
|
+
const createZ3Constraint = (ctx, expr, varMap, bitSize, signed) => {
|
|
233
|
+
const left = expressionToZ3(ctx, expr.vLeftExpression, varMap, bitSize, signed);
|
|
234
|
+
const right = expressionToZ3(ctx, expr.vRightExpression, varMap, bitSize, signed);
|
|
235
|
+
if (!left || !right)
|
|
236
|
+
return null;
|
|
237
|
+
switch (expr.operator) {
|
|
238
|
+
case '==':
|
|
239
|
+
return left.eq(right);
|
|
240
|
+
case '!=':
|
|
241
|
+
return left.neq(right);
|
|
242
|
+
case '<':
|
|
243
|
+
return signed ? left.slt(right) : left.ult(right);
|
|
244
|
+
case '<=':
|
|
245
|
+
return signed ? left.sle(right) : left.ule(right);
|
|
246
|
+
case '>':
|
|
247
|
+
return signed ? left.sgt(right) : left.ugt(right);
|
|
248
|
+
case '>=':
|
|
249
|
+
return signed ? left.sge(right) : left.uge(right);
|
|
250
|
+
default:
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Parse Z3 value string to BigInt
|
|
256
|
+
* Z3 returns hex values in format like #xFFFF... or decimal strings
|
|
257
|
+
*/
|
|
258
|
+
const parseZ3Value = (valueStr) => {
|
|
259
|
+
// Handle Z3 hex format: #xFFFF... -> 0xFFFF...
|
|
260
|
+
if (valueStr.startsWith('#x')) {
|
|
261
|
+
return BigInt('0x' + valueStr.slice(2));
|
|
262
|
+
}
|
|
263
|
+
// Handle Z3 binary format: #b0101... -> 0b0101...
|
|
264
|
+
if (valueStr.startsWith('#b')) {
|
|
265
|
+
return BigInt('0b' + valueStr.slice(2));
|
|
266
|
+
}
|
|
267
|
+
// Handle regular decimal or hex
|
|
268
|
+
return BigInt(valueStr);
|
|
269
|
+
};
|
|
270
|
+
const solveConstraint = async (ctx, constraint, varMap, bitSize, signed) => {
|
|
271
|
+
const solver = new ctx.Solver();
|
|
272
|
+
solver.add(constraint);
|
|
273
|
+
const result = await solver.check();
|
|
274
|
+
if (result !== 'sat')
|
|
275
|
+
return null;
|
|
276
|
+
const model = solver.model();
|
|
277
|
+
const solutions = new Map();
|
|
278
|
+
for (const [name, z3Var] of varMap) {
|
|
279
|
+
const value = model.eval(z3Var);
|
|
280
|
+
const valueStr = value.toString();
|
|
281
|
+
let numValue = parseZ3Value(valueStr);
|
|
282
|
+
// Convert to signed if needed
|
|
283
|
+
if (signed && numValue >= BigInt(1) << BigInt(bitSize - 1)) {
|
|
284
|
+
numValue = numValue - (BigInt(1) << BigInt(bitSize));
|
|
285
|
+
}
|
|
286
|
+
solutions.set(name, numValue);
|
|
287
|
+
}
|
|
288
|
+
return solutions;
|
|
289
|
+
};
|
|
290
|
+
// Global silent mode flag
|
|
291
|
+
let silentMode = false;
|
|
292
|
+
const setZ3Silent = (silent) => {
|
|
293
|
+
silentMode = silent;
|
|
294
|
+
};
|
|
295
|
+
exports.setZ3Silent = setZ3Silent;
|
|
296
|
+
const findZ3Solutions = async (fnDef) => {
|
|
297
|
+
const solutions = [];
|
|
298
|
+
try {
|
|
299
|
+
const { ctx } = await initZ3();
|
|
300
|
+
// Find all comparison operations
|
|
301
|
+
const binOps = fnDef.getChildrenByType(solc_typed_ast_1.BinaryOperation);
|
|
302
|
+
for (const binOp of binOps) {
|
|
303
|
+
// Only handle equality operator
|
|
304
|
+
if (binOp.operator !== '==') {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
// Skip trivial comparisons where left side is just a simple variable or member access
|
|
308
|
+
// e.g., "x == 5" or "block.number == 100000" - the answer is obvious
|
|
309
|
+
const leftIsTrivial = binOp.vLeftExpression instanceof solc_typed_ast_1.Identifier || binOp.vLeftExpression instanceof solc_typed_ast_1.MemberAccess;
|
|
310
|
+
const rightIsTrivial = binOp.vRightExpression instanceof solc_typed_ast_1.Identifier || binOp.vRightExpression instanceof solc_typed_ast_1.MemberAccess;
|
|
311
|
+
const leftIsLiteral = binOp.vLeftExpression instanceof solc_typed_ast_1.Literal;
|
|
312
|
+
const rightIsLiteral = binOp.vRightExpression instanceof solc_typed_ast_1.Literal;
|
|
313
|
+
// Check if right side is an enum value (MemberAccess with type reference)
|
|
314
|
+
const rightIsEnum = binOp.vRightExpression instanceof solc_typed_ast_1.MemberAccess &&
|
|
315
|
+
binOp.vRightExpression.vExpression instanceof solc_typed_ast_1.Identifier &&
|
|
316
|
+
/^[A-Z]/.test(binOp.vRightExpression.vExpression.name); // Enum types typically start with uppercase
|
|
317
|
+
const leftIsEnum = binOp.vLeftExpression instanceof solc_typed_ast_1.MemberAccess &&
|
|
318
|
+
binOp.vLeftExpression.vExpression instanceof solc_typed_ast_1.Identifier &&
|
|
319
|
+
/^[A-Z]/.test(binOp.vLeftExpression.vExpression.name);
|
|
320
|
+
if (leftIsTrivial && (rightIsLiteral || rightIsEnum)) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
// Also skip the reverse: "5 == x" or "ProposalState.Approved == p.state"
|
|
324
|
+
if (rightIsTrivial && (leftIsLiteral || leftIsEnum)) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const exprStr = getExpressionString(binOp);
|
|
328
|
+
// Extract variables from both sides
|
|
329
|
+
const leftVars = extractVariables(binOp.vLeftExpression);
|
|
330
|
+
const rightVars = extractVariables(binOp.vRightExpression);
|
|
331
|
+
const allVars = [...leftVars, ...rightVars];
|
|
332
|
+
// Only solve if there's exactly one unique variable
|
|
333
|
+
const uniqueVars = new Map();
|
|
334
|
+
for (const v of allVars) {
|
|
335
|
+
// Skip if the "variable" is actually a literal-like number
|
|
336
|
+
if (/^\d+$/.test(v.name))
|
|
337
|
+
continue;
|
|
338
|
+
uniqueVars.set(v.name, { type: v.type, node: v.node });
|
|
339
|
+
}
|
|
340
|
+
if (uniqueVars.size !== 1) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const [varName, varInfo] = [...uniqueVars.entries()][0];
|
|
344
|
+
const bitSize = getBitSize(varInfo.type);
|
|
345
|
+
const signed = isSigned(varInfo.type);
|
|
346
|
+
const varMap = new Map();
|
|
347
|
+
const z3Var = ctx.BitVec.const(varName, bitSize);
|
|
348
|
+
varMap.set(varName, z3Var);
|
|
349
|
+
const constraint = createZ3Constraint(ctx, binOp, varMap, bitSize, signed);
|
|
350
|
+
if (!constraint) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const result = await solveConstraint(ctx, constraint, varMap, bitSize, signed);
|
|
354
|
+
if (!result) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
for (const [name, value] of result) {
|
|
358
|
+
solutions.push({
|
|
359
|
+
variable: name,
|
|
360
|
+
value: value.toString(),
|
|
361
|
+
type: varInfo.type.replace(/\s+/g, ''),
|
|
362
|
+
expression: exprStr
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
// Z3 errors are non-fatal
|
|
369
|
+
if (!silentMode) {
|
|
370
|
+
console.warn(`[z3] Warning: ${e}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return solutions;
|
|
374
|
+
};
|
|
375
|
+
exports.findZ3Solutions = findZ3Solutions;
|
|
376
|
+
const getExpressionString = (expr) => {
|
|
377
|
+
if (expr instanceof solc_typed_ast_1.Literal) {
|
|
378
|
+
return expr.value || '0';
|
|
379
|
+
}
|
|
380
|
+
if (expr instanceof solc_typed_ast_1.Identifier) {
|
|
381
|
+
return expr.name;
|
|
382
|
+
}
|
|
383
|
+
if (expr instanceof solc_typed_ast_1.IndexAccess) {
|
|
384
|
+
const base = getExpressionString(expr.vBaseExpression);
|
|
385
|
+
const index = expr.vIndexExpression ? getExpressionString(expr.vIndexExpression) : '0';
|
|
386
|
+
return `${base}[${index}]`;
|
|
387
|
+
}
|
|
388
|
+
if (expr instanceof solc_typed_ast_1.MemberAccess) {
|
|
389
|
+
const base = getExpressionString(expr.vExpression);
|
|
390
|
+
return `${base}.${expr.memberName}`;
|
|
391
|
+
}
|
|
392
|
+
if (expr instanceof solc_typed_ast_1.FunctionCall) {
|
|
393
|
+
const fn = getExpressionString(expr.vExpression);
|
|
394
|
+
const args = expr.vArguments.map(a => getExpressionString(a)).join(', ');
|
|
395
|
+
return `${fn}(${args})`;
|
|
396
|
+
}
|
|
397
|
+
if (expr instanceof solc_typed_ast_1.UnaryOperation) {
|
|
398
|
+
const sub = getExpressionString(expr.vSubExpression);
|
|
399
|
+
if (expr.prefix) {
|
|
400
|
+
return `${expr.operator}${sub}`;
|
|
401
|
+
}
|
|
402
|
+
return `${sub}${expr.operator}`;
|
|
403
|
+
}
|
|
404
|
+
if (expr instanceof solc_typed_ast_1.BinaryOperation) {
|
|
405
|
+
const left = getExpressionString(expr.vLeftExpression);
|
|
406
|
+
const right = getExpressionString(expr.vRightExpression);
|
|
407
|
+
return `(${left} ${expr.operator} ${right})`;
|
|
408
|
+
}
|
|
409
|
+
if (expr instanceof solc_typed_ast_1.TupleExpression) {
|
|
410
|
+
// TupleExpression with single component is just parentheses
|
|
411
|
+
if (expr.vComponents.length === 1 && expr.vComponents[0]) {
|
|
412
|
+
return getExpressionString(expr.vComponents[0]);
|
|
413
|
+
}
|
|
414
|
+
// Multi-component tuple
|
|
415
|
+
return `(${expr.vComponents.map(c => c ? getExpressionString(c) : '').join(', ')})`;
|
|
416
|
+
}
|
|
417
|
+
return '?';
|
|
418
|
+
};
|
|
419
|
+
const cleanupZ3 = async () => {
|
|
420
|
+
if (z3Context) {
|
|
421
|
+
// Z3 context cleanup if needed
|
|
422
|
+
z3Context = null;
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
exports.cleanupZ3 = cleanupZ3;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recon-generate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "CLI to scaffold Recon fuzzing suite inside Foundry projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"handlebars": "^4.7.8",
|
|
27
27
|
"solc-typed-ast": "^19.1.0",
|
|
28
28
|
"src-location": "^1.1.0",
|
|
29
|
-
"yaml": "^2.8.2"
|
|
29
|
+
"yaml": "^2.8.2",
|
|
30
|
+
"z3-solver": "^4.15.4"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@types/node": "^24.0.4",
|