recon-generate 0.0.10 → 0.0.11
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/generator.d.ts +2 -0
- package/dist/generator.js +71 -0
- package/dist/index.js +55 -1
- package/dist/pathsGenerator.d.ts +5 -1
- package/dist/pathsGenerator.js +62 -11
- package/dist/templates/setup.js +4 -3
- package/dist/templates/wake/before_after.d.ts +1 -0
- package/dist/templates/wake/before_after.js +33 -0
- package/dist/templates/wake/conftest.d.ts +1 -0
- package/dist/templates/wake/conftest.js +18 -0
- package/dist/templates/wake/crytic_tester.d.ts +1 -0
- package/dist/templates/wake/crytic_tester.js +20 -0
- package/dist/templates/wake/flows.d.ts +1 -0
- package/dist/templates/wake/flows.js +18 -0
- package/dist/templates/wake/fuzz-test.d.ts +1 -0
- package/dist/templates/wake/fuzz-test.js +62 -0
- package/dist/templates/wake/handlebars_helpers.d.ts +2 -0
- package/dist/templates/wake/handlebars_helpers.js +84 -0
- package/dist/templates/wake/helpers/actor_manager.d.ts +1 -0
- package/dist/templates/wake/helpers/actor_manager.js +44 -0
- package/dist/templates/wake/helpers/asset_manager.d.ts +1 -0
- package/dist/templates/wake/helpers/asset_manager.js +44 -0
- package/dist/templates/wake/helpers/utils.d.ts +1 -0
- package/dist/templates/wake/helpers/utils.js +11 -0
- package/dist/templates/wake/helpers.d.ts +2 -0
- package/dist/templates/wake/helpers.js +81 -0
- package/dist/templates/wake/index.d.ts +10 -0
- package/dist/templates/wake/index.js +26 -0
- package/dist/templates/wake/invariants.d.ts +1 -0
- package/dist/templates/wake/invariants.js +21 -0
- package/dist/templates/wake/properties.d.ts +1 -0
- package/dist/templates/wake/properties.js +19 -0
- package/dist/templates/wake/setup.d.ts +1 -0
- package/dist/templates/wake/setup.js +48 -0
- package/dist/templates/wake/target_functions.d.ts +1 -0
- package/dist/templates/wake/target_functions.js +24 -0
- package/dist/templates/wake/targets/contract_targets.d.ts +1 -0
- package/dist/templates/wake/targets/contract_targets.js +23 -0
- package/dist/templates/wake/targets/managers_targets.d.ts +1 -0
- package/dist/templates/wake/targets/managers_targets.js +26 -0
- package/dist/templates/wake/test_fuzz.d.ts +1 -0
- package/dist/templates/wake/test_fuzz.js +27 -0
- package/dist/wakeGenerator.d.ts +32 -0
- package/dist/wakeGenerator.js +447 -0
- package/package.json +1 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WakeGenerator = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs/promises"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const types_1 = require("./types");
|
|
41
|
+
const utils_1 = require("./utils");
|
|
42
|
+
const wakeTemplates = __importStar(require("./templates/wake"));
|
|
43
|
+
const case_1 = require("case");
|
|
44
|
+
// Embed the Python introspection script
|
|
45
|
+
const INSPECT_SCRIPT = `
|
|
46
|
+
import inspect
|
|
47
|
+
import json
|
|
48
|
+
import sys
|
|
49
|
+
import os
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
import importlib.util
|
|
52
|
+
|
|
53
|
+
# Ensure we can import from current directory
|
|
54
|
+
sys.path.insert(0, os.getcwd())
|
|
55
|
+
|
|
56
|
+
def format_annotation(annotation):
|
|
57
|
+
if annotation == inspect.Parameter.empty:
|
|
58
|
+
return "Any"
|
|
59
|
+
|
|
60
|
+
# Get string representation
|
|
61
|
+
s = str(annotation)
|
|
62
|
+
|
|
63
|
+
# Simplify Union and Optional for Wake generators
|
|
64
|
+
# We recursively flatten until we find a concrete type
|
|
65
|
+
# This prevents the TypeError: issubclass() arg 1 must be a class in Wake generators
|
|
66
|
+
import re
|
|
67
|
+
|
|
68
|
+
def simplify(type_str):
|
|
69
|
+
while "Union" in type_str or "Optional" in type_str:
|
|
70
|
+
# Find the innermost Union[...] or Optional[...]
|
|
71
|
+
m = re.search(r"(Union|Optional)\[([^\[\]]+)\]", type_str)
|
|
72
|
+
if not m: break
|
|
73
|
+
content = m.group(2)
|
|
74
|
+
parts = [p.strip() for p in content.split(",")]
|
|
75
|
+
# Pick first non-None
|
|
76
|
+
best = parts[0]
|
|
77
|
+
for p in parts:
|
|
78
|
+
if p not in ("None", "NoneType"):
|
|
79
|
+
best = p
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
# Special case: if Address or Account is present, use Address for Wake compatibility
|
|
83
|
+
if any("Address" in p or "Account" in p for p in parts):
|
|
84
|
+
best = "Address"
|
|
85
|
+
|
|
86
|
+
type_str = type_str[:m.start()] + best + type_str[m.end():]
|
|
87
|
+
return type_str
|
|
88
|
+
|
|
89
|
+
s = simplify(s)
|
|
90
|
+
s = s.replace("typing.", "").replace("wake.testing.core.", "")
|
|
91
|
+
s = s.replace("<class '", "").replace("'>", "")
|
|
92
|
+
return s
|
|
93
|
+
|
|
94
|
+
def inspect_pytypes():
|
|
95
|
+
contracts = []
|
|
96
|
+
mock_erc20 = None
|
|
97
|
+
target_dir = Path("pytypes")
|
|
98
|
+
|
|
99
|
+
if not target_dir.exists():
|
|
100
|
+
return { "contracts": [], "mock_erc20_module": None }
|
|
101
|
+
|
|
102
|
+
for root, dirs, files in os.walk(target_dir):
|
|
103
|
+
for file in files:
|
|
104
|
+
if file.endswith(".py") and file != "__init__.py":
|
|
105
|
+
rel_path = Path(root) / file
|
|
106
|
+
module_path = str(rel_path).replace(os.sep, ".").replace(".py", "")
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
module = importlib.import_module(module_path)
|
|
110
|
+
|
|
111
|
+
for name, obj in inspect.getmembers(module):
|
|
112
|
+
if inspect.isclass(obj) and hasattr(obj, "deploy") and obj.__module__ == module_path:
|
|
113
|
+
# Check for MockERC20 globally
|
|
114
|
+
if name == "MockERC20":
|
|
115
|
+
mock_erc20 = module_path
|
|
116
|
+
|
|
117
|
+
# STRICT FILTER: Only include contracts from src folder
|
|
118
|
+
if not module_path.startswith("pytypes.src"):
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
# Determine mutable functions from _abi
|
|
122
|
+
mutable_functions = set()
|
|
123
|
+
if hasattr(obj, "_abi"):
|
|
124
|
+
try:
|
|
125
|
+
# _abi is dict: selector -> item
|
|
126
|
+
for item in obj._abi.values():
|
|
127
|
+
if item.get("type") == "function" and item.get("stateMutability") not in ["view", "pure"]:
|
|
128
|
+
mutable_functions.add(item.get("name"))
|
|
129
|
+
except Exception:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
contract_info = {
|
|
133
|
+
"name": name,
|
|
134
|
+
"module": module_path,
|
|
135
|
+
"methods": [],
|
|
136
|
+
"deploy_args": []
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
deploy_method = getattr(obj, "deploy")
|
|
141
|
+
sig = inspect.signature(deploy_method)
|
|
142
|
+
for param_name, param in sig.parameters.items():
|
|
143
|
+
if param_name in ["cls", "return_tx", "request_type", "chain", "from_", "value", "gas_limit", "gas_price", "max_fee_per_gas", "max_priority_fee_per_gas", "access_list", "type", "block", "confirmations", "to"]:
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
contract_info["deploy_args"].append({
|
|
147
|
+
"name": param_name,
|
|
148
|
+
"type": format_annotation(param.annotation),
|
|
149
|
+
"default": str(param.default) if param.default != inspect.Parameter.empty else None
|
|
150
|
+
})
|
|
151
|
+
except Exception:
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
for method_name, method in inspect.getmembers(obj):
|
|
155
|
+
if method_name.startswith("_") or method_name in ["deploy", "address", "balance", "code", "chain"]:
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
if not inspect.isfunction(method):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
# Filter: Must be mutable
|
|
162
|
+
if method_name not in mutable_functions:
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
sig = inspect.signature(method)
|
|
167
|
+
args = []
|
|
168
|
+
for param_name, param in sig.parameters.items():
|
|
169
|
+
if param_name == "self": continue
|
|
170
|
+
if param_name in ["from_", "value", "gas_limit", "gas_price", "max_fee_per_gas", "max_priority_fee_per_gas", "access_list", "type", "block", "confirmations", "return_tx", "request_type", "to"]:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
args.append({
|
|
174
|
+
"name": param_name,
|
|
175
|
+
"type": format_annotation(param.annotation)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
contract_info["methods"].append({
|
|
179
|
+
"name": method_name,
|
|
180
|
+
"args": args
|
|
181
|
+
})
|
|
182
|
+
except Exception:
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
contracts.append(contract_info)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
return { "contracts": contracts, "mock_erc20_module": mock_erc20 }
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
print(json.dumps(inspect_pytypes(), indent=2))
|
|
193
|
+
`;
|
|
194
|
+
class WakeGenerator {
|
|
195
|
+
constructor(foundryRoot, options) {
|
|
196
|
+
this.foundryRoot = foundryRoot;
|
|
197
|
+
this.options = options;
|
|
198
|
+
}
|
|
199
|
+
logDebug(message, obj) {
|
|
200
|
+
if (!this.options.debug)
|
|
201
|
+
return;
|
|
202
|
+
if (obj !== undefined) {
|
|
203
|
+
console.info(`[recon-generate wake] ${message}`, obj);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.info(`[recon-generate wake] ${message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
runCmd(cmd, cwd) {
|
|
210
|
+
return new Promise((resolve, reject) => {
|
|
211
|
+
(0, child_process_1.exec)(cmd, { cwd, env: { ...process.env, PATH: (0, utils_1.getEnvPath)() } }, (err, stdout, stderr) => {
|
|
212
|
+
if (err) {
|
|
213
|
+
reject(new Error(stderr || err.message));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
resolve(stdout);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async ensureBuild() {
|
|
222
|
+
const outDir = path.join(this.foundryRoot, '.recon', 'out');
|
|
223
|
+
if (await (0, utils_1.fileExists)(outDir) && !this.options.forceBuild) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
console.log('Building project...');
|
|
227
|
+
await fs.mkdir(path.join(this.foundryRoot, '.recon'), { recursive: true });
|
|
228
|
+
const skipPaths = [
|
|
229
|
+
'test',
|
|
230
|
+
'tests',
|
|
231
|
+
'script',
|
|
232
|
+
'scripts',
|
|
233
|
+
'contracts/test',
|
|
234
|
+
'contracts/tests',
|
|
235
|
+
'lib/**/test/**',
|
|
236
|
+
'lib/**/tests/**'
|
|
237
|
+
];
|
|
238
|
+
const skipArg = skipPaths.map(p => `--skip ${p}`).join(' ');
|
|
239
|
+
const cmd = `forge build --build-info ${skipArg} --out .recon/out`.replace(/\s+/g, ' ').trim();
|
|
240
|
+
await this.runCmd(cmd, this.foundryRoot);
|
|
241
|
+
}
|
|
242
|
+
async runWakeUp() {
|
|
243
|
+
console.log('Running wake up to generate pytypes...');
|
|
244
|
+
try {
|
|
245
|
+
await this.runCmd('wake up', this.foundryRoot);
|
|
246
|
+
console.log('✓ pytypes generated');
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
console.warn('⚠ wake up failed - ensure wake is installed and configured:', e);
|
|
250
|
+
throw e;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async copyBuiltins() {
|
|
254
|
+
const reconPytypesDir = path.join(this.foundryRoot, 'pytypes', 'recon');
|
|
255
|
+
await fs.mkdir(reconPytypesDir, { recursive: true });
|
|
256
|
+
// locate builtins directory relative to dist/
|
|
257
|
+
const candidates = [
|
|
258
|
+
path.resolve(__dirname, '..', 'src', 'templates', 'wake', 'builtins'),
|
|
259
|
+
path.resolve(__dirname, '../../src/templates/wake/builtins'),
|
|
260
|
+
path.resolve(__dirname, 'templates/wake/builtins')
|
|
261
|
+
];
|
|
262
|
+
let builtinsDir;
|
|
263
|
+
for (const p of candidates) {
|
|
264
|
+
if (await (0, utils_1.fileExists)(p)) {
|
|
265
|
+
builtinsDir = p;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!builtinsDir) {
|
|
270
|
+
console.warn("⚠ Could not find builtin templates directory. AssetManager might fail.");
|
|
271
|
+
console.warn("Checked paths:", candidates);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await fs.writeFile(path.join(reconPytypesDir, '__init__.py'), '');
|
|
275
|
+
const files = await fs.readdir(builtinsDir);
|
|
276
|
+
for (const file of files) {
|
|
277
|
+
if (file.endsWith('.py')) {
|
|
278
|
+
const content = await fs.readFile(path.join(builtinsDir, file), 'utf8');
|
|
279
|
+
await fs.writeFile(path.join(reconPytypesDir, file), content);
|
|
280
|
+
console.log(`✓ Copied builtin ${file} to ${path.join(reconPytypesDir, file)}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async inspectPytypes() {
|
|
285
|
+
const tempScript = path.join(this.foundryRoot, '.recon', 'inspect_wake.py');
|
|
286
|
+
await fs.mkdir(path.dirname(tempScript), { recursive: true });
|
|
287
|
+
await fs.writeFile(tempScript, INSPECT_SCRIPT);
|
|
288
|
+
try {
|
|
289
|
+
const output = await this.runCmd(`python3 ${tempScript}`, this.foundryRoot);
|
|
290
|
+
return JSON.parse(output);
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
// Fallback to python
|
|
294
|
+
try {
|
|
295
|
+
const output = await this.runCmd(`python ${tempScript}`, this.foundryRoot);
|
|
296
|
+
return JSON.parse(output);
|
|
297
|
+
}
|
|
298
|
+
catch (e2) {
|
|
299
|
+
throw new Error(`Failed to run introspection script: ${String(e2)}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
matchesSignatureOrName(signature, set) {
|
|
304
|
+
if (!set || set.size === 0) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
const nameOnly = signature.split('(')[0];
|
|
308
|
+
if (set.has(signature) || set.has(nameOnly)) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
isContractAllowed(name) {
|
|
314
|
+
const { include, exclude } = this.options;
|
|
315
|
+
if (include && (include.contractOnly.size > 0 || include.functions.size > 0)) {
|
|
316
|
+
const inInclude = include.contractOnly.has(name) || include.functions.has(name);
|
|
317
|
+
if (!inInclude)
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
if (exclude && exclude.contractOnly.has(name)) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
isFunctionIncluded(contractName, method) {
|
|
326
|
+
const sig = `${method.name}(${method.args.map(a => a.type).join(',')})`;
|
|
327
|
+
const { include, exclude } = this.options;
|
|
328
|
+
if (include && include.functions.size > 0) {
|
|
329
|
+
const fnSet = include.functions.get(contractName);
|
|
330
|
+
if (fnSet) {
|
|
331
|
+
if (!this.matchesSignatureOrName(sig, fnSet)) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (exclude && exclude.functions.size > 0) {
|
|
337
|
+
const fnSet = exclude.functions.get(contractName);
|
|
338
|
+
if (fnSet && this.matchesSignatureOrName(sig, fnSet)) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
async run() {
|
|
345
|
+
if (!this.options.force && await (0, utils_1.fileExists)(this.options.suiteDir)) {
|
|
346
|
+
throw new Error(`${this.options.suiteDir} exists. Use --force.`);
|
|
347
|
+
}
|
|
348
|
+
await this.ensureBuild();
|
|
349
|
+
try {
|
|
350
|
+
await this.runWakeUp();
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
console.warn('Wake up warning (continuing):', e);
|
|
354
|
+
}
|
|
355
|
+
// Setup builtin mock
|
|
356
|
+
await this.copyBuiltins();
|
|
357
|
+
const introspection = await this.inspectPytypes();
|
|
358
|
+
const contracts = introspection.contracts;
|
|
359
|
+
this.logDebug('Discovered contracts from pytypes', contracts.map(c => c.name));
|
|
360
|
+
const filteredContracts = contracts.filter(c => this.isContractAllowed(c.name));
|
|
361
|
+
const contractFlows = new Map();
|
|
362
|
+
for (const contract of filteredContracts) {
|
|
363
|
+
const flows = [];
|
|
364
|
+
for (const method of contract.methods) {
|
|
365
|
+
if (this.isFunctionIncluded(contract.name, method)) {
|
|
366
|
+
// Check admin
|
|
367
|
+
const sig = `${method.name}(${method.args.map(a => a.type).join(',')})`;
|
|
368
|
+
let actor = types_1.Actor.ACTOR;
|
|
369
|
+
if (this.options.admin && this.options.admin.has(contract.name)) {
|
|
370
|
+
const adminSet = this.options.admin.get(contract.name);
|
|
371
|
+
if (this.matchesSignatureOrName(sig, adminSet)) {
|
|
372
|
+
actor = types_1.Actor.ADMIN;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
flows.push({
|
|
376
|
+
contractName: contract.name,
|
|
377
|
+
method: method,
|
|
378
|
+
actor: actor,
|
|
379
|
+
mode: types_1.Mode.NORMAL
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (flows.length > 0) {
|
|
384
|
+
contractFlows.set(contract.name, flows);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (await (0, utils_1.fileExists)(this.options.suiteDir)) {
|
|
388
|
+
await fs.rm(this.options.suiteDir, { recursive: true, force: true });
|
|
389
|
+
}
|
|
390
|
+
await fs.mkdir(this.options.suiteDir, { recursive: true });
|
|
391
|
+
// Create directories
|
|
392
|
+
const helpersDir = path.join(this.options.suiteDir, 'helpers');
|
|
393
|
+
const targetsDir = path.join(this.options.suiteDir, 'targets');
|
|
394
|
+
await fs.mkdir(helpersDir, { recursive: true });
|
|
395
|
+
await fs.mkdir(targetsDir, { recursive: true });
|
|
396
|
+
// __init__.py files
|
|
397
|
+
await fs.writeFile(path.join(this.options.suiteDir, '__init__.py'), '');
|
|
398
|
+
await fs.writeFile(path.join(helpersDir, '__init__.py'), '');
|
|
399
|
+
await fs.writeFile(path.join(targetsDir, '__init__.py'), '');
|
|
400
|
+
// Helpers
|
|
401
|
+
await fs.writeFile(path.join(helpersDir, 'actor_manager.py'), wakeTemplates.actorManagerTemplate({}));
|
|
402
|
+
await fs.writeFile(path.join(helpersDir, 'asset_manager.py'), wakeTemplates.assetManagerTemplate({}));
|
|
403
|
+
await fs.writeFile(path.join(helpersDir, 'utils.py'), wakeTemplates.utilsTemplate({}));
|
|
404
|
+
// targets/managers_targets.py
|
|
405
|
+
await fs.writeFile(path.join(targetsDir, 'managers_targets.py'), wakeTemplates.managersTargetsTemplate({}));
|
|
406
|
+
// targets/{contract}_targets.py
|
|
407
|
+
for (const contract of filteredContracts) {
|
|
408
|
+
const flows = contractFlows.get(contract.name) || [];
|
|
409
|
+
// Even if no flows, we might want the class? Usually yes.
|
|
410
|
+
const content = wakeTemplates.contractTargetsTemplate({
|
|
411
|
+
contract,
|
|
412
|
+
flows
|
|
413
|
+
});
|
|
414
|
+
const filename = `${(0, case_1.snake)(contract.name)}_targets.py`;
|
|
415
|
+
await fs.writeFile(path.join(targetsDir, filename), content);
|
|
416
|
+
}
|
|
417
|
+
// setup.py
|
|
418
|
+
await fs.writeFile(path.join(this.options.suiteDir, 'setup.py'), wakeTemplates.setupTemplate({ contracts: filteredContracts }));
|
|
419
|
+
// target_functions.py
|
|
420
|
+
await fs.writeFile(path.join(this.options.suiteDir, 'target_functions.py'), wakeTemplates.targetFunctionsTemplate({ contracts: filteredContracts }));
|
|
421
|
+
// properties.py
|
|
422
|
+
await fs.writeFile(path.join(this.options.suiteDir, 'properties.py'), wakeTemplates.propertiesTemplate({}));
|
|
423
|
+
// before_after.py
|
|
424
|
+
await fs.writeFile(path.join(this.options.suiteDir, 'before_after.py'), wakeTemplates.beforeAfterTemplate({}));
|
|
425
|
+
// test_crytic.py
|
|
426
|
+
const testContent = wakeTemplates.cryticTesterTemplate({});
|
|
427
|
+
await fs.writeFile(path.join(this.options.suiteDir, 'test_crytic.py'), testContent);
|
|
428
|
+
console.log(`✓ Generated Wake suite at ${this.options.suiteDir}`);
|
|
429
|
+
}
|
|
430
|
+
async listAvailable() {
|
|
431
|
+
await this.ensureBuild();
|
|
432
|
+
await this.runWakeUp();
|
|
433
|
+
const introspection = await this.inspectPytypes();
|
|
434
|
+
const filtered = introspection.contracts.filter(c => this.isContractAllowed(c.name));
|
|
435
|
+
console.log('Available contracts/functions (Wake):');
|
|
436
|
+
for (const c of filtered) {
|
|
437
|
+
console.log(`- ${c.name} (${c.module})`);
|
|
438
|
+
for (const m of c.methods) {
|
|
439
|
+
const sig = `${m.name}(${m.args.map(a => a.type).join(',')})`;
|
|
440
|
+
if (this.isFunctionIncluded(c.name, m)) {
|
|
441
|
+
console.log(` • ${sig}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
exports.WakeGenerator = WakeGenerator;
|