thirdweb 5.119.1 → 5.119.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/stylus/builder.ts"],"names":[],"mappings":"AAaA,wBAAsB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,iBAiBrD;AAED,wBAAsB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,iBAiBpD"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/stylus/builder.ts"],"names":[],"mappings":"AAcA,wBAAsB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,iBAWrD;AAED,wBAAsB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,iBAWpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/stylus/create.ts"],"names":[],"mappings":"AAKA,wBAAsB,mBAAmB,kBA4LxC"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/stylus/create.ts"],"names":[],"mappings":"AAKA,wBAAsB,mBAAmB,kBAsLxC"}
@@ -1,2 +1,2 @@
1
- export declare const version = "5.119.1";
1
+ export declare const version = "5.119.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -400,7 +400,7 @@
400
400
  }
401
401
  },
402
402
  "typings": "./dist/types/exports/thirdweb.d.ts",
403
- "version": "5.119.1",
403
+ "version": "5.119.3",
404
404
  "scripts": {
405
405
  "bench": "vitest -c ./test/vitest.config.ts bench",
406
406
  "bench:compare": "bun run ./benchmarks/run.ts",
@@ -1,6 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { parseAbiItem } from "abitype";
4
5
  import open from "open";
5
6
  import ora, { type Ora } from "ora";
6
7
  import prompts from "prompts";
@@ -16,12 +17,6 @@ export async function publishStylus(secretKey?: string) {
16
17
 
17
18
  checkPrerequisites(spinner, "cargo", ["--version"], "Rust (cargo)");
18
19
  checkPrerequisites(spinner, "rustc", ["--version"], "Rust compiler (rustc)");
19
- checkPrerequisites(
20
- spinner,
21
- "solc",
22
- ["--version"],
23
- "Solidity compiler (solc)",
24
- );
25
20
 
26
21
  const uri = await buildStylus(spinner, secretKey);
27
22
 
@@ -35,12 +30,6 @@ export async function deployStylus(secretKey?: string) {
35
30
 
36
31
  checkPrerequisites(spinner, "cargo", ["--version"], "Rust (cargo)");
37
32
  checkPrerequisites(spinner, "rustc", ["--version"], "Rust compiler (rustc)");
38
- checkPrerequisites(
39
- spinner,
40
- "solc",
41
- ["--version"],
42
- "Solidity compiler (solc)",
43
- );
44
33
 
45
34
  const uri = await buildStylus(spinner, secretKey);
46
35
 
@@ -99,9 +88,9 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
99
88
  }
100
89
  spinner.succeed("Initcode generated.");
101
90
 
102
- // Step 3: Run stylus command to generate abi
91
+ // Step 3: Run stylus command to generate abi (plain Solidity, no solc needed)
103
92
  spinner.start("Generating ABI...");
104
- const abiResult = spawnSync("cargo", ["stylus", "export-abi", "--json"], {
93
+ const abiResult = spawnSync("cargo", ["stylus", "export-abi"], {
105
94
  encoding: "utf-8",
106
95
  });
107
96
  if (abiResult.status !== 0) {
@@ -109,15 +98,21 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
109
98
  process.exit(1);
110
99
  }
111
100
 
112
- const abiContent = abiResult.stdout.trim();
113
- if (!abiContent) {
101
+ const solidityOutput = abiResult.stdout.trim();
102
+ if (!solidityOutput) {
114
103
  spinner.fail("Failed to generate ABI.");
115
104
  process.exit(1);
116
105
  }
106
+
107
+ const interfaces = parseSolidityInterfaces(solidityOutput);
108
+ if (interfaces.length === 0) {
109
+ spinner.fail("No interfaces found in ABI output.");
110
+ process.exit(1);
111
+ }
117
112
  spinner.succeed("ABI generated.");
118
113
 
119
114
  // Step 3.5: detect the constructor
120
- spinner.start("Detecting constructor");
115
+ spinner.start("Detecting constructor\u2026");
121
116
  const constructorResult = spawnSync("cargo", ["stylus", "constructor"], {
122
117
  encoding: "utf-8",
123
118
  });
@@ -127,23 +122,16 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
127
122
  process.exit(1);
128
123
  }
129
124
 
130
- const constructorSigRaw = constructorResult.stdout.trim(); // e.g. "constructor(address owner)"
125
+ const constructorSigRaw = constructorResult.stdout.trim();
131
126
  spinner.succeed(`Constructor found: ${constructorSigRaw || "none"}`);
132
127
 
133
128
  // Step 4: Process the output
134
- const parts = abiContent.split(/======= <stdin>:/g).filter(Boolean);
135
- const contractNames = extractContractNamesFromExportAbi(abiContent);
136
-
137
- let selectedContractName: string | undefined;
138
- let selectedAbiContent: string | undefined;
129
+ let selectedIndex = 0;
139
130
 
140
- if (contractNames.length === 1) {
141
- selectedContractName = contractNames[0]?.replace(/^I/, "");
142
- selectedAbiContent = parts[0];
143
- } else {
131
+ if (interfaces.length > 1) {
144
132
  const response = await prompts({
145
- choices: contractNames.map((name, idx) => ({
146
- title: name,
133
+ choices: interfaces.map((iface, idx) => ({
134
+ title: iface.name,
147
135
  value: idx,
148
136
  })),
149
137
  message: "Select entrypoint:",
@@ -151,46 +139,31 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
151
139
  type: "select",
152
140
  });
153
141
 
154
- const selectedIndex = response.contract;
155
-
156
- if (typeof selectedIndex !== "number") {
142
+ if (typeof response.contract !== "number") {
157
143
  spinner.fail("No contract selected.");
158
144
  process.exit(1);
159
145
  }
160
146
 
161
- selectedContractName = contractNames[selectedIndex]?.replace(/^I/, "");
162
- selectedAbiContent = parts[selectedIndex];
147
+ selectedIndex = response.contract;
163
148
  }
164
149
 
165
- if (!selectedAbiContent) {
166
- throw new Error("Entrypoint not found");
167
- }
168
-
169
- if (!selectedContractName) {
170
- spinner.fail("Error: Could not determine contract name from ABI output.");
171
- process.exit(1);
172
- }
173
-
174
- let cleanedAbi = "";
175
- try {
176
- const jsonMatch = selectedAbiContent.match(/\[.*\]/s);
177
- if (jsonMatch) {
178
- cleanedAbi = jsonMatch[0];
179
- } else {
180
- throw new Error("No valid JSON ABI found in the file.");
181
- }
182
- } catch (error) {
183
- spinner.fail("Error: ABI file contains invalid format.");
184
- console.error(error);
150
+ const selectedInterface = interfaces[selectedIndex];
151
+ if (!selectedInterface) {
152
+ spinner.fail("No interface found.");
185
153
  process.exit(1);
186
154
  }
187
155
 
188
- // biome-ignore lint/suspicious/noExplicitAny: <>
189
- const abiArray: any[] = JSON.parse(cleanedAbi);
156
+ const selectedContractName = selectedInterface.name.replace(/^I/, "");
157
+ // biome-ignore lint/suspicious/noExplicitAny: ABI is untyped JSON from parseAbiItem
158
+ const abiArray: any[] = selectedInterface.abi;
190
159
 
191
160
  const constructorAbi = constructorSigToAbi(constructorSigRaw);
192
- if (constructorAbi && !abiArray.some((e) => e.type === "constructor")) {
193
- abiArray.unshift(constructorAbi); // put it at the top for readability
161
+ if (
162
+ constructorAbi &&
163
+ // biome-ignore lint/suspicious/noExplicitAny: ABI entries have varying shapes
164
+ !abiArray.some((e: any) => e.type === "constructor")
165
+ ) {
166
+ abiArray.unshift(constructorAbi);
194
167
  }
195
168
 
196
169
  const metadata = {
@@ -256,10 +229,94 @@ async function buildStylus(spinner: Ora, secretKey?: string) {
256
229
  }
257
230
  }
258
231
 
259
- function extractContractNamesFromExportAbi(abiRawOutput: string): string[] {
260
- return [...abiRawOutput.matchAll(/<stdin>:(I?[A-Za-z0-9_]+)/g)]
261
- .map((m) => m[1])
262
- .filter((name): name is string => typeof name === "string");
232
+ // biome-ignore lint/suspicious/noExplicitAny: ABI items from parseAbiItem are untyped
233
+ type AbiEntry = any;
234
+ type ParsedInterface = { name: string; abi: AbiEntry[] };
235
+
236
+ function parseSolidityInterfaces(source: string): ParsedInterface[] {
237
+ const results: ParsedInterface[] = [];
238
+
239
+ const ifaceRegex = /interface\s+(I?[A-Za-z0-9_]+)\s*\{([\s\S]*?)\n\}/g;
240
+ for (
241
+ let ifaceMatch = ifaceRegex.exec(source);
242
+ ifaceMatch !== null;
243
+ ifaceMatch = ifaceRegex.exec(source)
244
+ ) {
245
+ const name = ifaceMatch[1] ?? "";
246
+ const body = ifaceMatch[2] ?? "";
247
+ const abi: AbiEntry[] = [];
248
+
249
+ // Build struct lookup: name -> tuple type string
250
+ const structs = new Map<string, string>();
251
+ const structRegex = /struct\s+(\w+)\s*\{([^}]*)\}/g;
252
+ for (
253
+ let structMatch = structRegex.exec(body);
254
+ structMatch !== null;
255
+ structMatch = structRegex.exec(body)
256
+ ) {
257
+ const fields = (structMatch[2] ?? "")
258
+ .split(";")
259
+ .map((f) => f.trim())
260
+ .filter(Boolean)
261
+ .map((f) => f.split(/\s+/)[0] ?? "");
262
+ structs.set(structMatch[1] ?? "", `(${fields.join(",")})`);
263
+ }
264
+
265
+ // Resolve struct references in a type string (iterative for nested structs)
266
+ const resolveStructs = (sig: string): string => {
267
+ let resolved = sig;
268
+ for (let i = 0; i < 10; i++) {
269
+ let changed = false;
270
+ for (const [sName, sTuple] of structs) {
271
+ const re = new RegExp(`\\b${sName}\\b(\\[\\])?`, "g");
272
+ const next = resolved.replace(
273
+ re,
274
+ (_, arr) => `${sTuple}${arr ?? ""}`,
275
+ );
276
+ if (next !== resolved) {
277
+ resolved = next;
278
+ changed = true;
279
+ }
280
+ }
281
+ if (!changed) break;
282
+ }
283
+ return resolved;
284
+ };
285
+
286
+ // Extract each statement (function/error/event) delimited by ;
287
+ const statements = body
288
+ .split(";")
289
+ .map((s) => s.replace(/\n/g, " ").trim())
290
+ .filter(
291
+ (s) =>
292
+ s.startsWith("function ") ||
293
+ s.startsWith("error ") ||
294
+ s.startsWith("event "),
295
+ );
296
+
297
+ for (const stmt of statements) {
298
+ // Strip Solidity qualifiers that abitype doesn't expect
299
+ let cleaned = stmt
300
+ .replace(/\b(external|public|internal|private)\b/g, "")
301
+ .replace(/\b(memory|calldata|storage)\b/g, "")
302
+ .replace(/\s+/g, " ")
303
+ .trim();
304
+
305
+ // Resolve struct type names to tuple types
306
+ cleaned = resolveStructs(cleaned);
307
+
308
+ try {
309
+ const parsed = parseAbiItem(cleaned);
310
+ abi.push(parsed);
311
+ } catch {
312
+ // Skip unparseable items
313
+ }
314
+ }
315
+
316
+ results.push({ abi, name });
317
+ }
318
+
319
+ return results;
263
320
  }
264
321
 
265
322
  function getUrl(hash: string, command: string) {
@@ -8,12 +8,6 @@ export async function createStylusProject() {
8
8
 
9
9
  checkPrerequisites(spinner, "cargo", ["--version"], "Rust (cargo)");
10
10
  checkPrerequisites(spinner, "rustc", ["--version"], "Rust compiler (rustc)");
11
- checkPrerequisites(
12
- spinner,
13
- "solc",
14
- ["--version"],
15
- "Solidity compiler (solc)",
16
- );
17
11
 
18
12
  // Step 1: Ensure cargo is installed
19
13
  const cargoCheck = spawnSync("cargo", ["--version"]);
@@ -24,7 +18,7 @@ export async function createStylusProject() {
24
18
 
25
19
  // Step 2: Install stylus etc.
26
20
  spinner.start("Installing Stylus...");
27
- const install = spawnSync("cargo", ["install", "--force", "cargo-stylus"], {
21
+ const install = spawnSync("cargo", ["install", "cargo-stylus"], {
28
22
  stdio: "inherit",
29
23
  });
30
24
  if (install.status !== 0) {
@@ -33,7 +27,7 @@ export async function createStylusProject() {
33
27
  }
34
28
  spinner.succeed("Stylus installed.");
35
29
 
36
- spawnSync("rustup", ["default", "1.87"], {
30
+ spawnSync("rustup", ["default", "stable"], {
37
31
  stdio: "inherit",
38
32
  });
39
33
  spawnSync("rustup", ["target", "add", "wasm32-unknown-unknown"], {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.119.1";
1
+ export const version = "5.119.3";