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,374 @@
1
+ /**
2
+ * Blockchain explorer API client
3
+ */
4
+
5
+ import {
6
+ ExplorerAPIResponse,
7
+ ContractSourceCode,
8
+ ParsedContractSource,
9
+ } from "@/types/blockchain";
10
+ import { getExplorerConfig } from "@/lib/blockchain-explorers";
11
+
12
+ export class BlockchainAPIError extends Error {
13
+ constructor(
14
+ message: string,
15
+ public statusCode?: number,
16
+ public isNotVerified: boolean = false,
17
+ public isInvalidAddress: boolean = false,
18
+ public isAPIKeyMissing: boolean = false
19
+ ) {
20
+ super(message);
21
+ this.name = "BlockchainAPIError";
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Blockchain API Client
27
+ */
28
+ export class BlockchainAPIClient {
29
+ private apiKeys: Record<string, string> = {};
30
+
31
+ constructor() {
32
+ // Load API keys from environment (client-side only)
33
+ if (typeof window !== "undefined") {
34
+ console.log("🔑 Loading API keys from environment...");
35
+
36
+ const arbiscanKey = process.env.NEXT_PUBLIC_ARBISCAN_API_KEY;
37
+ const etherscanKey = process.env.NEXT_PUBLIC_ETHERSCAN_API_KEY;
38
+ const basescanKey = process.env.NEXT_PUBLIC_BASESCAN_API_KEY;
39
+
40
+ console.log("Available keys:", {
41
+ arbiscan: !!arbiscanKey,
42
+ etherscan: !!etherscanKey,
43
+ basescan: !!basescanKey,
44
+ });
45
+
46
+ this.apiKeys = {
47
+ "arbiscan.io": arbiscanKey || "",
48
+ "sepolia.arbiscan.io": arbiscanKey || "", // Use same key for testnet
49
+ "etherscan.io": etherscanKey || "",
50
+ "sepolia.etherscan.io": etherscanKey || "",
51
+ "basescan.org": basescanKey || "",
52
+ };
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Fetch contract source code from explorer
58
+ */
59
+ async getContractSource(
60
+ explorerDomain: string,
61
+ address: string
62
+ ): Promise<ContractSourceCode> {
63
+ const config = getExplorerConfig(explorerDomain);
64
+
65
+ if (!config) {
66
+ throw new BlockchainAPIError(
67
+ `Unsupported blockchain explorer: ${explorerDomain}`,
68
+ 0,
69
+ false,
70
+ false,
71
+ false
72
+ );
73
+ }
74
+
75
+ // Get API key for this explorer (for V2: typically your Etherscan API key)
76
+ const apiKey = this.apiKeys[explorerDomain] || "";
77
+
78
+ console.log("🔍 API Key check:", {
79
+ domain: explorerDomain,
80
+ hasKey: !!apiKey,
81
+ keyPreview: apiKey ? apiKey.slice(0, 10) + "..." : "none",
82
+ });
83
+
84
+ if (!apiKey && config.apiKeyRequired) {
85
+ throw new BlockchainAPIError(
86
+ `API key required for ${config.name}. Please add NEXT_PUBLIC_ETHERSCAN_API_KEY to your .env.local file.`,
87
+ 0,
88
+ false,
89
+ false,
90
+ true
91
+ );
92
+ }
93
+
94
+ // --- V2 IMPORTANT: include chainid (default is 1, but you should always pass it) ---
95
+ // Ensure your BlockchainExplorerConfig includes chainId:number
96
+ const chainId =
97
+ (config as any).chainId ??
98
+ (config.network === "sepolia" && config.chain === "ethereum"
99
+ ? 11155111
100
+ : config.network === "mainnet" && config.chain === "ethereum"
101
+ ? 1
102
+ : undefined);
103
+
104
+ if (!chainId) {
105
+ throw new BlockchainAPIError(
106
+ `Missing chainId for explorer ${config.name}. Add chainId to BLOCKCHAIN_EXPLORERS config.`,
107
+ 0
108
+ );
109
+ }
110
+
111
+ const params = new URLSearchParams({
112
+ chainid: String(chainId), // ✅ V2
113
+ module: "contract",
114
+ action: "getsourcecode",
115
+ address,
116
+ });
117
+
118
+ if (apiKey) params.set("apikey", apiKey);
119
+
120
+ const url = `${config.apiUrl}?${params.toString()}`;
121
+
122
+ console.log("📡 API Request:", {
123
+ url: apiKey ? url.replace(apiKey, "API_KEY_HIDDEN") : url,
124
+ address,
125
+ chainId,
126
+ });
127
+
128
+ try {
129
+ const response = await fetch(url);
130
+
131
+ if (!response.ok) {
132
+ throw new BlockchainAPIError(
133
+ `Failed to fetch from ${config.name}: ${response.statusText}`,
134
+ response.status
135
+ );
136
+ }
137
+
138
+ const data: ExplorerAPIResponse = await response.json();
139
+
140
+ console.log("📦 API Response:", {
141
+ status: data.status,
142
+ message: data.message,
143
+ hasResult: !!data.result,
144
+ });
145
+
146
+ // Check API response status
147
+ if (data.status !== "1") {
148
+ console.error("❌ API Error Details:", data);
149
+
150
+ const resultText = typeof data.result === "string" ? data.result : "";
151
+ const combinedText = `${data.message || ""} ${resultText || ""}`.trim();
152
+
153
+ // V1 deprecation message (means you're still hitting a V1 endpoint somewhere)
154
+ if (combinedText.toLowerCase().includes("deprecated v1 endpoint")) {
155
+ throw new BlockchainAPIError(
156
+ `You are hitting a deprecated V1 endpoint. Ensure config.apiUrl is "https://api.etherscan.io/v2/api" and you pass "chainid" for ${config.chain} ${config.network}.`,
157
+ 0
158
+ );
159
+ }
160
+
161
+ // Invalid API key (often comes via result text in V2)
162
+ if (combinedText.toLowerCase().includes("invalid api key")) {
163
+ throw new BlockchainAPIError(
164
+ `Invalid API key for ${config.name}. Please check NEXT_PUBLIC_ETHERSCAN_API_KEY.`,
165
+ 0,
166
+ false,
167
+ false,
168
+ true
169
+ );
170
+ }
171
+
172
+ // Some V2 errors are returned as NOTOK with details in `result`
173
+ if (data.message === "NOTOK") {
174
+ throw new BlockchainAPIError(
175
+ `API request failed: ${
176
+ resultText || data.message
177
+ }\n\nChecks:\n1) API key is valid\n2) Contract address is correct\n3) Correct chainid (${chainId}) for ${
178
+ config.chain
179
+ } ${config.network}`,
180
+ 0
181
+ );
182
+ }
183
+
184
+ throw new BlockchainAPIError(
185
+ `API Error: ${combinedText || "Unknown error"}`,
186
+ 0
187
+ );
188
+ }
189
+
190
+ // Parse result
191
+ const result = Array.isArray(data.result) ? data.result[0] : data.result;
192
+
193
+ if (!result || typeof result === "string") {
194
+ throw new BlockchainAPIError("Invalid response from explorer API", 0);
195
+ }
196
+
197
+ const sourceCode = result as ContractSourceCode;
198
+
199
+ // Check if contract is verified
200
+ if (!sourceCode.SourceCode || sourceCode.SourceCode === "") {
201
+ throw new BlockchainAPIError(
202
+ "Contract is not verified on this explorer",
203
+ 0,
204
+ true
205
+ );
206
+ }
207
+
208
+ console.log("✅ Contract loaded:", sourceCode.ContractName);
209
+
210
+ return sourceCode;
211
+ } catch (error) {
212
+ if (error instanceof BlockchainAPIError) {
213
+ throw error;
214
+ }
215
+
216
+ console.error("❌ Network/Unknown error:", error);
217
+ throw new BlockchainAPIError(
218
+ "Network error. Please check your connection and try again.",
219
+ 0
220
+ );
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Parse contract source code into files
226
+ */
227
+ parseContractSource(sourceCode: ContractSourceCode): ParsedContractSource {
228
+ const rawSource = sourceCode.SourceCode;
229
+
230
+ console.log("🔍 Parsing contract source...");
231
+ console.log("Raw source length:", rawSource.length);
232
+ console.log("First 100 chars:", rawSource.substring(0, 100));
233
+
234
+ // Check if it's multi-file (JSON format)
235
+ if (rawSource.startsWith("{")) {
236
+ try {
237
+ let parsed: any;
238
+
239
+ // Try double-brace format first: {{...}}
240
+ if (rawSource.startsWith("{{") && rawSource.endsWith("}}")) {
241
+ console.log("📦 Detected double-brace format");
242
+ const jsonStr = rawSource.slice(1, -1);
243
+ parsed = JSON.parse(jsonStr);
244
+ } else {
245
+ console.log("📦 Detected standard JSON format");
246
+ parsed = JSON.parse(rawSource);
247
+ }
248
+
249
+ console.log("Parsed keys:", Object.keys(parsed));
250
+
251
+ // Standard JSON format: { "language": "Solidity", "sources": {...} }
252
+ if (parsed.sources) {
253
+ console.log("✅ Using standard JSON sources format");
254
+ console.log("Source files:", Object.keys(parsed.sources));
255
+
256
+ const files = Object.entries(parsed.sources).map(
257
+ ([path, sourceObj]: [string, any]) => {
258
+ // Clean path
259
+ const cleanPath = path.startsWith("/") ? path.slice(1) : path;
260
+
261
+ // Extract content - handle different structures
262
+ let content = "";
263
+ if (typeof sourceObj === "string") {
264
+ content = sourceObj;
265
+ } else if (sourceObj && typeof sourceObj === "object") {
266
+ content = sourceObj.content || sourceObj.source || "";
267
+ }
268
+
269
+ console.log(` - ${cleanPath}: ${content.length} bytes`);
270
+
271
+ return {
272
+ path: cleanPath,
273
+ content,
274
+ };
275
+ }
276
+ );
277
+
278
+ // Also include language and settings as files if present
279
+ if (parsed.language) {
280
+ files.push({
281
+ path: "metadata/language.txt",
282
+ content: parsed.language,
283
+ });
284
+ }
285
+
286
+ if (parsed.settings) {
287
+ files.push({
288
+ path: "metadata/settings.json",
289
+ content: JSON.stringify(parsed.settings, null, 2),
290
+ });
291
+ }
292
+
293
+ return {
294
+ type: "multi-file",
295
+ files: files.filter((f) => f.content && f.content.length > 0), // Filter empty files
296
+ mainFile: files[0]?.path || "Contract.sol",
297
+ };
298
+ }
299
+
300
+ // Legacy format: { "Contract.sol": {...}, "OtherFile.sol": {...} }
301
+ console.log("✅ Using legacy JSON format");
302
+ const files = Object.entries(parsed)
303
+ .filter(([key]) => !["language", "settings"].includes(key))
304
+ .map(([path, sourceObj]: [string, any]) => {
305
+ const cleanPath = path.startsWith("/") ? path.slice(1) : path;
306
+
307
+ let content = "";
308
+ if (typeof sourceObj === "string") {
309
+ content = sourceObj;
310
+ } else if (sourceObj && typeof sourceObj === "object") {
311
+ content = sourceObj.content || sourceObj.source || "";
312
+ }
313
+
314
+ console.log(` - ${cleanPath}: ${content.length} bytes`);
315
+
316
+ return {
317
+ path: cleanPath,
318
+ content,
319
+ };
320
+ })
321
+ .filter((f) => f.content && f.content.length > 0);
322
+
323
+ if (files.length === 0) {
324
+ throw new Error("No valid source files found in JSON");
325
+ }
326
+
327
+ return {
328
+ type: "multi-file",
329
+ files,
330
+ mainFile: files[0]?.path || "Contract.sol",
331
+ };
332
+ } catch (error) {
333
+ console.error("❌ Failed to parse JSON:", error);
334
+ console.log("Falling back to single file mode");
335
+ }
336
+ }
337
+
338
+ // Single file or flattened contract
339
+ console.log("📄 Using single file format");
340
+ const fileName = `${sourceCode.ContractName}.sol`;
341
+
342
+ if (!rawSource || rawSource.length === 0) {
343
+ throw new Error("Source code is empty");
344
+ }
345
+
346
+ return {
347
+ type: rawSource.includes("// File:") ? "flattened" : "single",
348
+ files: [
349
+ {
350
+ path: fileName,
351
+ content: rawSource,
352
+ },
353
+ ],
354
+ mainFile: fileName,
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Set API key for an explorer
360
+ */
361
+ setAPIKey(explorerDomain: string, apiKey: string): void {
362
+ this.apiKeys[explorerDomain] = apiKey;
363
+ }
364
+
365
+ /**
366
+ * Check if API key is set for explorer
367
+ */
368
+ hasAPIKey(explorerDomain: string): boolean {
369
+ return !!this.apiKeys[explorerDomain];
370
+ }
371
+ }
372
+
373
+ // Export singleton instance
374
+ export const blockchainAPI = new BlockchainAPIClient();
@@ -0,0 +1,75 @@
1
+ // lib/blockchain-explorers.ts
2
+ import { BlockchainExplorerConfig } from "@/types/blockchain";
3
+
4
+ const ETHERSCAN_V2_BASE = "https://api.etherscan.io/v2/api";
5
+
6
+ export const BLOCKCHAIN_EXPLORERS: Record<string, BlockchainExplorerConfig> = {
7
+ "etherscan.io": {
8
+ name: "Etherscan",
9
+ chain: "ethereum",
10
+ network: "mainnet",
11
+ chainId: 1,
12
+ apiUrl: ETHERSCAN_V2_BASE,
13
+ explorerUrl: "https://etherscan.io",
14
+ apiKeyRequired: true,
15
+ },
16
+ "sepolia.etherscan.io": {
17
+ name: "Sepolia Etherscan",
18
+ chain: "ethereum",
19
+ network: "sepolia",
20
+ chainId: 11155111,
21
+ apiUrl: ETHERSCAN_V2_BASE, // ✅ don't use api-sepolia host for V2
22
+ explorerUrl: "https://sepolia.etherscan.io",
23
+ apiKeyRequired: true,
24
+ },
25
+ "arbiscan.io": {
26
+ name: "Arbiscan",
27
+ chain: "arbitrum",
28
+ network: "mainnet",
29
+ chainId: 42161,
30
+ apiUrl: ETHERSCAN_V2_BASE, // ✅ unified V2 base
31
+ explorerUrl: "https://arbiscan.io",
32
+ apiKeyRequired: true,
33
+ },
34
+ "sepolia.arbiscan.io": {
35
+ name: "Arbiscan Sepolia",
36
+ chain: "arbitrum",
37
+ network: "sepolia",
38
+ chainId: 421614,
39
+ apiUrl: ETHERSCAN_V2_BASE,
40
+ explorerUrl: "https://sepolia.arbiscan.io",
41
+ apiKeyRequired: true,
42
+ },
43
+ "basescan.org": {
44
+ name: "Basescan",
45
+ chain: "base",
46
+ network: "mainnet",
47
+ chainId: 8453,
48
+ apiUrl: ETHERSCAN_V2_BASE,
49
+ explorerUrl: "https://basescan.org",
50
+ apiKeyRequired: true,
51
+ },
52
+ };
53
+
54
+ /**
55
+ * Get explorer config by domain
56
+ */
57
+ export function getExplorerConfig(
58
+ domain: string
59
+ ): BlockchainExplorerConfig | null {
60
+ return BLOCKCHAIN_EXPLORERS[domain] || null;
61
+ }
62
+
63
+ /**
64
+ * Get all supported explorers
65
+ */
66
+ export function getSupportedExplorers(): BlockchainExplorerConfig[] {
67
+ return Object.values(BLOCKCHAIN_EXPLORERS);
68
+ }
69
+
70
+ /**
71
+ * Check if domain is a supported explorer
72
+ */
73
+ export function isSupportedExplorer(domain: string): boolean {
74
+ return domain in BLOCKCHAIN_EXPLORERS;
75
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Blockchain contract loader - for interaction panel, not editor
3
+ */
4
+
5
+ import { ContractInteractionData } from "@/types/blockchain";
6
+ import { BlockchainURLInfo } from "@/lib/url-parser";
7
+ import { blockchainAPI } from "@/lib/blockchain-api";
8
+ import { getExplorerConfig } from "@/lib/blockchain-explorers";
9
+
10
+ export interface BlockchainLoadProgress {
11
+ stage: "validating" | "fetching" | "parsing" | "complete" | "error";
12
+ message: string;
13
+ progress: number; // 0-100
14
+ contractName?: string;
15
+ }
16
+
17
+ export type BlockchainProgressCallback = (
18
+ progress: BlockchainLoadProgress
19
+ ) => void;
20
+
21
+ /**
22
+ * Fetch contract data for interaction (not for editor)
23
+ */
24
+ export async function fetchContractForInteraction(
25
+ urlInfo: BlockchainURLInfo,
26
+ onProgress?: BlockchainProgressCallback
27
+ ): Promise<ContractInteractionData> {
28
+ const { explorerDomain, address, chain, network } = urlInfo;
29
+
30
+ try {
31
+ // Stage 1: Validate address format
32
+ onProgress?.({
33
+ stage: "validating",
34
+ message: "Validating contract address...",
35
+ progress: 10,
36
+ });
37
+
38
+ if (!address.match(/^0x[a-fA-F0-9]{40}$/)) {
39
+ throw new Error("Invalid Ethereum address format");
40
+ }
41
+
42
+ // Stage 2: Fetch contract source + ABI
43
+ onProgress?.({
44
+ stage: "fetching",
45
+ message: `Fetching contract from ${chain}...`,
46
+ progress: 40,
47
+ });
48
+
49
+ const sourceCode = await blockchainAPI.getContractSource(
50
+ explorerDomain,
51
+ address
52
+ );
53
+
54
+ onProgress?.({
55
+ stage: "fetching",
56
+ message: `Found contract: ${sourceCode.ContractName}`,
57
+ progress: 70,
58
+ contractName: sourceCode.ContractName,
59
+ });
60
+
61
+ // Stage 3: Extract ABI
62
+ onProgress?.({
63
+ stage: "parsing",
64
+ message: "Extracting contract ABI...",
65
+ progress: 85,
66
+ });
67
+
68
+ if (!sourceCode.ABI || sourceCode.ABI === "") {
69
+ throw new Error("Contract ABI not available");
70
+ }
71
+
72
+ // Get explorer config for URL
73
+ const explorerConfig = getExplorerConfig(explorerDomain);
74
+ const explorerUrl = explorerConfig
75
+ ? `${explorerConfig.explorerUrl}/address/${address}`
76
+ : urlInfo.rawUrl;
77
+
78
+ // Build contract interaction data
79
+ const contractData: ContractInteractionData = {
80
+ address,
81
+ name: sourceCode.ContractName,
82
+ chain,
83
+ network,
84
+ chainId: explorerConfig?.chainId || 0,
85
+ abi: sourceCode.ABI,
86
+ verified: true,
87
+ compiler: sourceCode.CompilerVersion,
88
+ optimization: sourceCode.OptimizationUsed === "1",
89
+ explorerUrl,
90
+ };
91
+
92
+ onProgress?.({
93
+ stage: "complete",
94
+ message: `Ready to interact with ${sourceCode.ContractName}`,
95
+ progress: 100,
96
+ contractName: sourceCode.ContractName,
97
+ });
98
+
99
+ return contractData;
100
+ } catch (error) {
101
+ const errorMessage =
102
+ error instanceof Error ? error.message : "Unknown error";
103
+
104
+ onProgress?.({
105
+ stage: "error",
106
+ message: errorMessage,
107
+ progress: 0,
108
+ });
109
+
110
+ throw error;
111
+ }
112
+ }
@@ -0,0 +1,64 @@
1
+ export const CARGO_TOML_TEMPLATE = `[package]
2
+ name = "stylus-hello-world"
3
+ version = "0.1.11"
4
+ edition = "2021"
5
+ license = "MIT OR Apache-2.0"
6
+ homepage = "https://github.com/OffchainLabs/stylus-hello-world"
7
+ repository = "https://github.com/OffchainLabs/stylus-hello-world"
8
+ keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
9
+ description = "Stylus hello world example"
10
+
11
+ [dependencies]
12
+ alloy-primitives = "=0.8.20"
13
+ alloy-sol-types = "=0.8.20"
14
+ stylus-sdk = "0.9.0"
15
+ hex = { version = "0.4", default-features = false }
16
+
17
+ [dev-dependencies]
18
+ alloy-primitives = { version = "=0.8.20", features = ["sha3-keccak"] }
19
+ tokio = { version = "1.12.0", features = ["full"] }
20
+ ethers = "2.0"
21
+ eyre = "0.6.8"
22
+ stylus-sdk = { version = "0.9.0", features = ["stylus-test"] }
23
+ dotenv = "0.15.0"
24
+
25
+ [features]
26
+ default = ["mini-alloc"]
27
+ export-abi = ["stylus-sdk/export-abi"]
28
+ debug = ["stylus-sdk/debug"]
29
+ mini-alloc = ["stylus-sdk/mini-alloc"]
30
+
31
+ [[bin]]
32
+ name = "stylus-hello-world"
33
+ path = "src/main.rs"
34
+ required-features = ["export-abi"]
35
+
36
+ [lib]
37
+ crate-type = ["lib", "cdylib"]
38
+
39
+ [profile.release]
40
+ codegen-units = 1
41
+ strip = true
42
+ lto = true
43
+ panic = "abort"
44
+ opt-level = 3
45
+ `;
46
+
47
+ export const RUST_TOOLCHAIN_TOML = `[toolchain]
48
+ channel = "1.87.0"
49
+ targets = ["wasm32-unknown-unknown"]
50
+ `;
51
+
52
+ export const MAIN_RS_TEMPLATE = `#![cfg_attr(not(feature = "export-abi"), no_main)]
53
+
54
+ #[cfg(feature = "export-abi")]
55
+ fn main() {
56
+ // Generated by Stylus when #[entrypoint] + #[public] exist in src/lib.rs
57
+ stylus_hello_world::print_abi("MIT-OR-APACHE-2.0", "pragma solidity ^0.8.23;");
58
+ }
59
+ `;
60
+
61
+ export const GITIGNORE_TEMPLATE = `target/
62
+ .DS_Store
63
+ Cargo.lock
64
+ `;