create-stylus-ide 1.0.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.
Files changed (135) hide show
  1. package/Readme.MD +1515 -0
  2. package/cli.js +28 -0
  3. package/frontend/.vscode/settings.json +9 -0
  4. package/frontend/app/api/chat/route.ts +101 -0
  5. package/frontend/app/api/check-setup/route.ts +93 -0
  6. package/frontend/app/api/cleanup/route.ts +14 -0
  7. package/frontend/app/api/compile/route.ts +95 -0
  8. package/frontend/app/api/compile-stream/route.ts +98 -0
  9. package/frontend/app/api/complete/route.ts +86 -0
  10. package/frontend/app/api/deploy/route.ts +118 -0
  11. package/frontend/app/api/export-abi/route.ts +58 -0
  12. package/frontend/app/favicon.ico +0 -0
  13. package/frontend/app/globals.css +177 -0
  14. package/frontend/app/layout.tsx +29 -0
  15. package/frontend/app/ml/page.tsx +694 -0
  16. package/frontend/app/page.tsx +1132 -0
  17. package/frontend/app/providers.tsx +18 -0
  18. package/frontend/app/qlearning/page.tsx +188 -0
  19. package/frontend/app/raytracing/page.tsx +268 -0
  20. package/frontend/components/abi/ABIDialog.tsx +132 -0
  21. package/frontend/components/ai/AICompletionPopup.tsx +76 -0
  22. package/frontend/components/ai/ChatPanel.tsx +292 -0
  23. package/frontend/components/ai/QuickActions.tsx +128 -0
  24. package/frontend/components/blockchain/BlockchainContractBanner.tsx +64 -0
  25. package/frontend/components/blockchain/BlockchainLoadingDialog.tsx +188 -0
  26. package/frontend/components/deploy/DeployDialog.tsx +334 -0
  27. package/frontend/components/editor/FileTabs.tsx +181 -0
  28. package/frontend/components/editor/MonacoEditor.tsx +306 -0
  29. package/frontend/components/file-tree/ContextMenu.tsx +110 -0
  30. package/frontend/components/file-tree/DeleteConfirmDialog.tsx +61 -0
  31. package/frontend/components/file-tree/FileInputDialog.tsx +97 -0
  32. package/frontend/components/file-tree/FileNode.tsx +60 -0
  33. package/frontend/components/file-tree/FileTree.tsx +259 -0
  34. package/frontend/components/file-tree/FileTreeSkeleton.tsx +26 -0
  35. package/frontend/components/file-tree/FolderNode.tsx +105 -0
  36. package/frontend/components/github/GitHubLoadingDialog.tsx +201 -0
  37. package/frontend/components/github/GitHubMetadataBanner.tsx +61 -0
  38. package/frontend/components/github/LoadFromGitHubDialog.tsx +125 -0
  39. package/frontend/components/github/URLCopyButton.tsx +60 -0
  40. package/frontend/components/interact/ContractInteraction.tsx +323 -0
  41. package/frontend/components/interact/ContractPlaceholder.tsx +41 -0
  42. package/frontend/components/orbit/BenchmarkDialog.tsx +342 -0
  43. package/frontend/components/orbit/OrbitExplorer.tsx +273 -0
  44. package/frontend/components/project/ProjectActions.tsx +176 -0
  45. package/frontend/components/q-learning/ContractConfig.tsx +172 -0
  46. package/frontend/components/q-learning/MazeGrid.tsx +346 -0
  47. package/frontend/components/q-learning/PathAnimation.tsx +384 -0
  48. package/frontend/components/q-learning/QTableHeatmap.tsx +300 -0
  49. package/frontend/components/q-learning/TrainingForm.tsx +349 -0
  50. package/frontend/components/ray-tracing/ContractConfig.tsx +245 -0
  51. package/frontend/components/ray-tracing/MintingForm.tsx +280 -0
  52. package/frontend/components/ray-tracing/RenderCanvas.tsx +228 -0
  53. package/frontend/components/ray-tracing/RenderingPanel.tsx +259 -0
  54. package/frontend/components/ray-tracing/StyleControls.tsx +217 -0
  55. package/frontend/components/setup/SetupGuide.tsx +290 -0
  56. package/frontend/components/ui/KeyboardShortcutHint.tsx +74 -0
  57. package/frontend/components/ui/alert-dialog.tsx +157 -0
  58. package/frontend/components/ui/alert.tsx +66 -0
  59. package/frontend/components/ui/badge.tsx +46 -0
  60. package/frontend/components/ui/button.tsx +62 -0
  61. package/frontend/components/ui/card.tsx +92 -0
  62. package/frontend/components/ui/context-menu.tsx +252 -0
  63. package/frontend/components/ui/dialog.tsx +143 -0
  64. package/frontend/components/ui/dropdown-menu.tsx +257 -0
  65. package/frontend/components/ui/input.tsx +21 -0
  66. package/frontend/components/ui/label.tsx +24 -0
  67. package/frontend/components/ui/progress.tsx +31 -0
  68. package/frontend/components/ui/scroll-area.tsx +58 -0
  69. package/frontend/components/ui/select.tsx +190 -0
  70. package/frontend/components/ui/separator.tsx +28 -0
  71. package/frontend/components/ui/sheet.tsx +139 -0
  72. package/frontend/components/ui/skeleton.tsx +13 -0
  73. package/frontend/components/ui/slider.tsx +63 -0
  74. package/frontend/components/ui/sonner.tsx +40 -0
  75. package/frontend/components/ui/tabs.tsx +66 -0
  76. package/frontend/components/ui/textarea.tsx +18 -0
  77. package/frontend/components/wallet/ConnectButton.tsx +167 -0
  78. package/frontend/components/wallet/FaucetButton.tsx +256 -0
  79. package/frontend/components.json +22 -0
  80. package/frontend/eslint.config.mjs +18 -0
  81. package/frontend/hooks/useAICompletion.ts +75 -0
  82. package/frontend/hooks/useBlockchainLoader.ts +58 -0
  83. package/frontend/hooks/useChats.ts +137 -0
  84. package/frontend/hooks/useCompilation.ts +173 -0
  85. package/frontend/hooks/useFileTabs.ts +178 -0
  86. package/frontend/hooks/useGitHubLoader.ts +50 -0
  87. package/frontend/hooks/useKeyboardShortcuts.ts +47 -0
  88. package/frontend/hooks/usePanelState.ts +115 -0
  89. package/frontend/hooks/useProjectState.ts +276 -0
  90. package/frontend/hooks/useResponsive.ts +29 -0
  91. package/frontend/lib/abi-parser.ts +58 -0
  92. package/frontend/lib/blockchain-api.ts +374 -0
  93. package/frontend/lib/blockchain-explorers.ts +75 -0
  94. package/frontend/lib/blockchain-loader.ts +112 -0
  95. package/frontend/lib/cargo-template.ts +64 -0
  96. package/frontend/lib/compilation.ts +529 -0
  97. package/frontend/lib/constants.ts +31 -0
  98. package/frontend/lib/deployment.ts +176 -0
  99. package/frontend/lib/file-utils.ts +83 -0
  100. package/frontend/lib/github-api.ts +246 -0
  101. package/frontend/lib/github-loader.ts +369 -0
  102. package/frontend/lib/ml-contract-template.txt +900 -0
  103. package/frontend/lib/orbit-chains.ts +181 -0
  104. package/frontend/lib/output-formatter.ts +68 -0
  105. package/frontend/lib/project-manager.ts +632 -0
  106. package/frontend/lib/ray-tracing-abi.ts +206 -0
  107. package/frontend/lib/storage.ts +189 -0
  108. package/frontend/lib/templates.ts +1662 -0
  109. package/frontend/lib/url-parser.ts +188 -0
  110. package/frontend/lib/utils.ts +6 -0
  111. package/frontend/lib/wagmi-config.ts +24 -0
  112. package/frontend/next.config.ts +7 -0
  113. package/frontend/package-lock.json +16259 -0
  114. package/frontend/package.json +60 -0
  115. package/frontend/postcss.config.mjs +7 -0
  116. package/frontend/public/file.svg +1 -0
  117. package/frontend/public/globe.svg +1 -0
  118. package/frontend/public/ml-weights/.gitkeep +0 -0
  119. package/frontend/public/ml-weights/model.pkl +0 -0
  120. package/frontend/public/ml-weights/model_weights.json +27102 -0
  121. package/frontend/public/ml-weights/test_samples.json +7888 -0
  122. package/frontend/public/next.svg +1 -0
  123. package/frontend/public/vercel.svg +1 -0
  124. package/frontend/public/window.svg +1 -0
  125. package/frontend/scripts/check-env.js +52 -0
  126. package/frontend/scripts/setup.js +285 -0
  127. package/frontend/tailwind.config.ts +64 -0
  128. package/frontend/tsconfig.json +34 -0
  129. package/frontend/types/blockchain.ts +63 -0
  130. package/frontend/types/github.ts +54 -0
  131. package/frontend/types/project.ts +106 -0
  132. package/ml-training/README.md +56 -0
  133. package/ml-training/train_tiny_model.py +325 -0
  134. package/ml-training/update_template.py +59 -0
  135. package/package.json +30 -0
