Skip to content

Commit 16482cf

Browse files
committed
Stylus ERC20 template for CLI (#7192)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## 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}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent e8a25d7 commit 16482cf

File tree

2 files changed

+78
-20
lines changed

2 files changed

+78
-20
lines changed

packages/thirdweb/src/cli/commands/stylus/builder.ts

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
33
import { join } from "node:path";
44
import open from "open";
55
import ora, { type Ora } from "ora";
6+
import prompts from "prompts";
67
import { parse } from "toml";
78
import { createThirdwebClient } from "../../../client/client.js";
89
import { upload } from "../../../storage/upload.js";
@@ -95,15 +96,49 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
9596
spinner.succeed("ABI generated.");
9697

9798
// Step 4: Process the output
98-
const contractName = extractContractNameFromExportAbi(abiContent);
99-
if (!contractName) {
99+
const parts = abiContent.split(/======= <stdin>:/g).filter(Boolean);
100+
const contractNames = extractContractNamesFromExportAbi(abiContent);
101+
102+
let selectedContractName: string | undefined;
103+
let selectedAbiContent: string | undefined;
104+
105+
if (contractNames.length === 1) {
106+
selectedContractName = contractNames[0]?.replace(/^I/, "");
107+
selectedAbiContent = parts[0];
108+
} else {
109+
const response = await prompts({
110+
type: "select",
111+
name: "contract",
112+
message: "Select entrypoint:",
113+
choices: contractNames.map((name, idx) => ({
114+
title: name,
115+
value: idx,
116+
})),
117+
});
118+
119+
const selectedIndex = response.contract;
120+
121+
if (typeof selectedIndex !== "number") {
122+
spinner.fail("No contract selected.");
123+
process.exit(1);
124+
}
125+
126+
selectedContractName = contractNames[selectedIndex]?.replace(/^I/, "");
127+
selectedAbiContent = parts[selectedIndex];
128+
}
129+
130+
if (!selectedAbiContent) {
131+
throw new Error("Entrypoint not found");
132+
}
133+
134+
if (!selectedContractName) {
100135
spinner.fail("Error: Could not determine contract name from ABI output.");
101136
process.exit(1);
102137
}
103138

104139
let cleanedAbi = "";
105140
try {
106-
const jsonMatch = abiContent.match(/\[.*\]/s);
141+
const jsonMatch = selectedAbiContent.match(/\[.*\]/s);
107142
if (jsonMatch) {
108143
cleanedAbi = jsonMatch[0];
109144
} else {
@@ -125,7 +160,7 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
125160
},
126161
settings: {
127162
compilationTarget: {
128-
"src/main.rs": contractName,
163+
"src/main.rs": selectedContractName,
129164
},
130165
},
131166
sources: {},
@@ -152,12 +187,12 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
152187
client,
153188
files: [
154189
{
155-
name: contractName,
190+
name: selectedContractName,
156191
metadataUri,
157192
bytecodeUri,
158193
analytics: {
159194
command: "publish-stylus",
160-
contract_name: contractName,
195+
contract_name: selectedContractName,
161196
cli_version: "",
162197
project_type: "stylus",
163198
},
@@ -178,12 +213,10 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
178213
}
179214
}
180215

181-
function extractContractNameFromExportAbi(abiRawOutput: string): string | null {
182-
const match = abiRawOutput.match(/<stdin>:(I[A-Za-z0-9_]+)/);
183-
if (match?.[1]) {
184-
return match[1].replace(/^I/, "");
185-
}
186-
return null;
216+
function extractContractNamesFromExportAbi(abiRawOutput: string): string[] {
217+
return [...abiRawOutput.matchAll(/<stdin>:(I?[A-Za-z0-9_]+)/g)]
218+
.map((m) => m[1])
219+
.filter((name): name is string => typeof name === "string");
187220
}
188221

189222
function getUrl(hash: string, command: string) {

packages/thirdweb/src/cli/commands/stylus/create.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,40 @@ export async function createStylusProject() {
3737
message: "Project name:",
3838
initial: "my-stylus-project",
3939
});
40-
spinner.start(`Creating new Stylus project: ${projectName}...`);
41-
const newProject = spawnSync("cargo", ["stylus", "new", projectName], {
42-
stdio: "inherit",
40+
41+
// Step 4: Select project type
42+
const { projectType } = await prompts({
43+
type: "select",
44+
name: "projectType",
45+
message: "Select a template:",
46+
choices: [
47+
{ title: "Default", value: "default" },
48+
{ title: "ERC20", value: "erc20" },
49+
],
4350
});
44-
if (newProject.status !== 0) {
45-
spinner.fail("Failed to create Stylus project.");
46-
process.exit(1);
51+
52+
// Step 5: Create the project
53+
if (projectType === "default") {
54+
spinner.start(`Creating new Stylus project: ${projectName}...`);
55+
const newProject = spawnSync("cargo", ["stylus", "new", projectName], {
56+
stdio: "inherit",
57+
});
58+
if (newProject.status !== 0) {
59+
spinner.fail("Failed to create Stylus project.");
60+
process.exit(1);
61+
}
62+
} else if (projectType === "erc20") {
63+
const repoUrl = "[email protected]:thirdweb-example/stylus-erc20-template.git";
64+
spinner.start(`Creating new ERC20 Stylus project: ${projectName}...`);
65+
const clone = spawnSync("git", ["clone", repoUrl, projectName], {
66+
stdio: "inherit",
67+
});
68+
if (clone.status !== 0) {
69+
spinner.fail("Failed to create Stylus project.");
70+
process.exit(1);
71+
}
4772
}
48-
spinner.succeed("Project created successfully.");
4973

50-
console.log(`\n✅ Done! cd into your project: ${projectName}`);
74+
spinner.succeed("Project created successfully.");
75+
console.log(`\n✅ cd into your project: ${projectName}`);
5176
}

0 commit comments

Comments
 (0)