From 16482cfa6a64cfabbf03964cf0b4f147b1c9488e Mon Sep 17 00:00:00 2001 From: kumaryash90 Date: Thu, 29 May 2025 12:12:00 +0000 Subject: [PATCH] Stylus ERC20 template for CLI (#7192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR enhances the `create.ts` and `builder.ts` files for the Stylus project by adding project type selection and improving contract name handling during ABI processing. It introduces prompts for user input and refines how contract names are extracted and utilized. ### Detailed summary - In `create.ts`, added prompts for selecting project type (Default or ERC20). - Updated project creation logic based on the selected type. - In `builder.ts`, improved contract name extraction to handle multiple contracts. - Added prompts for selecting the entrypoint if multiple contracts are found. - Refined usage of selected contract names throughout the build process. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit - **New Features** - Added support for handling multiple contract ABIs, allowing users to select a contract entrypoint when more than one is present. - Introduced a prompt to select a project template type ("default" or "erc20") during project creation. - **Improvements** - Enhanced error handling and user guidance when no contract is selected or found. - Updated success messages and console output for improved clarity. --- .../src/cli/commands/stylus/builder.ts | 57 +++++++++++++++---- .../src/cli/commands/stylus/create.ts | 41 ++++++++++--- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/packages/thirdweb/src/cli/commands/stylus/builder.ts b/packages/thirdweb/src/cli/commands/stylus/builder.ts index b6a6e63a41b..39616213eb6 100644 --- a/packages/thirdweb/src/cli/commands/stylus/builder.ts +++ b/packages/thirdweb/src/cli/commands/stylus/builder.ts @@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import open from "open"; import ora, { type Ora } from "ora"; +import prompts from "prompts"; import { parse } from "toml"; import { createThirdwebClient } from "../../../client/client.js"; import { upload } from "../../../storage/upload.js"; @@ -95,15 +96,49 @@ async function buildStylus(spinner: Ora, secretKey?: string) { spinner.succeed("ABI generated."); // Step 4: Process the output - const contractName = extractContractNameFromExportAbi(abiContent); - if (!contractName) { + const parts = abiContent.split(/======= :/g).filter(Boolean); + const contractNames = extractContractNamesFromExportAbi(abiContent); + + let selectedContractName: string | undefined; + let selectedAbiContent: string | undefined; + + if (contractNames.length === 1) { + selectedContractName = contractNames[0]?.replace(/^I/, ""); + selectedAbiContent = parts[0]; + } else { + const response = await prompts({ + type: "select", + name: "contract", + message: "Select entrypoint:", + choices: contractNames.map((name, idx) => ({ + title: name, + value: idx, + })), + }); + + const selectedIndex = response.contract; + + if (typeof selectedIndex !== "number") { + spinner.fail("No contract selected."); + process.exit(1); + } + + selectedContractName = contractNames[selectedIndex]?.replace(/^I/, ""); + selectedAbiContent = parts[selectedIndex]; + } + + if (!selectedAbiContent) { + throw new Error("Entrypoint not found"); + } + + if (!selectedContractName) { spinner.fail("Error: Could not determine contract name from ABI output."); process.exit(1); } let cleanedAbi = ""; try { - const jsonMatch = abiContent.match(/\[.*\]/s); + const jsonMatch = selectedAbiContent.match(/\[.*\]/s); if (jsonMatch) { cleanedAbi = jsonMatch[0]; } else { @@ -125,7 +160,7 @@ async function buildStylus(spinner: Ora, secretKey?: string) { }, settings: { compilationTarget: { - "src/main.rs": contractName, + "src/main.rs": selectedContractName, }, }, sources: {}, @@ -152,12 +187,12 @@ async function buildStylus(spinner: Ora, secretKey?: string) { client, files: [ { - name: contractName, + name: selectedContractName, metadataUri, bytecodeUri, analytics: { command: "publish-stylus", - contract_name: contractName, + contract_name: selectedContractName, cli_version: "", project_type: "stylus", }, @@ -178,12 +213,10 @@ async function buildStylus(spinner: Ora, secretKey?: string) { } } -function extractContractNameFromExportAbi(abiRawOutput: string): string | null { - const match = abiRawOutput.match(/:(I[A-Za-z0-9_]+)/); - if (match?.[1]) { - return match[1].replace(/^I/, ""); - } - return null; +function extractContractNamesFromExportAbi(abiRawOutput: string): string[] { + return [...abiRawOutput.matchAll(/:(I?[A-Za-z0-9_]+)/g)] + .map((m) => m[1]) + .filter((name): name is string => typeof name === "string"); } function getUrl(hash: string, command: string) { diff --git a/packages/thirdweb/src/cli/commands/stylus/create.ts b/packages/thirdweb/src/cli/commands/stylus/create.ts index 561517067b1..d632fd06010 100644 --- a/packages/thirdweb/src/cli/commands/stylus/create.ts +++ b/packages/thirdweb/src/cli/commands/stylus/create.ts @@ -37,15 +37,40 @@ export async function createStylusProject() { message: "Project name:", initial: "my-stylus-project", }); - spinner.start(`Creating new Stylus project: ${projectName}...`); - const newProject = spawnSync("cargo", ["stylus", "new", projectName], { - stdio: "inherit", + + // Step 4: Select project type + const { projectType } = await prompts({ + type: "select", + name: "projectType", + message: "Select a template:", + choices: [ + { title: "Default", value: "default" }, + { title: "ERC20", value: "erc20" }, + ], }); - if (newProject.status !== 0) { - spinner.fail("Failed to create Stylus project."); - process.exit(1); + + // Step 5: Create the project + if (projectType === "default") { + spinner.start(`Creating new Stylus project: ${projectName}...`); + const newProject = spawnSync("cargo", ["stylus", "new", projectName], { + stdio: "inherit", + }); + if (newProject.status !== 0) { + spinner.fail("Failed to create Stylus project."); + process.exit(1); + } + } else if (projectType === "erc20") { + const repoUrl = "git@github.com:thirdweb-example/stylus-erc20-template.git"; + spinner.start(`Creating new ERC20 Stylus project: ${projectName}...`); + const clone = spawnSync("git", ["clone", repoUrl, projectName], { + stdio: "inherit", + }); + if (clone.status !== 0) { + spinner.fail("Failed to create Stylus project."); + process.exit(1); + } } - spinner.succeed("Project created successfully."); - console.log(`\n✅ Done! cd into your project: ${projectName}`); + spinner.succeed("Project created successfully."); + console.log(`\n✅ cd into your project: ${projectName}`); }