@@ -0,0 +1,529 @@
1
+ import { spawn } from "child_process";
2
+ import { COMPILATION_CONSTANTS, ERROR_MESSAGES } from "./constants";
3
+ import { createProjectStructure, writeProjectFiles } from "./file-utils";
4
+ import {
5
+ CARGO_TOML_TEMPLATE,
6
+ RUST_TOOLCHAIN_TOML,
7
+ MAIN_RS_TEMPLATE,
8
+ GITIGNORE_TEMPLATE,
9
+ } from "./cargo-template";
10
+ import path from "path";
11
+ import fs from "fs/promises";
12
+
13
+ export interface CompilationOutput {
14
+ type: "stdout" | "stderr" | "error" | "complete" | "result";
15
+ data: string;
16
+ timestamp?: number;
17
+ }
18
+
19
+ export interface CompilationResult {
20
+ success: boolean;
21
+ exitCode: number;
22
+ output: CompilationOutput[];
23
+ wasmSize?: number;
24
+ error?: string;
25
+ }
26
+
27
+ // NEW: Project file interface
28
+ export interface ProjectFile {
29
+ path: string;
30
+ content: string;
31
+ }
32
+
33
+ function stripAnsi(input: string) {
34
+ return input.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
35
+ }
36
+
37
+ // Install WASM target for the pinned toolchain
38
+ async function installWasmTarget(projectPath: string): Promise<boolean> {
39
+ return new Promise((resolve) => {
40
+ const args = [
41
+ "target",
42
+ "add",
43
+ "wasm32-unknown-unknown",
44
+ "--toolchain",
45
+ COMPILATION_CONSTANTS.RUST_TOOLCHAIN_CHANNEL,
46
+ ];
47
+
48
+ const proc = spawn("rustup", args, {
49
+ cwd: projectPath,
50
+ shell: false,
51
+ env: process.env,
52
+ });
53
+
54
+ proc.on("close", (code) => resolve(code === 0));
55
+ proc.on("error", () => resolve(false));
56
+ });
57
+ }
58
+
59
+ // Generate Cargo.lock file
60
+ async function generateLockfile(projectPath: string): Promise<boolean> {
61
+ return new Promise((resolve) => {
62
+ const proc = spawn("cargo", ["generate-lockfile"], {
63
+ cwd: projectPath,
64
+ shell: false,
65
+ env: process.env,
66
+ });
67
+
68
+ proc.on("close", (code) => resolve(code === 0));
69
+ proc.on("error", () => resolve(false));
70
+ });
71
+ }
72
+
73
+ // Get WASM file size
74
+ async function getWasmSize(projectPath: string): Promise<number | null> {
75
+ try {
76
+ const wasmPath = path.join(
77
+ projectPath,
78
+ "target",
79
+ "wasm32-unknown-unknown",
80
+ "release",
81
+ "stylus_hello_world.wasm"
82
+ );
83
+ const stats = await fs.stat(wasmPath);
84
+ return stats.size;
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ // NEW: Write multiple project files
91
+ async function writeMultipleProjectFiles(
92
+ projectPath: string,
93
+ projectFiles: ProjectFile[]
94
+ ): Promise<void> {
95
+ for (const file of projectFiles) {
96
+ const filePath = path.join(projectPath, file.path);
97
+
98
+ // Create directory if needed
99
+ const fileDir = path.dirname(filePath);
100
+ await fs.mkdir(fileDir, { recursive: true });
101
+
102
+ // Write file
103
+ await fs.writeFile(filePath, file.content, "utf-8");
104
+ }
105
+ }
106
+
107
+ // NEW: Check if files include Cargo.toml
108
+ function hasCargoToml(files: ProjectFile[]): boolean {
109
+ return files.some(
110
+ (f) => f.path === "Cargo.toml" || f.path === "./Cargo.toml"
111
+ );
112
+ }
113
+
114
+ // NEW: Check if files include rust-toolchain.toml
115
+ function hasRustToolchain(files: ProjectFile[]): boolean {
116
+ return files.some(
117
+ (f) =>
118
+ f.path === "rust-toolchain.toml" || f.path === "./rust-toolchain.toml"
119
+ );
120
+ }
121
+
122
+ export async function runCargoStylusCheck(
123
+ projectPath: string,
124
+ code: string,
125
+ onOutput?: (output: CompilationOutput) => void,
126
+ projectFiles?: ProjectFile[] // NEW: Optional multi-file support
127
+ ): Promise<CompilationResult> {
128
+ try {
129
+ // STEP 1: Create project structure
130
+ const { srcPath } = await createProjectStructure(projectPath);
131
+
132
+ // STEP 2: Write project files
133
+ if (projectFiles && projectFiles.length > 0) {
134
+ // NEW: Multi-file mode
135
+ await writeMultipleProjectFiles(projectPath, projectFiles);
136
+
137
+ // Add default files if not provided
138
+ if (!hasCargoToml(projectFiles)) {
139
+ await fs.writeFile(
140
+ path.join(projectPath, "Cargo.toml"),
141
+ CARGO_TOML_TEMPLATE,
142
+ "utf-8"
143
+ );
144
+ }
145
+
146
+ if (!hasRustToolchain(projectFiles)) {
147
+ await fs.writeFile(
148
+ path.join(projectPath, "rust-toolchain.toml"),
149
+ RUST_TOOLCHAIN_TOML,
150
+ "utf-8"
151
+ );
152
+ }
153
+
154
+ // Add .gitignore if not provided
155
+ const hasGitignore = projectFiles.some(
156
+ (f) => f.path === ".gitignore" || f.path === "./.gitignore"
157
+ );
158
+ if (!hasGitignore) {
159
+ await fs.writeFile(
160
+ path.join(projectPath, ".gitignore"),
161
+ GITIGNORE_TEMPLATE,
162
+ "utf-8"
163
+ );
164
+ }
165
+
166
+ // Add main.rs if not provided
167
+ const hasMainRs = projectFiles.some(
168
+ (f) => f.path === "src/main.rs" || f.path === "./src/main.rs"
169
+ );
170
+ if (!hasMainRs) {
171
+ await fs.writeFile(
172
+ path.join(srcPath, "main.rs"),
173
+ MAIN_RS_TEMPLATE,
174
+ "utf-8"
175
+ );
176
+ }
177
+
178
+ // Send output message about file count
179
+ const fileCountMsg: CompilationOutput = {
180
+ type: "stdout",
181
+ data: `📁 Compiling project with ${projectFiles.length} file(s)`,
182
+ timestamp: 0,
183
+ };
184
+ onOutput?.(fileCountMsg);
185
+ } else {
186
+ // Legacy single-file mode (backward compatible)
187
+ await writeProjectFiles(
188
+ projectPath,
189
+ srcPath,
190
+ code,
191
+ CARGO_TOML_TEMPLATE,
192
+ RUST_TOOLCHAIN_TOML,
193
+ MAIN_RS_TEMPLATE,
194
+ GITIGNORE_TEMPLATE
195
+ );
196
+ }
197
+
198
+ // STEP 3: Ensure wasm target exists for the pinned toolchain
199
+ const wasmInstalled = await installWasmTarget(projectPath);
200
+ if (!wasmInstalled) {
201
+ return {
202
+ success: false,
203
+ exitCode: -1,
204
+ output: [{ type: "error", data: ERROR_MESSAGES.WASM_TARGET_MISSING }],
205
+ error: ERROR_MESSAGES.WASM_TARGET_MISSING,
206
+ };
207
+ }
208
+
209
+ // STEP 4: Generate Cargo.lock (helps reproducibility)
210
+ const lockfileGenerated = await generateLockfile(projectPath);
211
+ if (!lockfileGenerated) {
212
+ return {
213
+ success: false,
214
+ exitCode: -1,
215
+ output: [{ type: "error", data: "Failed to generate Cargo.lock file" }],
216
+ error: "Failed to generate Cargo.lock file",
217
+ };
218
+ }
219
+
220
+ // STEP 5: Run cargo build (LOCAL COMPILATION - NO RPC NEEDED)
221
+ return await new Promise((resolve) => {
222
+ const output: CompilationOutput[] = [];
223
+ const startTime = Date.now();
224
+
225
+ const proc = spawn("cargo", COMPILATION_CONSTANTS.COMMANDS.BUILD, {
226
+ cwd: projectPath,
227
+ shell: false,
228
+ env: {
229
+ ...process.env,
230
+ CARGO_TERM_COLOR: "always",
231
+ CARGO_TARGET_DIR: `${projectPath}/target`,
232
+ },
233
+ });
234
+
235
+ const timeout = setTimeout(() => {
236
+ proc.kill();
237
+ const msg: CompilationOutput = {
238
+ type: "error",
239
+ data: ERROR_MESSAGES.TIMEOUT,
240
+ timestamp: Date.now() - startTime,
241
+ };
242
+ output.push(msg);
243
+ onOutput?.(msg);
244
+
245
+ resolve({
246
+ success: false,
247
+ exitCode: -1,
248
+ output,
249
+ error: ERROR_MESSAGES.TIMEOUT,
250
+ });
251
+ }, COMPILATION_CONSTANTS.COMPILE_TIMEOUT);
252
+
253
+ proc.stdout.on("data", (data) => {
254
+ const msg: CompilationOutput = {
255
+ type: "stdout",
256
+ data: data.toString(),
257
+ timestamp: Date.now() - startTime,
258
+ };
259
+ output.push(msg);
260
+ onOutput?.(msg);
261
+ });
262
+
263
+ proc.stderr.on("data", (data) => {
264
+ const msg: CompilationOutput = {
265
+ type: "stderr",
266
+ data: data.toString(),
267
+ timestamp: Date.now() - startTime,
268
+ };
269
+ output.push(msg);
270
+ onOutput?.(msg);
271
+ });
272
+
273
+ proc.on("error", (error) => {
274
+ clearTimeout(timeout);
275
+ const msg: CompilationOutput = {
276
+ type: "error",
277
+ data: error.message,
278
+ timestamp: Date.now() - startTime,
279
+ };
280
+ output.push(msg);
281
+ onOutput?.(msg);
282
+
283
+ resolve({
284
+ success: false,
285
+ exitCode: -1,
286
+ output,
287
+ error: error.message,
288
+ });
289
+ });
290
+
291
+ proc.on("close", async (code) => {
292
+ clearTimeout(timeout);
293
+
294
+ // Get WASM size if compilation succeeded
295
+ let wasmSize: number | undefined;
296
+ if (code === 0) {
297
+ const size = await getWasmSize(projectPath);
298
+ if (size !== null) {
299
+ wasmSize = size;
300
+ const sizeMB = (size / 1024).toFixed(2);
301
+ const maxMB = (COMPILATION_CONSTANTS.MAX_WASM_SIZE / 1024).toFixed(
302
+ 2
303
+ );
304
+
305
+ // Add size info to output
306
+ const sizeMsg: CompilationOutput = {
307
+ type:
308
+ size > COMPILATION_CONSTANTS.MAX_WASM_SIZE ? "error" : "stdout",
309
+ data:
310
+ size > COMPILATION_CONSTANTS.MAX_WASM_SIZE
311
+ ? `⚠️ WASM size: ${sizeMB} KB (exceeds ${maxMB} KB limit)`
312
+ : `✓ WASM size: ${sizeMB} KB (within ${maxMB} KB limit)`,
313
+ timestamp: Date.now() - startTime,
314
+ };
315
+ output.push(sizeMsg);
316
+ onOutput?.(sizeMsg);
317
+ }
318
+ }
319
+
320
+ const msg: CompilationOutput = {
321
+ type: "complete",
322
+ data:
323
+ code === 0 ? "✓ Compilation successful" : "✗ Compilation failed",
324
+ timestamp: Date.now() - startTime,
325
+ };
326
+ output.push(msg);
327
+ onOutput?.(msg);
328
+
329
+ resolve({
330
+ success: code === 0,
331
+ exitCode: code ?? -1,
332
+ output,
333
+ wasmSize,
334
+ });
335
+ });
336
+ });
337
+ } catch (error) {
338
+ return {
339
+ success: false,
340
+ exitCode: -1,
341
+ output: [
342
+ {
343
+ type: "error",
344
+ data: error instanceof Error ? error.message : "Unknown error",
345
+ },
346
+ ],
347
+ error: error instanceof Error ? error.message : "Unknown error",
348
+ };
349
+ }
350
+ }
351
+
352
+ export function parseCompilationErrors(stderr: string): {
353
+ line: number;
354
+ column: number;
355
+ message: string;
356
+ }[] {
357
+ const errors: { line: number; column: number; message: string }[] = [];
358
+ const clean = stripAnsi(stderr);
359
+
360
+ // Matches:
361
+ // error[E0412]: message...
362
+ // --> src/lib.rs:16:31
363
+ const re =
364
+ /error(?:\[[A-Z0-9]+\])?:\s+(.+?)\n(?:.|\n)*?-->\s+src[\\/]+lib\.rs:(\d+):(\d+)/g;
365
+
366
+ let m: RegExpExecArray | null;
367
+ while ((m = re.exec(clean)) !== null) {
368
+ errors.push({
369
+ message: m[1].trim(),
370
+ line: parseInt(m[2], 10),
371
+ column: parseInt(m[3], 10),
372
+ });
373
+ }
374
+
375
+ return errors;
376
+ }
377
+
378
+ type RunResult = {
379
+ code: number;
380
+ stdout: string;
381
+ stderr: string;
382
+ timedOut: boolean;
383
+ };
384
+
385
+ function extractFromFirstInterface(text: string): string | null {
386
+ const idx = text.indexOf("interface ");
387
+ if (idx === -1) return null;
388
+ return text.slice(idx).trim();
389
+ }
390
+
391
+ function extractJsonArray(text: string): string | null {
392
+ const start = text.indexOf("[");
393
+ const end = text.lastIndexOf("]");
394
+ if (start === -1 || end === -1 || end <= start) return null;
395
+ return text.slice(start, end + 1);
396
+ }
397
+
398
+ function runCargo(
399
+ cwd: string,
400
+ args: string[],
401
+ timeoutMs: number,
402
+ onOutput?: (o: CompilationOutput) => void
403
+ ): Promise<RunResult> {
404
+ return new Promise((resolve) => {
405
+ const start = Date.now();
406
+ const proc = spawn("cargo", args, {
407
+ cwd,
408
+ shell: false,
409
+ env: {
410
+ ...process.env,
411
+ // ✅ don't pollute parsing with colors
412
+ CARGO_TERM_COLOR: "never",
413
+ },
414
+ });
415
+
416
+ let stdout = "";
417
+ let stderr = "";
418
+ let timedOut = false;
419
+
420
+ const timeout = setTimeout(() => {
421
+ timedOut = true;
422
+ try {
423
+ proc.kill("SIGKILL");
424
+ } catch {}
425
+ }, timeoutMs);
426
+
427
+ proc.stdout.on("data", (d) => {
428
+ const s = d.toString();
429
+ stdout += s;
430
+ onOutput?.({ type: "stdout", data: s, timestamp: Date.now() - start });
431
+ });
432
+
433
+ proc.stderr.on("data", (d) => {
434
+ const s = d.toString();
435
+ stderr += s;
436
+ onOutput?.({ type: "stderr", data: s, timestamp: Date.now() - start });
437
+ });
438
+
439
+ proc.on("error", (e) => {
440
+ clearTimeout(timeout);
441
+ resolve({
442
+ code: -1,
443
+ stdout,
444
+ stderr: stderr + `\n${e.message}`,
445
+ timedOut,
446
+ });
447
+ });
448
+
449
+ proc.on("close", (code) => {
450
+ clearTimeout(timeout);
451
+ resolve({ code: code ?? -1, stdout, stderr, timedOut });
452
+ });
453
+ });
454
+ }
455
+
456
+ export async function exportContractABI(
457
+ projectPath: string,
458
+ onOutput?: (output: CompilationOutput) => void
459
+ ): Promise<{
460
+ success: boolean;
461
+ solidity?: string;
462
+ abi?: string;
463
+ error?: string;
464
+ details?: string;
465
+ }> {
466
+ const timeoutMs = Math.min(COMPILATION_CONSTANTS.COMPILE_TIMEOUT, 180_000);
467
+
468
+ // 1) Solidity interface
469
+ const solRes = await runCargo(
470
+ projectPath,
471
+ ["stylus", "export-abi", "--rust-features=export-abi"],
472
+ timeoutMs,
473
+ onOutput
474
+ );
475
+
476
+ const combinedSol = `${solRes.stdout}\n${solRes.stderr}`;
477
+ const solidity =
478
+ extractFromFirstInterface(solRes.stdout) ||
479
+ extractFromFirstInterface(solRes.stderr) ||
480
+ extractFromFirstInterface(combinedSol);
481
+
482
+ if (solRes.timedOut) {
483
+ return {
484
+ success: false,
485
+ error: "ABI export timed out",
486
+ details: `Timed out after ${timeoutMs}ms\nSTDERR:\n${solRes.stderr}\nSTDOUT:\n${solRes.stdout}`,
487
+ };
488
+ }
489
+
490
+ if (solRes.code !== 0 || !solidity) {
491
+ return {
492
+ success: false,
493
+ error: "Failed to export Solidity interface",
494
+ details: `exit=${solRes.code}\nSTDERR:\n${solRes.stderr}\nSTDOUT:\n${solRes.stdout}`,
495
+ };
496
+ }
497
+
498
+ // 2) JSON ABI (optional; may require solc)
499
+ const jsonRes = await runCargo(
500
+ projectPath,
501
+ ["stylus", "export-abi", "--json", "--rust-features=export-abi"],
502
+ timeoutMs,
503
+ onOutput
504
+ );
505
+
506
+ let abi: string | undefined;
507
+
508
+ if (jsonRes.code === 0) {
509
+ const mixed = `${jsonRes.stdout}\n${jsonRes.stderr}`;
510
+ const jsonPart =
511
+ extractJsonArray(jsonRes.stdout) ||
512
+ extractJsonArray(jsonRes.stderr) ||
513
+ extractJsonArray(mixed);
514
+
515
+ if (jsonPart) {
516
+ try {
517
+ abi = JSON.stringify(JSON.parse(jsonPart), null, 2);
518
+ } catch {
519
+ // ignore parse failures; still return solidity
520
+ }
521
+ }
522
+ }
523
+
524
+ return {
525
+ success: true,
526
+ solidity,
527
+ abi,
528
+ };
529
+ }
@@ -0,0 +1,31 @@
1
+ export const COMPILATION_CONSTANTS = {
2
+ COMMANDS: {
3
+ BUILD: ["build", "--target", "wasm32-unknown-unknown", "--release"],
4
+ CHECK: ["stylus", "check", "--endpoint"], // For Phase 2
5
+ EXPORT_ABI: [
6
+ "run",
7
+ "--release",
8
+ "--features",
9
+ "export-abi",
10
+ "--bin",
11
+ "stylus-hello-world",
12
+ ], // RUN the binary
13
+ },
14
+
15
+ RUST_TOOLCHAIN_CHANNEL: "1.87.0",
16
+ TEMP_BASE: ".stylus-temp",
17
+ COMPILE_TIMEOUT: 120000,
18
+ PROCESS_TIMEOUT: 180000,
19
+ MAX_WASM_SIZE: 24 * 1024,
20
+ };
21
+
22
+ export const ERROR_MESSAGES = {
23
+ RUST_NOT_FOUND: "Rust is not installed. Run: npm run setup",
24
+ CARGO_STYLUS_NOT_FOUND:
25
+ "cargo-stylus not found. Run: cargo install cargo-stylus",
26
+ COMPILATION_FAILED: "Compilation failed. Check output for details.",
27
+ TIMEOUT: "Compilation timed out after 2 minutes.",
28
+ WASM_TARGET_MISSING: "WASM target not installed for Rust toolchain",
29
+ WASM_TOO_LARGE:
30
+ "WASM binary exceeds 24KB size limit. Try optimizing with opt-level = 's' or 'z'",
31
+ };