@secondlayer/cli 1.2.0 → 1.2.1

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.
package/dist/index.js CHANGED
@@ -1,20 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
2
  var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
3
  var __export = (target, all) => {
19
4
  for (var name in all)
20
5
  __defProp(target, name, {
@@ -81,7 +66,19 @@ var init_format = () => {};
81
66
  // src/core/plugin-manager.ts
82
67
  import { promises as fs } from "fs";
83
68
  import path from "path";
84
- import { validateStacksAddress } from "@stacks/transactions";
69
+ import { isValidAddress as _validateStacksAddress } from "@secondlayer/stacks";
70
+
71
+ // src/utils/contract-id.ts
72
+ function parseContractId(contractId) {
73
+ const dotIndex = contractId.indexOf(".");
74
+ if (dotIndex === -1) {
75
+ throw new Error(`Invalid contract ID: "${contractId}" (expected "address.contractName")`);
76
+ }
77
+ return {
78
+ address: contractId.slice(0, dotIndex),
79
+ contractName: contractId.slice(dotIndex + 1)
80
+ };
81
+ }
85
82
 
86
83
  // src/types/plugin.ts
87
84
  function isClarinetContract(c) {
@@ -92,6 +89,8 @@ function isDirectFileContract(c) {
92
89
  }
93
90
 
94
91
  // src/core/plugin-manager.ts
92
+ var validateStacksAddress = _validateStacksAddress;
93
+
95
94
  class PluginManager {
96
95
  plugins = [];
97
96
  logger;
@@ -153,11 +152,11 @@ class PluginManager {
153
152
  for (let contract of contracts) {
154
153
  if (isClarinetContract(contract) && contract.abi) {
155
154
  const address = typeof contract.address === "string" ? contract.address : "";
156
- const [contractAddress, contractName] = address.split(".");
155
+ const parsed = parseContractId(address);
157
156
  const processed = {
158
- name: contract.name || contractName,
159
- address: contractAddress,
160
- contractName,
157
+ name: contract.name || parsed.contractName,
158
+ address: parsed.address,
159
+ contractName: parsed.contractName,
161
160
  abi: contract.abi,
162
161
  source: "local",
163
162
  metadata: { source: "clarinet" }
@@ -167,11 +166,11 @@ class PluginManager {
167
166
  }
168
167
  if (isDirectFileContract(contract) && contract.abi) {
169
168
  const address = typeof contract.address === "string" ? contract.address : "";
170
- const [contractAddress, contractName] = address.split(".");
169
+ const parsed = parseContractId(address);
171
170
  const processed = {
172
- name: contract.name || contractName,
173
- address: contractAddress,
174
- contractName,
171
+ name: contract.name || parsed.contractName,
172
+ address: parsed.address,
173
+ contractName: parsed.contractName,
175
174
  abi: contract.abi,
176
175
  source: "local",
177
176
  metadata: { source: "direct" }
@@ -199,11 +198,11 @@ class PluginManager {
199
198
  }
200
199
  if (contract.abi) {
201
200
  const addressStr = typeof contract.address === "string" ? contract.address : "";
202
- const [contractAddress, originalContractName] = addressStr.split(".");
201
+ const parsed = parseContractId(addressStr);
203
202
  const processed = {
204
- name: contract.name || originalContractName || "unknown",
205
- address: contractAddress || "unknown",
206
- contractName: originalContractName || contract.name || "unknown",
203
+ name: contract.name || parsed.contractName || "unknown",
204
+ address: parsed.address || "unknown",
205
+ contractName: parsed.contractName || contract.name || "unknown",
207
206
  abi: contract.abi,
208
207
  source: "api",
209
208
  metadata: contract.metadata
@@ -344,11 +343,10 @@ ${JSON.stringify(content, null, 2)}`;
344
343
  return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
345
344
  },
346
345
  validateAddress: (address) => {
347
- return validateStacksAddress(address.split(".")[0]);
346
+ return validateStacksAddress(parseContractId(address).address);
348
347
  },
349
348
  parseContractId: (contractId) => {
350
- const [address, contractName] = contractId.split(".");
351
- return { address, contractName };
349
+ return parseContractId(contractId);
352
350
  },
353
351
  formatCode: async (code) => {
354
352
  const { formatCode: formatCode2 } = await Promise.resolve().then(() => (init_format(), exports_format));
@@ -379,25 +377,25 @@ ${JSON.stringify(content, null, 2)}`;
379
377
  }
380
378
  // src/plugins/clarinet/index.ts
381
379
  import { initSimnet } from "@hirosystems/clarinet-sdk";
382
- import { toCamelCase as toCamelCase3 } from "@secondlayer/clarity-types";
380
+ import { toCamelCase as toCamelCase4 } from "@secondlayer/stacks/clarity";
383
381
 
384
382
  // src/generators/contract.ts
385
383
  init_format();
386
384
  import {
387
- toCamelCase as toCamelCase2
388
- } from "@secondlayer/clarity-types";
385
+ toCamelCase as toCamelCase3
386
+ } from "@secondlayer/stacks/clarity";
389
387
 
390
388
  // src/utils/type-mapping.ts
391
389
  import {
392
390
  toCamelCase,
393
- isClarityList,
394
- isClarityTuple,
395
- isClarityOptional,
396
- isClarityResponse,
397
- isClarityBuffer,
398
- isClarityStringAscii,
399
- isClarityStringUtf8
400
- } from "@secondlayer/clarity-types";
391
+ isAbiList,
392
+ isAbiTuple,
393
+ isAbiOptional,
394
+ isAbiResponse,
395
+ isAbiBuffer,
396
+ isAbiStringAscii,
397
+ isAbiStringUtf8
398
+ } from "@secondlayer/stacks/clarity";
401
399
  function clarityTypeToTS(type) {
402
400
  if (typeof type === "string") {
403
401
  switch (type) {
@@ -427,31 +425,31 @@ function clarityTypeToTS(type) {
427
425
  }
428
426
  }
429
427
  }
430
- if (isClarityBuffer(type)) {
428
+ if (isAbiBuffer(type)) {
431
429
  return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
432
430
  }
433
- if (isClarityStringAscii(type) || isClarityStringUtf8(type)) {
431
+ if (isAbiStringAscii(type) || isAbiStringUtf8(type)) {
434
432
  return "string";
435
433
  }
436
- if (isClarityOptional(type)) {
434
+ if (isAbiOptional(type)) {
437
435
  const innerType = clarityTypeToTS(type.optional);
438
436
  if (innerType.includes(" | ") && !innerType.startsWith("(")) {
439
437
  return `(${innerType}) | null`;
440
438
  }
441
439
  return `${innerType} | null`;
442
440
  }
443
- if (isClarityList(type)) {
441
+ if (isAbiList(type)) {
444
442
  const innerType = clarityTypeToTS(type.list.type);
445
443
  if (innerType.includes(" | ") && !innerType.startsWith("(")) {
446
444
  return `(${innerType})[]`;
447
445
  }
448
446
  return `${innerType}[]`;
449
447
  }
450
- if (isClarityTuple(type)) {
448
+ if (isAbiTuple(type)) {
451
449
  const fields = type.tuple.map((field) => `${toCamelCase(field.name)}: ${clarityTypeToTS(field.type)}`).join("; ");
452
450
  return `{ ${fields} }`;
453
451
  }
454
- if (isClarityResponse(type)) {
452
+ if (isAbiResponse(type)) {
455
453
  const okType = clarityTypeToTS(type.response.ok);
456
454
  const errType = clarityTypeToTS(type.response.error);
457
455
  return `{ ok: ${okType} } | { err: ${errType} }`;
@@ -462,6 +460,145 @@ function getTypeForArg(arg) {
462
460
  return clarityTypeToTS(arg.type);
463
461
  }
464
462
 
463
+ // src/utils/clarity-conversion.ts
464
+ import {
465
+ toCamelCase as toCamelCase2,
466
+ isAbiStringAscii as isAbiStringAscii2,
467
+ isAbiStringUtf8 as isAbiStringUtf82,
468
+ isAbiBuffer as isAbiBuffer2,
469
+ isAbiOptional as isAbiOptional2,
470
+ isAbiList as isAbiList2,
471
+ isAbiTuple as isAbiTuple2,
472
+ isAbiResponse as isAbiResponse2
473
+ } from "@secondlayer/stacks/clarity";
474
+ function generateClarityConversion(argName, argType) {
475
+ const type = argType.type;
476
+ if (typeof type === "string") {
477
+ switch (type) {
478
+ case "uint128":
479
+ return `Cl.uint(${argName})`;
480
+ case "int128":
481
+ return `Cl.int(${argName})`;
482
+ case "bool":
483
+ return `Cl.bool(${argName})`;
484
+ case "principal":
485
+ case "trait_reference":
486
+ return `(() => {
487
+ const [address, contractName] = ${argName}.split(".") as [string, string | undefined];
488
+ if (!validateStacksAddress(address)) {
489
+ throw new Error("Invalid Stacks address format");
490
+ }
491
+ if (contractName !== undefined) {
492
+ if (!CONTRACT_NAME_REGEX.test(contractName)) {
493
+ throw new Error("Invalid contract name format: must start with letter and contain only letters, numbers, and hyphens");
494
+ }
495
+ return Cl.contractPrincipal(address, contractName);
496
+ }
497
+ return Cl.standardPrincipal(${argName});
498
+ })()`;
499
+ default:
500
+ return `${argName}`;
501
+ }
502
+ }
503
+ if (isAbiStringAscii2(type)) {
504
+ return `Cl.stringAscii(${argName})`;
505
+ }
506
+ if (isAbiStringUtf82(type)) {
507
+ return `Cl.stringUtf8(${argName})`;
508
+ }
509
+ if (isAbiBuffer2(type)) {
510
+ return `(() => {
511
+ const value = ${argName};
512
+ if (value instanceof Uint8Array) {
513
+ return Cl.buffer(value);
514
+ }
515
+ if (typeof value === 'object' && value !== null && value.type && value.value) {
516
+ switch (value.type) {
517
+ case 'ascii':
518
+ return Cl.bufferFromAscii(value.value);
519
+ case 'utf8':
520
+ return Cl.bufferFromUtf8(value.value);
521
+ case 'hex':
522
+ return Cl.bufferFromHex(value.value);
523
+ default:
524
+ throw new Error(\`Unsupported buffer type: \${value.type}\`);
525
+ }
526
+ }
527
+ if (typeof value === 'string') {
528
+ if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
529
+ return Cl.bufferFromHex(value);
530
+ }
531
+ const hasNonAscii = value.split('').some(char => char.charCodeAt(0) > 127);
532
+ if (hasNonAscii) {
533
+ return Cl.bufferFromUtf8(value);
534
+ }
535
+ return Cl.bufferFromAscii(value);
536
+ }
537
+ throw new Error(\`Invalid buffer value: \${value}\`);
538
+ })()`;
539
+ }
540
+ if (isAbiOptional2(type)) {
541
+ const innerConversion = generateClarityConversion(argName, {
542
+ type: type.optional
543
+ });
544
+ return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
545
+ }
546
+ if (isAbiList2(type)) {
547
+ const innerConversion = generateClarityConversion("item", {
548
+ type: type.list.type
549
+ });
550
+ const maxLength = type.list.length || 100;
551
+ return `(() => {
552
+ const listValue = ${argName};
553
+ if (listValue.length > ${maxLength}) {
554
+ throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
555
+ }
556
+ return Cl.list(listValue.map(item => ${innerConversion}));
557
+ })()`;
558
+ }
559
+ if (isAbiTuple2(type)) {
560
+ const requiredFields = type.tuple.map((f) => f.name);
561
+ const fieldNames = JSON.stringify(requiredFields);
562
+ const fields = type.tuple.map((field) => {
563
+ const camelFieldName = toCamelCase2(field.name);
564
+ const fieldConversion = generateClarityConversion(`tupleValue.${camelFieldName}`, { type: field.type });
565
+ return `"${field.name}": ${fieldConversion}`;
566
+ }).join(", ");
567
+ return `(() => {
568
+ const tupleValue = ${argName};
569
+ const requiredFields = ${fieldNames};
570
+ for (const fieldName of requiredFields) {
571
+ const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
572
+ if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
573
+ throw new Error(\`Missing tuple field: \${fieldName}\`);
574
+ }
575
+ }
576
+ return Cl.tuple({ ${fields} });
577
+ })()`;
578
+ }
579
+ if (isAbiResponse2(type)) {
580
+ const okConversion = generateClarityConversion(`responseValue.ok`, {
581
+ type: type.response.ok
582
+ });
583
+ const errConversion = generateClarityConversion(`responseValue.err`, {
584
+ type: type.response.error
585
+ });
586
+ return `(() => {
587
+ const responseValue = ${argName};
588
+ const hasOk = 'ok' in responseValue;
589
+ const hasErr = 'err' in responseValue;
590
+ if (hasOk && !hasErr) {
591
+ return Cl.ok(${okConversion});
592
+ }
593
+ if (hasErr && !hasOk) {
594
+ return Cl.error(${errConversion});
595
+ }
596
+ throw new Error("Response must have exactly 'ok' or 'err' property");
597
+ })()`;
598
+ }
599
+ return `${argName}`;
600
+ }
601
+
465
602
  // src/generators/contract.ts
466
603
  function generateNetworkUtils() {
467
604
  return `/**
@@ -502,12 +639,12 @@ function generateValidationUtils() {
502
639
  const CONTRACT_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9\\-]{0,127}$/;`;
503
640
  }
504
641
  async function generateContractInterface(contracts) {
505
- const imports = `import { Cl, validateStacksAddress } from '@stacks/transactions'`;
642
+ const imports = `import { Cl, validateStacksAddress } from '@secondlayer/stacks'`;
506
643
  const header = `/**
507
644
  * Generated by @secondlayer/cli
508
645
  * DO NOT EDIT MANUALLY
509
646
  *
510
- * @requires @stacks/transactions - Install with: npm install @stacks/transactions
647
+ * @requires @secondlayer/stacks - Install with: npm install @secondlayer/stacks
511
648
  */`;
512
649
  const validationUtils = generateValidationUtils();
513
650
  const networkUtils = generateNetworkUtils();
@@ -553,7 +690,7 @@ function generateAbiConstant(name, abi) {
553
690
  return `export const ${name}Abi = ${abiJson} as const`;
554
691
  }
555
692
  function generateMethod(func, address, contractName) {
556
- const methodName = toCamelCase2(func.name);
693
+ const methodName = toCamelCase3(func.name);
557
694
  if (func.args.length === 0) {
558
695
  return `${methodName}() {
559
696
  return {
@@ -566,7 +703,7 @@ function generateMethod(func, address, contractName) {
566
703
  }
567
704
  if (func.args.length === 1) {
568
705
  const originalArgName = func.args[0].name;
569
- const argName = toCamelCase2(originalArgName);
706
+ const argName = toCamelCase3(originalArgName);
570
707
  const argType = getTypeForArg(func.args[0]);
571
708
  const clarityConversion = generateClarityConversion(argName, func.args[0]);
572
709
  return `${methodName}(...args: [{ ${argName}: ${argType} }] | [${argType}]) {
@@ -582,17 +719,17 @@ function generateMethod(func, address, contractName) {
582
719
  }
583
720
  }`;
584
721
  }
585
- const argsList = func.args.map((arg) => toCamelCase2(arg.name)).join(", ");
722
+ const argsList = func.args.map((arg) => toCamelCase3(arg.name)).join(", ");
586
723
  const argsTypes = func.args.map((arg) => {
587
- const camelName = toCamelCase2(arg.name);
724
+ const camelName = toCamelCase3(arg.name);
588
725
  return `${camelName}: ${getTypeForArg(arg)}`;
589
726
  }).join("; ");
590
727
  const argsArray = func.args.map((arg) => {
591
- const argName = toCamelCase2(arg.name);
728
+ const argName = toCamelCase3(arg.name);
592
729
  return generateClarityConversion(argName, arg);
593
730
  }).join(", ");
594
731
  const objectAccess = func.args.map((arg) => {
595
- const camelName = toCamelCase2(arg.name);
732
+ const camelName = toCamelCase3(arg.name);
596
733
  return `args[0].${camelName}`;
597
734
  }).join(", ");
598
735
  const positionTypes = func.args.map((arg) => getTypeForArg(arg)).join(", ");
@@ -609,152 +746,19 @@ function generateMethod(func, address, contractName) {
609
746
  }
610
747
  }`;
611
748
  }
612
- function generateClarityConversion(argName, argType) {
613
- const type = argType.type;
614
- if (typeof type === "string") {
615
- switch (type) {
616
- case "uint128":
617
- return `Cl.uint(${argName})`;
618
- case "int128":
619
- return `Cl.int(${argName})`;
620
- case "bool":
621
- return `Cl.bool(${argName})`;
622
- case "principal":
623
- case "trait_reference":
624
- return `(() => {
625
- const [address, contractName] = ${argName}.split(".") as [string, string | undefined];
626
- if (!validateStacksAddress(address)) {
627
- throw new Error("Invalid Stacks address format");
628
- }
629
- if (contractName !== undefined) {
630
- if (!CONTRACT_NAME_REGEX.test(contractName)) {
631
- throw new Error("Invalid contract name format: must start with letter and contain only letters, numbers, and hyphens");
632
- }
633
- return Cl.contractPrincipal(address, contractName);
634
- }
635
- return Cl.standardPrincipal(${argName});
636
- })()`;
637
- default:
638
- return `${argName}`;
639
- }
640
- }
641
- if (type["string-ascii"]) {
642
- return `Cl.stringAscii(${argName})`;
643
- }
644
- if (type["string-utf8"]) {
645
- return `Cl.stringUtf8(${argName})`;
646
- }
647
- if (type.buff) {
648
- return `(() => {
649
- const value = ${argName};
650
- // Direct Uint8Array
651
- if (value instanceof Uint8Array) {
652
- return Cl.buffer(value);
653
- }
654
- // Object notation with explicit type
655
- if (typeof value === 'object' && value !== null && value.type && value.value) {
656
- switch (value.type) {
657
- case 'ascii':
658
- return Cl.bufferFromAscii(value.value);
659
- case 'utf8':
660
- return Cl.bufferFromUtf8(value.value);
661
- case 'hex':
662
- return Cl.bufferFromHex(value.value);
663
- default:
664
- throw new Error(\`Unsupported buffer type: \${value.type}\`);
665
- }
666
- }
667
- // Auto-detect string type
668
- if (typeof value === 'string') {
669
- // Check for hex (0x prefix or pure hex pattern)
670
- if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
671
- return Cl.bufferFromHex(value);
672
- }
673
- // Check for non-ASCII characters (UTF-8) using char code comparison
674
- const hasNonAscii = value.split('').some(char => char.charCodeAt(0) > 127);
675
- if (hasNonAscii) {
676
- return Cl.bufferFromUtf8(value);
677
- }
678
- // Default to ASCII for simple ASCII strings
679
- return Cl.bufferFromAscii(value);
680
- }
681
- throw new Error(\`Invalid buffer value: \${value}\`);
682
- })()`;
683
- }
684
- if (type.optional) {
685
- const innerConversion = generateClarityConversion(argName, {
686
- type: type.optional
687
- });
688
- return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
689
- }
690
- if (type.list) {
691
- const innerConversion = generateClarityConversion("item", {
692
- type: type.list.type
693
- });
694
- const maxLength = type.list.length || 100;
695
- return `(() => {
696
- const listValue = ${argName};
697
- if (listValue.length > ${maxLength}) {
698
- throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
699
- }
700
- return Cl.list(listValue.map(item => ${innerConversion}));
701
- })()`;
702
- }
703
- if (type.tuple) {
704
- const requiredFields = type.tuple.map((f) => f.name);
705
- const fieldNames = JSON.stringify(requiredFields);
706
- const fields = type.tuple.map((field) => {
707
- const camelFieldName = toCamelCase2(field.name);
708
- const fieldConversion = generateClarityConversion(`tupleValue.${camelFieldName}`, { type: field.type });
709
- return `"${field.name}": ${fieldConversion}`;
710
- }).join(", ");
711
- return `(() => {
712
- const tupleValue = ${argName};
713
- const requiredFields = ${fieldNames};
714
- for (const fieldName of requiredFields) {
715
- const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
716
- if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
717
- throw new Error(\`Missing tuple field: \${fieldName}\`);
718
- }
719
- }
720
- return Cl.tuple({ ${fields} });
721
- })()`;
722
- }
723
- if (type.response) {
724
- const okConversion = generateClarityConversion(`responseValue.ok`, {
725
- type: type.response.ok
726
- });
727
- const errConversion = generateClarityConversion(`responseValue.err`, {
728
- type: type.response.error
729
- });
730
- return `(() => {
731
- const responseValue = ${argName};
732
- const hasOk = 'ok' in responseValue;
733
- const hasErr = 'err' in responseValue;
734
- if (hasOk && !hasErr) {
735
- return Cl.ok(${okConversion});
736
- }
737
- if (hasErr && !hasOk) {
738
- return Cl.error(${errConversion});
739
- }
740
- throw new Error("Response must have exactly 'ok' or 'err' property");
741
- })()`;
742
- }
743
- return `${argName}`;
744
- }
745
749
  function generateMapsObject(maps, address, contractName) {
746
750
  if (!maps || maps.length === 0) {
747
751
  return "";
748
752
  }
749
753
  const mapMethods = maps.map((map) => {
750
- const methodName = toCamelCase2(map.name);
754
+ const methodName = toCamelCase3(map.name);
751
755
  const keyType = getTypeForArg({ type: map.key });
752
756
  const valueType = getTypeForArg({ type: map.value });
753
757
  const keyConversion = generateMapKeyConversion(map.key);
754
758
  return `${methodName}: {
755
759
  async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
756
760
  try {
757
- const { cvToJSON, serializeCV } = await import('@stacks/transactions');
761
+ const { cvToJSON, serializeCV } = await import('@secondlayer/stacks/clarity');
758
762
  const baseUrl = getApiUrl('${address}', options?.network);
759
763
  const mapKey = ${keyConversion};
760
764
  const keyHex = serializeCV(mapKey).toString('hex');
@@ -777,7 +781,7 @@ function generateMapsObject(maps, address, contractName) {
777
781
  return null; // none value
778
782
  }
779
783
 
780
- const { deserializeCV } = await import('@stacks/transactions');
784
+ const { deserializeCV } = await import('@secondlayer/stacks/clarity');
781
785
  const cv = deserializeCV(result.data);
782
786
  const parsed = cvToJSON(cv);
783
787
  // Unwrap the (some ...) wrapper
@@ -808,12 +812,12 @@ function generateVarsObject(variables, address, contractName) {
808
812
  return "";
809
813
  }
810
814
  const varMethods = dataVars.map((variable) => {
811
- const methodName = toCamelCase2(variable.name);
815
+ const methodName = toCamelCase3(variable.name);
812
816
  const valueType = getTypeForArg({ type: variable.type });
813
817
  return `${methodName}: {
814
818
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
815
819
  try {
816
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
820
+ const { cvToJSON, deserializeCV } = await import('@secondlayer/stacks/clarity');
817
821
  const baseUrl = getApiUrl('${address}', options?.network);
818
822
 
819
823
  const response = await fetch(
@@ -853,12 +857,12 @@ function generateConstantsObject(variables, address, contractName) {
853
857
  return "";
854
858
  }
855
859
  const constMethods = constants.map((constant) => {
856
- const methodName = toCamelCase2(constant.name);
860
+ const methodName = toCamelCase3(constant.name);
857
861
  const valueType = getTypeForArg({ type: constant.type });
858
862
  return `${methodName}: {
859
863
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
860
864
  try {
861
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
865
+ const { cvToJSON, deserializeCV } = await import('@secondlayer/stacks/clarity');
862
866
  const baseUrl = getApiUrl('${address}', options?.network);
863
867
 
864
868
  const response = await fetch(
@@ -892,7 +896,7 @@ function generateConstantsObject(variables, address, contractName) {
892
896
  function generateMapKeyConversion(keyType) {
893
897
  if (keyType.tuple) {
894
898
  const fields = keyType.tuple.map((field) => {
895
- const camelFieldName = toCamelCase2(field.name);
899
+ const camelFieldName = toCamelCase3(field.name);
896
900
  const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
897
901
  return `"${field.name}": ${fieldConversion}`;
898
902
  }).join(", ");
@@ -901,14 +905,167 @@ function generateMapKeyConversion(keyType) {
901
905
  return generateClarityConversion("key", { type: keyType });
902
906
  }
903
907
 
904
- // src/plugins/clarinet/index.ts
905
- function sanitizeContractName(name) {
906
- return toCamelCase3(name);
908
+ // src/utils/abi-compat.ts
909
+ function normalizeAccess(access) {
910
+ if (access === "read_only")
911
+ return "read-only";
912
+ return access;
907
913
  }
908
- async function isUserDefinedContract(contractId, manifestPath) {
909
- const [address, contractName] = contractId.split(".");
910
- try {
911
- const { promises: fs2 } = await import("fs");
914
+ function normalizeType(type) {
915
+ if (typeof type === "string") {
916
+ switch (type) {
917
+ case "uint128":
918
+ case "int128":
919
+ case "bool":
920
+ case "principal":
921
+ case "trait_reference":
922
+ return type;
923
+ default:
924
+ return type;
925
+ }
926
+ }
927
+ if (typeof type !== "object" || type === null) {
928
+ throw new Error(`Invalid ABI type: expected object, got ${typeof type}`);
929
+ }
930
+ const typeObj = type;
931
+ if ("buffer" in typeObj) {
932
+ const buffer = typeObj.buffer;
933
+ return {
934
+ buff: {
935
+ length: buffer?.length ?? 32
936
+ }
937
+ };
938
+ }
939
+ if ("buff" in typeObj) {
940
+ const buff = typeObj.buff;
941
+ return {
942
+ buff: {
943
+ length: buff?.length ?? 32
944
+ }
945
+ };
946
+ }
947
+ if ("string-ascii" in typeObj) {
948
+ const strAscii = typeObj["string-ascii"];
949
+ return {
950
+ "string-ascii": {
951
+ length: strAscii?.length ?? 256
952
+ }
953
+ };
954
+ }
955
+ if ("string-utf8" in typeObj) {
956
+ const strUtf8 = typeObj["string-utf8"];
957
+ return {
958
+ "string-utf8": {
959
+ length: strUtf8?.length ?? 256
960
+ }
961
+ };
962
+ }
963
+ if ("response" in typeObj) {
964
+ const response = typeObj.response;
965
+ return {
966
+ response: {
967
+ ok: normalizeType(response?.ok ?? "bool"),
968
+ error: normalizeType(response?.error ?? "uint128")
969
+ }
970
+ };
971
+ }
972
+ if ("optional" in typeObj) {
973
+ return {
974
+ optional: normalizeType(typeObj.optional)
975
+ };
976
+ }
977
+ if ("list" in typeObj) {
978
+ const list = typeObj.list;
979
+ return {
980
+ list: {
981
+ type: normalizeType(list?.type ?? "uint128"),
982
+ length: list?.length ?? 100
983
+ }
984
+ };
985
+ }
986
+ if ("tuple" in typeObj) {
987
+ const tuple = typeObj.tuple;
988
+ return {
989
+ tuple: tuple.map((field) => ({
990
+ name: field.name,
991
+ type: normalizeType(field.type)
992
+ }))
993
+ };
994
+ }
995
+ throw new Error(`Unknown ABI type structure: ${JSON.stringify(type)}`);
996
+ }
997
+ function normalizeFunction(func) {
998
+ const access = normalizeAccess(func.access);
999
+ const args = func.args ?? [];
1000
+ const outputs = func.outputs;
1001
+ return {
1002
+ name: func.name,
1003
+ access,
1004
+ args: args.map((arg) => ({
1005
+ name: arg.name,
1006
+ type: normalizeType(arg.type)
1007
+ })),
1008
+ outputs: normalizeType(typeof outputs === "object" && outputs !== null && "type" in outputs ? outputs.type : outputs)
1009
+ };
1010
+ }
1011
+ function normalizeMap(map) {
1012
+ return {
1013
+ name: map.name,
1014
+ key: normalizeType(map.key),
1015
+ value: normalizeType(map.value)
1016
+ };
1017
+ }
1018
+ function normalizeVariable(variable) {
1019
+ return {
1020
+ name: variable.name,
1021
+ type: normalizeType(variable.type),
1022
+ access: variable.access
1023
+ };
1024
+ }
1025
+ function normalizeAbi(abi) {
1026
+ if (typeof abi !== "object" || abi === null) {
1027
+ return { functions: [] };
1028
+ }
1029
+ const abiObj = abi;
1030
+ const functions = [];
1031
+ const maps = [];
1032
+ const variables = [];
1033
+ if (Array.isArray(abiObj.functions)) {
1034
+ for (const func of abiObj.functions) {
1035
+ if (typeof func === "object" && func !== null) {
1036
+ functions.push(normalizeFunction(func));
1037
+ }
1038
+ }
1039
+ }
1040
+ if (Array.isArray(abiObj.maps)) {
1041
+ for (const map of abiObj.maps) {
1042
+ if (typeof map === "object" && map !== null) {
1043
+ maps.push(normalizeMap(map));
1044
+ }
1045
+ }
1046
+ }
1047
+ if (Array.isArray(abiObj.variables)) {
1048
+ for (const variable of abiObj.variables) {
1049
+ if (typeof variable === "object" && variable !== null) {
1050
+ variables.push(normalizeVariable(variable));
1051
+ }
1052
+ }
1053
+ }
1054
+ return {
1055
+ functions,
1056
+ maps: maps.length > 0 ? maps : undefined,
1057
+ variables: variables.length > 0 ? variables : undefined
1058
+ };
1059
+ }
1060
+
1061
+ // src/plugins/clarinet/index.ts
1062
+ function sanitizeContractName(name) {
1063
+ return toCamelCase4(name);
1064
+ }
1065
+ async function isUserDefinedContract(contractId, manifestPath) {
1066
+ const { address, contractName } = parseContractId(contractId);
1067
+ try {
1068
+ const { promises: fs2 } = await import("fs");
912
1069
  const tomlContent = await fs2.readFile(manifestPath, "utf-8");
913
1070
  const contractSectionRegex = /^\[contracts\.([^\]]+)\]/gm;
914
1071
  const userContracts = new Set;
@@ -950,7 +1107,7 @@ var clarinet = (options = {}) => {
950
1107
  const contractInterfaces = simnet.getContractsInterfaces();
951
1108
  const contracts = [];
952
1109
  for (const [contractId, abi] of contractInterfaces) {
953
- const [_, contractName] = contractId.split(".");
1110
+ const { contractName } = parseContractId(contractId);
954
1111
  if (!await isUserDefinedContract(contractId, manifestPath)) {
955
1112
  if (options.debug) {
956
1113
  console.log(`\uD83D\uDEAB Skipping system contract: ${contractId}`);
@@ -967,7 +1124,7 @@ var clarinet = (options = {}) => {
967
1124
  contracts.push({
968
1125
  name: sanitizedName,
969
1126
  address: contractId,
970
- abi,
1127
+ abi: normalizeAbi(abi),
971
1128
  _clarinetSource: true
972
1129
  });
973
1130
  }
@@ -1013,155 +1170,33 @@ async function hasClarinetProject(path2 = "./Clarinet.toml") {
1013
1170
  }
1014
1171
  }
1015
1172
  // src/plugins/actions/generators.ts
1016
- import { toCamelCase as toCamelCase4 } from "@secondlayer/clarity-types";
1173
+ import { toCamelCase as toCamelCase6 } from "@secondlayer/stacks/clarity";
1174
+
1175
+ // src/utils/generator-helpers.ts
1176
+ import { toCamelCase as toCamelCase5 } from "@secondlayer/stacks/clarity";
1017
1177
  function generateArgsSignature(args) {
1018
1178
  if (args.length === 0)
1019
1179
  return "";
1020
1180
  const argsTypes = args.map((arg) => {
1021
- const camelName = toCamelCase4(arg.name);
1181
+ const camelName = toCamelCase5(arg.name);
1022
1182
  return `${camelName}: ${getTypeForArg(arg)}`;
1023
1183
  }).join("; ");
1024
1184
  return `args: { ${argsTypes} }, `;
1025
1185
  }
1026
- function generateClarityArgs(args, _contractName) {
1186
+ function generateClarityArgs(args) {
1027
1187
  if (args.length === 0)
1028
1188
  return "";
1029
1189
  return args.map((arg) => {
1030
- const argName = `args.${toCamelCase4(arg.name)}`;
1031
- return generateClarityConversion2(argName, arg);
1190
+ const argName = `args.${toCamelCase5(arg.name)}`;
1191
+ return generateClarityConversion(argName, arg);
1032
1192
  }).join(", ");
1033
1193
  }
1034
- function generateClarityConversion2(argName, argType) {
1035
- const type = argType.type;
1036
- if (typeof type === "string") {
1037
- switch (type) {
1038
- case "uint128":
1039
- return `Cl.uint(${argName})`;
1040
- case "int128":
1041
- return `Cl.int(${argName})`;
1042
- case "bool":
1043
- return `Cl.bool(${argName})`;
1044
- case "principal":
1045
- case "trait_reference":
1046
- return `(() => {
1047
- const [address, contractName] = ${argName}.split(".") as [string, string | undefined];
1048
- if (!validateStacksAddress(address)) {
1049
- throw new Error("Invalid Stacks address format");
1050
- }
1051
- if (contractName !== undefined) {
1052
- if (!CONTRACT_NAME_REGEX.test(contractName)) {
1053
- throw new Error("Invalid contract name format: must start with letter and contain only letters, numbers, and hyphens");
1054
- }
1055
- return Cl.contractPrincipal(address, contractName);
1056
- }
1057
- return Cl.standardPrincipal(${argName});
1058
- })()`;
1059
- default:
1060
- return `${argName}`;
1061
- }
1062
- }
1063
- if (type["string-ascii"]) {
1064
- return `Cl.stringAscii(${argName})`;
1065
- }
1066
- if (type["string-utf8"]) {
1067
- return `Cl.stringUtf8(${argName})`;
1068
- }
1069
- if (type.buff) {
1070
- return `(() => {
1071
- const value = ${argName};
1072
- if (value instanceof Uint8Array) {
1073
- return Cl.buffer(value);
1074
- }
1075
- if (typeof value === 'object' && value !== null && value.type && value.value) {
1076
- switch (value.type) {
1077
- case 'ascii':
1078
- return Cl.bufferFromAscii(value.value);
1079
- case 'utf8':
1080
- return Cl.bufferFromUtf8(value.value);
1081
- case 'hex':
1082
- return Cl.bufferFromHex(value.value);
1083
- default:
1084
- throw new Error(\`Unsupported buffer type: \${value.type}\`);
1085
- }
1086
- }
1087
- if (typeof value === 'string') {
1088
- if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
1089
- return Cl.bufferFromHex(value);
1090
- }
1091
- const hasNonAscii = value.split('').some(char => char.charCodeAt(0) > 127);
1092
- if (hasNonAscii) {
1093
- return Cl.bufferFromUtf8(value);
1094
- }
1095
- return Cl.bufferFromAscii(value);
1096
- }
1097
- throw new Error(\`Invalid buffer value: \${value}\`);
1098
- })()`;
1099
- }
1100
- if (type.optional) {
1101
- const innerConversion = generateClarityConversion2(argName, {
1102
- type: type.optional
1103
- });
1104
- return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
1105
- }
1106
- if (type.list) {
1107
- const innerConversion = generateClarityConversion2("item", {
1108
- type: type.list.type
1109
- });
1110
- const maxLength = type.list.length || 100;
1111
- return `(() => {
1112
- const listValue = ${argName};
1113
- if (listValue.length > ${maxLength}) {
1114
- throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
1115
- }
1116
- return Cl.list(listValue.map(item => ${innerConversion}));
1117
- })()`;
1118
- }
1119
- if (type.tuple) {
1120
- const requiredFields = type.tuple.map((f) => f.name);
1121
- const fieldNames = JSON.stringify(requiredFields);
1122
- const fields = type.tuple.map((field) => {
1123
- const camelFieldName = toCamelCase4(field.name);
1124
- const fieldConversion = generateClarityConversion2(`tupleValue.${camelFieldName}`, { type: field.type });
1125
- return `"${field.name}": ${fieldConversion}`;
1126
- }).join(", ");
1127
- return `(() => {
1128
- const tupleValue = ${argName};
1129
- const requiredFields = ${fieldNames};
1130
- for (const fieldName of requiredFields) {
1131
- const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
1132
- if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
1133
- throw new Error(\`Missing tuple field: \${fieldName}\`);
1134
- }
1135
- }
1136
- return Cl.tuple({ ${fields} });
1137
- })()`;
1138
- }
1139
- if (type.response) {
1140
- const okConversion = generateClarityConversion2(`responseValue.ok`, {
1141
- type: type.response.ok
1142
- });
1143
- const errConversion = generateClarityConversion2(`responseValue.err`, {
1144
- type: type.response.error
1145
- });
1146
- return `(() => {
1147
- const responseValue = ${argName};
1148
- const hasOk = 'ok' in responseValue;
1149
- const hasErr = 'err' in responseValue;
1150
- if (hasOk && !hasErr) {
1151
- return Cl.ok(${okConversion});
1152
- }
1153
- if (hasErr && !hasOk) {
1154
- return Cl.error(${errConversion});
1155
- }
1156
- throw new Error("Response must have exactly 'ok' or 'err' property");
1157
- })()`;
1158
- }
1159
- return `${argName}`;
1160
- }
1194
+
1195
+ // src/plugins/actions/generators.ts
1161
1196
  function generateReadHelpers(contract, options) {
1162
- const { abi, name } = contract;
1197
+ const { abi } = contract;
1163
1198
  const functions = abi.functions || [];
1164
- const readOnlyFunctions = functions.filter((f) => f.access === "read_only" || f.access === "read-only");
1199
+ const readOnlyFunctions = functions.filter((f) => f.access === "read-only");
1165
1200
  if (readOnlyFunctions.length === 0) {
1166
1201
  return "";
1167
1202
  }
@@ -1178,9 +1213,9 @@ function generateReadHelpers(contract, options) {
1178
1213
  return "";
1179
1214
  }
1180
1215
  const helpers = filteredFunctions.map((func) => {
1181
- const methodName = toCamelCase4(func.name);
1216
+ const methodName = toCamelCase6(func.name);
1182
1217
  const argsSignature = generateArgsSignature(func.args);
1183
- const clarityArgs = generateClarityArgs(func.args, name);
1218
+ const clarityArgs = generateClarityArgs(func.args);
1184
1219
  return `async ${methodName}(${argsSignature}options?: {
1185
1220
  network?: 'mainnet' | 'testnet' | 'devnet';
1186
1221
  senderAddress?: string;
@@ -1202,7 +1237,7 @@ function generateReadHelpers(contract, options) {
1202
1237
  }`;
1203
1238
  }
1204
1239
  function generateWriteHelpers(contract, options) {
1205
- const { abi, name } = contract;
1240
+ const { abi } = contract;
1206
1241
  const functions = abi.functions || [];
1207
1242
  const envVarName = options.senderKeyEnv ?? "STX_SENDER_KEY";
1208
1243
  const publicFunctions = functions.filter((f) => f.access === "public");
@@ -1222,9 +1257,9 @@ function generateWriteHelpers(contract, options) {
1222
1257
  return "";
1223
1258
  }
1224
1259
  const helpers = filteredFunctions.map((func) => {
1225
- const methodName = toCamelCase4(func.name);
1260
+ const methodName = toCamelCase6(func.name);
1226
1261
  const argsSignature = generateArgsSignature(func.args);
1227
- const clarityArgs = generateClarityArgs(func.args, name);
1262
+ const clarityArgs = generateClarityArgs(func.args);
1228
1263
  return `async ${methodName}(${argsSignature}senderKey?: string, options?: {
1229
1264
  network?: 'mainnet' | 'testnet' | 'devnet';
1230
1265
  fee?: string | number | undefined;
@@ -1318,28 +1353,19 @@ var actions = (options = {}) => {
1318
1353
  };
1319
1354
  };
1320
1355
  function addRequiredImports(content) {
1321
- const transactionsImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]@stacks\/transactions['"];/;
1322
- const match = content.match(transactionsImportRegex);
1356
+ const stacksImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]@secondlayer\/stacks['"];/;
1357
+ const match = content.match(stacksImportRegex);
1323
1358
  if (match) {
1324
- const existingImports = match[1].trim();
1325
- const requiredImports = [
1326
- "fetchCallReadOnlyFunction",
1327
- "makeContractCall"
1328
- ];
1329
- const newImports = requiredImports.filter((imp) => !existingImports.includes(imp)).join(", ");
1330
- if (newImports) {
1331
- const updatedImport = `import { ${existingImports}, ${newImports} } from '@stacks/transactions';`;
1332
- let updatedContent = content.replace(transactionsImportRegex, updatedImport);
1333
- if (!updatedContent.includes("type PostCondition")) {
1334
- updatedContent = updatedContent.replace(updatedImport, `${updatedImport}
1335
- import type { PostCondition } from '@stacks/transactions';`);
1336
- }
1337
- return updatedContent;
1359
+ let updatedContent = content;
1360
+ if (!updatedContent.includes("fetchCallReadOnlyFunction")) {
1361
+ updatedContent = updatedContent.replace(stacksImportRegex, `${match[0]}
1362
+ import { fetchCallReadOnlyFunction, makeContractCall } from '@secondlayer/stacks/clarity';`);
1338
1363
  }
1339
- if (!content.includes("type PostCondition")) {
1340
- return content.replace(transactionsImportRegex, `${match[0]}
1341
- import type { PostCondition } from '@stacks/transactions';`);
1364
+ if (!updatedContent.includes("type PostCondition")) {
1365
+ updatedContent = updatedContent.replace(stacksImportRegex, `${match[0]}
1366
+ import type { PostCondition } from '@secondlayer/stacks';`);
1342
1367
  }
1368
+ return updatedContent;
1343
1369
  }
1344
1370
  return content;
1345
1371
  }
@@ -1455,268 +1481,32 @@ export function useSecondLayerConfig(): SecondLayerReactConfig {
1455
1481
 
1456
1482
  // src/plugins/react/generators/generic.ts
1457
1483
  init_format();
1458
- var GENERIC_HOOKS = [
1459
- "useAccount",
1460
- "useConnect",
1461
- "useDisconnect",
1462
- "useNetwork",
1463
- "useContract",
1464
- "useOpenSTXTransfer",
1465
- "useSignMessage",
1466
- "useDeployContract",
1467
- "useReadContract",
1468
- "useTransaction",
1469
- "useBlock",
1470
- "useAccountTransactions",
1471
- "useWaitForTransaction"
1472
- ];
1473
- async function generateGenericHooks(excludeList = []) {
1474
- const hooksToGenerate = GENERIC_HOOKS.filter((hookName) => !excludeList.includes(hookName));
1475
- const imports = `import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
1476
- import { useState, useCallback } from 'react'
1477
- import { useSecondLayerConfig } from './provider'
1478
- import { connect, disconnect, isConnected, request, openContractCall as stacksOpenContractCall, openSTXTransfer, openSignatureRequestPopup, openContractDeploy } from '@stacks/connect'
1479
- import { Cl, validateStacksAddress } from '@stacks/transactions'
1480
- import type { PostCondition } from '@stacks/transactions'
1481
- import type { ExtractFunctionArgs, ExtractFunctionNames, ClarityContract } from '@secondlayer/clarity-types'
1482
1484
 
1483
- const API_URLS: Record<string, string> = {
1484
- mainnet: 'https://api.hiro.so',
1485
- testnet: 'https://api.testnet.hiro.so',
1486
- devnet: 'http://localhost:3999'
1487
- }
1485
+ // src/generators/templates/use-contract.ts
1486
+ var USE_CONTRACT_TEMPLATE = `export function useContract() {
1487
+ const config = useSecondLayerConfig()
1488
+ const queryClient = useQueryClient()
1489
+ const [isRequestPending, setIsRequestPending] = useState(false)
1488
1490
 
1489
- async function fetchTransaction({ txId, network, apiUrl }: { txId: string; network?: string; apiUrl?: string }): Promise<any> {
1490
- const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1491
- const response = await fetch(\`\${baseUrl}/extended/v1/tx/\${txId}\`)
1492
- if (!response.ok) throw new Error(\`Failed to fetch transaction: \${response.statusText}\`)
1493
- return response.json()
1494
- }
1491
+ // Helper function to convert JS values to Clarity values based on ABI
1492
+ const convertArgsWithAbi = (args: any, abiArgs: any[]): any[] => {
1493
+ if (!abiArgs || abiArgs.length === 0) return []
1495
1494
 
1496
- async function fetchBlock({ height, network, apiUrl }: { height: number; network?: string; apiUrl?: string }): Promise<any> {
1497
- const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1498
- const response = await fetch(\`\${baseUrl}/extended/v1/block/by_height/\${height}\`)
1499
- if (!response.ok) throw new Error(\`Failed to fetch block: \${response.statusText}\`)
1500
- return response.json()
1501
- }
1495
+ return abiArgs.map((abiArg, index) => {
1496
+ const argValue = Array.isArray(args)
1497
+ ? args[index]
1498
+ : args[abiArg.name] || args[abiArg.name.replace(/-/g, '').replace(/_/g, '')]
1499
+ return convertJSValueToClarityValue(argValue, abiArg.type)
1500
+ })
1501
+ }
1502
1502
 
1503
- async function fetchAccountTransactions({ address, network, apiUrl }: { address: string; network?: string; apiUrl?: string }): Promise<any> {
1504
- const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1505
- const response = await fetch(\`\${baseUrl}/extended/v1/address/\${address}/transactions\`)
1506
- if (!response.ok) throw new Error(\`Failed to fetch transactions: \${response.statusText}\`)
1507
- return response.json()
1508
- }`;
1509
- const header = `/**
1510
- * Generated generic Stacks React hooks
1511
- * DO NOT EDIT MANUALLY
1512
- */`;
1513
- const hooksCode = hooksToGenerate.map((hookName) => generateGenericHook(hookName)).filter(Boolean).join(`
1503
+ // Helper function to convert buffer values with auto-detection
1504
+ const convertBufferValue = (value: any): any => {
1505
+ // Direct Uint8Array
1506
+ if (value instanceof Uint8Array) {
1507
+ return Cl.buffer(value)
1508
+ }
1514
1509
 
1515
- `);
1516
- const code = `${imports}
1517
-
1518
- ${header}
1519
-
1520
- ${hooksCode}`;
1521
- return formatCode(code);
1522
- }
1523
- function generateGenericHook(hookName) {
1524
- switch (hookName) {
1525
- case "useAccount":
1526
- return `export function useAccount() {
1527
- const config = useSecondLayerConfig()
1528
-
1529
- return useQuery({
1530
- queryKey: ['stacks-account', config.network],
1531
- queryFn: async () => {
1532
- try {
1533
- // Check if already connected using @stacks/connect v8
1534
- const connected = isConnected()
1535
-
1536
- if (!connected) {
1537
- return {
1538
- address: undefined,
1539
- addresses: undefined,
1540
- isConnected: false,
1541
- isConnecting: false,
1542
- isDisconnected: true,
1543
- status: 'disconnected' as const
1544
- }
1545
- }
1546
-
1547
- // Get addresses using @stacks/connect v8 request method (SIP-030)
1548
- const result = await request('stx_getAddresses')
1549
-
1550
- if (!result || !result.addresses || result.addresses.length === 0) {
1551
- return {
1552
- address: undefined,
1553
- addresses: undefined,
1554
- isConnected: false,
1555
- isConnecting: false,
1556
- isDisconnected: true,
1557
- status: 'disconnected' as const
1558
- }
1559
- }
1560
-
1561
- // Extract STX addresses from the response
1562
- const stxAddresses = result.addresses
1563
- .filter((addr: any) => addr.address.startsWith('SP') || addr.address.startsWith('ST'))
1564
- .map((addr: any) => addr.address)
1565
-
1566
- return {
1567
- address: stxAddresses[0] || undefined,
1568
- addresses: stxAddresses,
1569
- isConnected: true,
1570
- isConnecting: false,
1571
- isDisconnected: false,
1572
- status: 'connected' as const
1573
- }
1574
- } catch (error) {
1575
- // Handle case where wallet is not available or user rejected
1576
- return {
1577
- address: undefined,
1578
- addresses: undefined,
1579
- isConnected: false,
1580
- isConnecting: false,
1581
- isDisconnected: true,
1582
- status: 'disconnected' as const
1583
- }
1584
- }
1585
- },
1586
- refetchOnWindowFocus: false,
1587
- retry: false,
1588
- staleTime: 1000 * 60 * 5, // 5 minutes
1589
- refetchInterval: 1000 * 30, // Refetch every 30 seconds to detect wallet changes
1590
- })
1591
- }`;
1592
- case "useConnect":
1593
- return `export function useConnect() {
1594
- const queryClient = useQueryClient()
1595
-
1596
- const mutation = useMutation({
1597
- mutationFn: async (options: { forceWalletSelect?: boolean } = {}) => {
1598
- // Use @stacks/connect v8 connect method
1599
- return await connect(options)
1600
- },
1601
- onSuccess: () => {
1602
- // Invalidate account queries to refetch connection state
1603
- queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
1604
- },
1605
- onError: (error) => {
1606
- console.error('Connection failed:', error)
1607
- }
1608
- })
1609
-
1610
- return {
1611
- // Custom connect function that works without arguments
1612
- connect: (options?: { forceWalletSelect?: boolean }) => {
1613
- return mutation.mutate(options || {})
1614
- },
1615
- connectAsync: async (options?: { forceWalletSelect?: boolean }) => {
1616
- return mutation.mutateAsync(options || {})
1617
- },
1618
- // Expose all the mutation state
1619
- isPending: mutation.isPending,
1620
- isError: mutation.isError,
1621
- isSuccess: mutation.isSuccess,
1622
- error: mutation.error,
1623
- data: mutation.data,
1624
- reset: mutation.reset,
1625
- // Keep the original mutate/mutateAsync for advanced users
1626
- mutate: mutation.mutate,
1627
- mutateAsync: mutation.mutateAsync
1628
- }
1629
- }`;
1630
- case "useDisconnect":
1631
- return `export function useDisconnect() {
1632
- const queryClient = useQueryClient()
1633
-
1634
- const mutation = useMutation({
1635
- mutationFn: async () => {
1636
- // Use @stacks/connect v8 disconnect method
1637
- return await disconnect()
1638
- },
1639
- onSuccess: () => {
1640
- // Clear all cached data on disconnect
1641
- queryClient.clear()
1642
- },
1643
- onError: (error) => {
1644
- console.error('Disconnect failed:', error)
1645
- }
1646
- })
1647
-
1648
- return {
1649
- // Custom disconnect function
1650
- disconnect: () => {
1651
- return mutation.mutate()
1652
- },
1653
- disconnectAsync: async () => {
1654
- return mutation.mutateAsync()
1655
- },
1656
- // Expose all the mutation state
1657
- isPending: mutation.isPending,
1658
- isError: mutation.isError,
1659
- isSuccess: mutation.isSuccess,
1660
- error: mutation.error,
1661
- data: mutation.data,
1662
- reset: mutation.reset,
1663
- // Keep the original mutate/mutateAsync for advanced users
1664
- mutate: mutation.mutate,
1665
- mutateAsync: mutation.mutateAsync
1666
- }
1667
- }`;
1668
- case "useNetwork":
1669
- return `export function useNetwork() {
1670
- const config = useSecondLayerConfig()
1671
-
1672
- return useQuery({
1673
- queryKey: ['stacks-network', config.network],
1674
- queryFn: async () => {
1675
- // Currently read-only from config
1676
- // Future: Use request('stx_getNetworks') when wallet support improves
1677
- const network = config.network
1678
-
1679
- return {
1680
- network,
1681
- isMainnet: network === 'mainnet',
1682
- isTestnet: network === 'testnet',
1683
- isDevnet: network === 'devnet',
1684
- // Future: Add switchNetwork when wallets support stx_networkChange
1685
- // switchNetwork: async (newNetwork: string) => {
1686
- // return await request('wallet_changeNetwork', { network: newNetwork })
1687
- // }
1688
- }
1689
- },
1690
- staleTime: Infinity, // Network config rarely changes
1691
- refetchOnWindowFocus: false,
1692
- retry: false
1693
- })
1694
- }`;
1695
- case "useContract":
1696
- return `export function useContract() {
1697
- const config = useSecondLayerConfig()
1698
- const queryClient = useQueryClient()
1699
- const [isRequestPending, setIsRequestPending] = useState(false)
1700
-
1701
- // Helper function to convert JS values to Clarity values based on ABI
1702
- const convertArgsWithAbi = (args: any, abiArgs: any[]): any[] => {
1703
- if (!abiArgs || abiArgs.length === 0) return []
1704
-
1705
- return abiArgs.map((abiArg, index) => {
1706
- const argValue = Array.isArray(args)
1707
- ? args[index]
1708
- : args[abiArg.name] || args[abiArg.name.replace(/-/g, '').replace(/_/g, '')]
1709
- return convertJSValueToClarityValue(argValue, abiArg.type)
1710
- })
1711
- }
1712
-
1713
- // Helper function to convert buffer values with auto-detection
1714
- const convertBufferValue = (value: any): any => {
1715
- // Direct Uint8Array
1716
- if (value instanceof Uint8Array) {
1717
- return Cl.buffer(value)
1718
- }
1719
-
1720
1510
  // Object notation with explicit type
1721
1511
  if (typeof value === 'object' && value !== null && value.type && value.value) {
1722
1512
  switch (value.type) {
@@ -1730,23 +1520,23 @@ function generateGenericHook(hookName) {
1730
1520
  throw new Error(\`Unsupported buffer type: \${value.type}\`)
1731
1521
  }
1732
1522
  }
1733
-
1523
+
1734
1524
  // Auto-detect string type
1735
1525
  if (typeof value === 'string') {
1736
1526
  // 1. Check for hex (0x prefix or pure hex pattern)
1737
1527
  if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
1738
1528
  return Cl.bufferFromHex(value)
1739
1529
  }
1740
-
1530
+
1741
1531
  // 2. Check for non-ASCII characters (UTF-8)
1742
1532
  if (!/^[\\x00-\\x7F]*$/.test(value)) {
1743
1533
  return Cl.bufferFromUtf8(value)
1744
1534
  }
1745
-
1535
+
1746
1536
  // 3. Default to ASCII for simple ASCII strings
1747
1537
  return Cl.bufferFromAscii(value)
1748
1538
  }
1749
-
1539
+
1750
1540
  throw new Error(\`Invalid buffer value: \${value}\`)
1751
1541
  }
1752
1542
 
@@ -1804,7 +1594,7 @@ function generateGenericHook(hookName) {
1804
1594
  }
1805
1595
 
1806
1596
  if (type.response) {
1807
- return 'ok' in value
1597
+ return 'ok' in value
1808
1598
  ? Cl.ok(convertJSValueToClarityValue(value.ok, type.response.ok))
1809
1599
  : Cl.error(convertJSValueToClarityValue(value.err, type.response.error))
1810
1600
  }
@@ -1817,13 +1607,13 @@ function generateGenericHook(hookName) {
1817
1607
  if (!abi || !abi.functions) return null
1818
1608
  return abi.functions.find((func: any) => func.name === functionName)
1819
1609
  }
1820
-
1821
- // Legacy function - unchanged, backward compatible
1610
+
1611
+ // Legacy function - pre-converted Clarity values
1822
1612
  const legacyOpenContractCall = useCallback(async (params: {
1823
1613
  contractAddress: string;
1824
1614
  contractName: string;
1825
1615
  functionName: string;
1826
- functionArgs: any[]; // Pre-converted Clarity values
1616
+ functionArgs: any[];
1827
1617
  network?: string;
1828
1618
  postConditions?: PostCondition[];
1829
1619
  attachment?: string;
@@ -1831,57 +1621,23 @@ function generateGenericHook(hookName) {
1831
1621
  onCancel?: () => void;
1832
1622
  }) => {
1833
1623
  setIsRequestPending(true)
1834
-
1624
+
1835
1625
  try {
1836
1626
  const { contractAddress, contractName, functionName, functionArgs, onFinish, onCancel, ...options } = params
1837
1627
  const network = params.network || config.network || 'mainnet'
1838
1628
  const contract = \`\${contractAddress}.\${contractName}\`
1839
-
1840
- // Try @stacks/connect v8 stx_callContract first (SIP-030)
1841
- try {
1842
- const result = await request('stx_callContract', {
1843
- contract,
1844
- functionName,
1845
- functionArgs,
1846
- network,
1847
- ...options
1848
- })
1849
-
1850
- // Invalidate relevant queries on success
1851
- queryClient.invalidateQueries({
1852
- queryKey: ['stacks-account']
1853
- })
1854
-
1855
- onFinish?.(result)
1856
- return result
1857
- } catch (connectError) {
1858
- // Fallback to openContractCall for broader wallet compatibility
1859
- console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
1860
-
1861
- return new Promise((resolve, reject) => {
1862
- stacksOpenContractCall({
1863
- contractAddress,
1864
- contractName,
1865
- functionName,
1866
- functionArgs,
1867
- network,
1868
- ...options,
1869
- onFinish: (data: any) => {
1870
- // Invalidate relevant queries on success
1871
- queryClient.invalidateQueries({
1872
- queryKey: ['stacks-account']
1873
- })
1874
-
1875
- onFinish?.(data)
1876
- resolve(data)
1877
- },
1878
- onCancel: () => {
1879
- onCancel?.()
1880
- reject(new Error('User cancelled transaction'))
1881
- }
1882
- })
1883
- })
1884
- }
1629
+
1630
+ const result = await request('stx_callContract', {
1631
+ contract,
1632
+ functionName,
1633
+ functionArgs,
1634
+ network,
1635
+ ...options
1636
+ })
1637
+
1638
+ queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
1639
+ onFinish?.(result)
1640
+ return result
1885
1641
  } catch (error) {
1886
1642
  console.error('Contract call failed:', error)
1887
1643
  throw error instanceof Error ? error : new Error('Contract call failed')
@@ -1892,7 +1648,7 @@ function generateGenericHook(hookName) {
1892
1648
 
1893
1649
  // Enhanced function - requires ABI, auto-converts JS values
1894
1650
  const openContractCall = useCallback(async <
1895
- T extends ClarityContract,
1651
+ T extends AbiContract,
1896
1652
  FN extends ExtractFunctionNames<T>
1897
1653
  >(params: {
1898
1654
  contractAddress: string;
@@ -1907,79 +1663,285 @@ function generateGenericHook(hookName) {
1907
1663
  onCancel?: () => void;
1908
1664
  }) => {
1909
1665
  setIsRequestPending(true)
1910
-
1666
+
1911
1667
  try {
1912
1668
  const { contractAddress, contractName, functionName, functionArgs, abi, onFinish, onCancel, ...options } = params
1913
1669
  const network = params.network || config.network || 'mainnet'
1914
1670
  const contract = \`\${contractAddress}.\${contractName}\`
1915
-
1916
- // Find the function in the ABI and convert args
1671
+
1917
1672
  const abiFunction = findFunctionInAbi(abi, functionName)
1918
1673
  if (!abiFunction) {
1919
1674
  throw new Error(\`Function '\${functionName}' not found in ABI\`)
1920
1675
  }
1921
-
1676
+
1922
1677
  const processedArgs = convertArgsWithAbi(functionArgs, abiFunction.args || [])
1923
-
1924
- // Try @stacks/connect v8 stx_callContract first (SIP-030)
1925
- try {
1926
- const result = await request('stx_callContract', {
1927
- contract,
1928
- functionName,
1929
- functionArgs: processedArgs,
1930
- network,
1931
- ...options
1932
- })
1933
-
1934
- // Invalidate relevant queries on success
1935
- queryClient.invalidateQueries({
1936
- queryKey: ['stacks-account']
1937
- })
1938
-
1939
- onFinish?.(result)
1940
- return result
1941
- } catch (connectError) {
1942
- // Fallback to openContractCall for broader wallet compatibility
1943
- console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
1944
-
1945
- return new Promise((resolve, reject) => {
1946
- stacksOpenContractCall({
1947
- contractAddress,
1948
- contractName,
1949
- functionName,
1950
- functionArgs: processedArgs,
1951
- network,
1952
- ...options,
1953
- onFinish: (data: any) => {
1954
- // Invalidate relevant queries on success
1955
- queryClient.invalidateQueries({
1956
- queryKey: ['stacks-account']
1957
- })
1958
-
1959
- onFinish?.(data)
1960
- resolve(data)
1961
- },
1962
- onCancel: () => {
1963
- onCancel?.()
1964
- reject(new Error('User cancelled transaction'))
1965
- }
1966
- })
1967
- })
1968
- }
1678
+
1679
+ const result = await request('stx_callContract', {
1680
+ contract,
1681
+ functionName,
1682
+ functionArgs: processedArgs,
1683
+ network,
1684
+ ...options
1685
+ })
1686
+
1687
+ queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
1688
+ onFinish?.(result)
1689
+ return result
1969
1690
  } catch (error) {
1970
1691
  console.error('Contract call failed:', error)
1971
1692
  throw error instanceof Error ? error : new Error('Contract call failed')
1972
1693
  } finally {
1973
1694
  setIsRequestPending(false)
1974
1695
  }
1975
- }, [config.network, queryClient])
1696
+ }, [config.network, queryClient])
1697
+
1698
+ return {
1699
+ legacyOpenContractCall,
1700
+ openContractCall,
1701
+ isRequestPending
1702
+ }
1703
+ }`;
1704
+
1705
+ // src/plugins/react/generators/generic.ts
1706
+ var GENERIC_HOOKS = [
1707
+ "useAccount",
1708
+ "useConnect",
1709
+ "useDisconnect",
1710
+ "useNetwork",
1711
+ "useContract",
1712
+ "useOpenSTXTransfer",
1713
+ "useSignMessage",
1714
+ "useDeployContract",
1715
+ "useReadContract",
1716
+ "useTransaction",
1717
+ "useBlock",
1718
+ "useAccountTransactions",
1719
+ "useWaitForTransaction"
1720
+ ];
1721
+ async function generateGenericHooks(excludeList = []) {
1722
+ const hooksToGenerate = GENERIC_HOOKS.filter((hookName) => !excludeList.includes(hookName));
1723
+ const imports = `import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
1724
+ import { useState, useCallback } from 'react'
1725
+ import { useSecondLayerConfig } from './provider'
1726
+ import { connect, disconnect, isConnected, request } from '@secondlayer/stacks/connect'
1727
+ import { Cl, validateStacksAddress } from '@secondlayer/stacks'
1728
+ import type { PostCondition } from '@secondlayer/stacks'
1729
+ import type { ExtractFunctionArgs, ExtractFunctionNames, AbiContract } from '@secondlayer/stacks/clarity'
1730
+
1731
+ const API_URLS: Record<string, string> = {
1732
+ mainnet: 'https://api.hiro.so',
1733
+ testnet: 'https://api.testnet.hiro.so',
1734
+ devnet: 'http://localhost:3999'
1735
+ }
1736
+
1737
+ async function fetchTransaction({ txId, network, apiUrl }: { txId: string; network?: string; apiUrl?: string }): Promise<any> {
1738
+ const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1739
+ const response = await fetch(\`\${baseUrl}/extended/v1/tx/\${txId}\`)
1740
+ if (!response.ok) throw new Error(\`Failed to fetch transaction: \${response.statusText}\`)
1741
+ return response.json()
1742
+ }
1743
+
1744
+ async function fetchBlock({ height, network, apiUrl }: { height: number; network?: string; apiUrl?: string }): Promise<any> {
1745
+ const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1746
+ const response = await fetch(\`\${baseUrl}/extended/v1/block/by_height/\${height}\`)
1747
+ if (!response.ok) throw new Error(\`Failed to fetch block: \${response.statusText}\`)
1748
+ return response.json()
1749
+ }
1750
+
1751
+ async function fetchAccountTransactions({ address, network, apiUrl }: { address: string; network?: string; apiUrl?: string }): Promise<any> {
1752
+ const baseUrl = apiUrl || API_URLS[network || 'mainnet']
1753
+ const response = await fetch(\`\${baseUrl}/extended/v1/address/\${address}/transactions\`)
1754
+ if (!response.ok) throw new Error(\`Failed to fetch transactions: \${response.statusText}\`)
1755
+ return response.json()
1756
+ }`;
1757
+ const header = `/**
1758
+ * Generated generic Stacks React hooks
1759
+ * DO NOT EDIT MANUALLY
1760
+ */`;
1761
+ const hooksCode = hooksToGenerate.map((hookName) => generateGenericHook(hookName)).filter(Boolean).join(`
1762
+
1763
+ `);
1764
+ const code = `${imports}
1765
+
1766
+ ${header}
1767
+
1768
+ ${hooksCode}`;
1769
+ return formatCode(code);
1770
+ }
1771
+ function generateGenericHook(hookName) {
1772
+ switch (hookName) {
1773
+ case "useAccount":
1774
+ return `export function useAccount() {
1775
+ const config = useSecondLayerConfig()
1776
+
1777
+ return useQuery({
1778
+ queryKey: ['stacks-account', config.network],
1779
+ queryFn: async () => {
1780
+ try {
1781
+ // Check if already connected
1782
+ const connected = isConnected()
1783
+
1784
+ if (!connected) {
1785
+ return {
1786
+ address: undefined,
1787
+ addresses: undefined,
1788
+ isConnected: false,
1789
+ isConnecting: false,
1790
+ isDisconnected: true,
1791
+ status: 'disconnected' as const
1792
+ }
1793
+ }
1794
+
1795
+ // Get addresses via SIP-030
1796
+ const result = await request('stx_getAddresses')
1797
+
1798
+ if (!result || !result.addresses || result.addresses.length === 0) {
1799
+ return {
1800
+ address: undefined,
1801
+ addresses: undefined,
1802
+ isConnected: false,
1803
+ isConnecting: false,
1804
+ isDisconnected: true,
1805
+ status: 'disconnected' as const
1806
+ }
1807
+ }
1808
+
1809
+ // Extract STX addresses from the response
1810
+ const stxAddresses = result.addresses
1811
+ .filter((addr: any) => addr.address.startsWith('SP') || addr.address.startsWith('ST'))
1812
+ .map((addr: any) => addr.address)
1813
+
1814
+ return {
1815
+ address: stxAddresses[0] || undefined,
1816
+ addresses: stxAddresses,
1817
+ isConnected: true,
1818
+ isConnecting: false,
1819
+ isDisconnected: false,
1820
+ status: 'connected' as const
1821
+ }
1822
+ } catch (error) {
1823
+ // Handle case where wallet is not available or user rejected
1824
+ return {
1825
+ address: undefined,
1826
+ addresses: undefined,
1827
+ isConnected: false,
1828
+ isConnecting: false,
1829
+ isDisconnected: true,
1830
+ status: 'disconnected' as const
1831
+ }
1832
+ }
1833
+ },
1834
+ refetchOnWindowFocus: false,
1835
+ retry: false,
1836
+ staleTime: 1000 * 60 * 5, // 5 minutes
1837
+ refetchInterval: 1000 * 30, // Refetch every 30 seconds to detect wallet changes
1838
+ })
1839
+ }`;
1840
+ case "useConnect":
1841
+ return `export function useConnect() {
1842
+ const queryClient = useQueryClient()
1843
+
1844
+ const mutation = useMutation({
1845
+ mutationFn: async (options: { forceWalletSelect?: boolean } = {}) => {
1846
+ // SIP-030 connect
1847
+ return await connect(options)
1848
+ },
1849
+ onSuccess: () => {
1850
+ // Invalidate account queries to refetch connection state
1851
+ queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
1852
+ },
1853
+ onError: (error) => {
1854
+ console.error('Connection failed:', error)
1855
+ }
1856
+ })
1976
1857
 
1977
1858
  return {
1978
- legacyOpenContractCall,
1979
- openContractCall,
1980
- isRequestPending
1859
+ // Custom connect function that works without arguments
1860
+ connect: (options?: { forceWalletSelect?: boolean }) => {
1861
+ return mutation.mutate(options || {})
1862
+ },
1863
+ connectAsync: async (options?: { forceWalletSelect?: boolean }) => {
1864
+ return mutation.mutateAsync(options || {})
1865
+ },
1866
+ // Expose all the mutation state
1867
+ isPending: mutation.isPending,
1868
+ isError: mutation.isError,
1869
+ isSuccess: mutation.isSuccess,
1870
+ error: mutation.error,
1871
+ data: mutation.data,
1872
+ reset: mutation.reset,
1873
+ // Keep the original mutate/mutateAsync for advanced users
1874
+ mutate: mutation.mutate,
1875
+ mutateAsync: mutation.mutateAsync
1876
+ }
1877
+ }`;
1878
+ case "useDisconnect":
1879
+ return `export function useDisconnect() {
1880
+ const queryClient = useQueryClient()
1881
+
1882
+ const mutation = useMutation({
1883
+ mutationFn: async () => {
1884
+ // SIP-030 disconnect
1885
+ return await disconnect()
1886
+ },
1887
+ onSuccess: () => {
1888
+ // Clear all cached data on disconnect
1889
+ queryClient.clear()
1890
+ },
1891
+ onError: (error) => {
1892
+ console.error('Disconnect failed:', error)
1893
+ }
1894
+ })
1895
+
1896
+ return {
1897
+ // Custom disconnect function
1898
+ disconnect: () => {
1899
+ return mutation.mutate()
1900
+ },
1901
+ disconnectAsync: async () => {
1902
+ return mutation.mutateAsync()
1903
+ },
1904
+ // Expose all the mutation state
1905
+ isPending: mutation.isPending,
1906
+ isError: mutation.isError,
1907
+ isSuccess: mutation.isSuccess,
1908
+ error: mutation.error,
1909
+ data: mutation.data,
1910
+ reset: mutation.reset,
1911
+ // Keep the original mutate/mutateAsync for advanced users
1912
+ mutate: mutation.mutate,
1913
+ mutateAsync: mutation.mutateAsync
1981
1914
  }
1982
1915
  }`;
1916
+ case "useNetwork":
1917
+ return `export function useNetwork() {
1918
+ const config = useSecondLayerConfig()
1919
+
1920
+ return useQuery({
1921
+ queryKey: ['stacks-network', config.network],
1922
+ queryFn: async () => {
1923
+ // Currently read-only from config
1924
+ // Future: Use request('stx_getNetworks') when wallet support improves
1925
+ const network = config.network
1926
+
1927
+ return {
1928
+ network,
1929
+ isMainnet: network === 'mainnet',
1930
+ isTestnet: network === 'testnet',
1931
+ isDevnet: network === 'devnet',
1932
+ // Future: Add switchNetwork when wallets support stx_networkChange
1933
+ // switchNetwork: async (newNetwork: string) => {
1934
+ // return await request('wallet_changeNetwork', { network: newNetwork })
1935
+ // }
1936
+ }
1937
+ },
1938
+ staleTime: Infinity, // Network config rarely changes
1939
+ refetchOnWindowFocus: false,
1940
+ retry: false
1941
+ })
1942
+ }`;
1943
+ case "useContract":
1944
+ return USE_CONTRACT_TEMPLATE;
1983
1945
  case "useReadContract":
1984
1946
  return `export function useReadContract<TArgs = any, TResult = any>(params: {
1985
1947
  contractAddress: string;
@@ -1994,7 +1956,7 @@ function generateGenericHook(hookName) {
1994
1956
  return useQuery<TResult>({
1995
1957
  queryKey: ['read-contract', params.contractAddress, params.contractName, params.functionName, params.args, params.network || config.network],
1996
1958
  queryFn: async () => {
1997
- const { fetchCallReadOnlyFunction } = await import('@stacks/transactions')
1959
+ const { fetchCallReadOnlyFunction } = await import('@secondlayer/stacks/clarity')
1998
1960
 
1999
1961
  // For now, we'll need to handle the args conversion here
2000
1962
  // In the future, we could integrate with the contract interface for automatic conversion
@@ -2095,39 +2057,25 @@ function generateGenericHook(hookName) {
2095
2057
  return `export function useOpenSTXTransfer() {
2096
2058
  const config = useSecondLayerConfig()
2097
2059
  const queryClient = useQueryClient()
2098
-
2060
+
2099
2061
  const mutation = useMutation({
2100
2062
  mutationFn: async (params: {
2101
2063
  recipient: string;
2102
2064
  amount: string | number;
2103
2065
  memo?: string;
2104
2066
  network?: string;
2105
- onFinish?: (data: any) => void;
2106
- onCancel?: () => void;
2107
2067
  }) => {
2108
- const { recipient, amount, memo, onFinish, onCancel, ...options } = params
2068
+ const { recipient, amount, memo } = params
2109
2069
  const network = params.network || config.network || 'mainnet'
2110
-
2111
- return new Promise((resolve, reject) => {
2112
- openSTXTransfer({
2113
- recipient,
2114
- amount: amount.toString(),
2115
- memo,
2116
- network,
2117
- ...options,
2118
- onFinish: (data: any) => {
2119
- onFinish?.(data)
2120
- resolve(data)
2121
- },
2122
- onCancel: () => {
2123
- onCancel?.()
2124
- reject(new Error('User cancelled transaction'))
2125
- }
2126
- })
2070
+
2071
+ return await request('stx_transferStx', {
2072
+ recipient,
2073
+ amount: amount.toString(),
2074
+ memo,
2075
+ network,
2127
2076
  })
2128
2077
  },
2129
2078
  onSuccess: () => {
2130
- // Invalidate relevant queries on success
2131
2079
  queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
2132
2080
  },
2133
2081
  onError: (error) => {
@@ -2140,15 +2088,12 @@ function generateGenericHook(hookName) {
2140
2088
  amount: string | number;
2141
2089
  memo?: string;
2142
2090
  network?: string;
2143
- onFinish?: (data: any) => void;
2144
- onCancel?: () => void;
2145
2091
  }) => {
2146
2092
  return mutation.mutateAsync(params)
2147
2093
  }, [mutation])
2148
2094
 
2149
2095
  return {
2150
2096
  openSTXTransfer,
2151
- // Expose mutation state
2152
2097
  isPending: mutation.isPending,
2153
2098
  isError: mutation.isError,
2154
2099
  isSuccess: mutation.isSuccess,
@@ -2160,31 +2105,18 @@ function generateGenericHook(hookName) {
2160
2105
  case "useSignMessage":
2161
2106
  return `export function useSignMessage() {
2162
2107
  const config = useSecondLayerConfig()
2163
-
2108
+
2164
2109
  const mutation = useMutation({
2165
2110
  mutationFn: async (params: {
2166
2111
  message: string;
2167
2112
  network?: string;
2168
- onFinish?: (data: any) => void;
2169
- onCancel?: () => void;
2170
2113
  }) => {
2171
- const { message, onFinish, onCancel, ...options } = params
2114
+ const { message } = params
2172
2115
  const network = params.network || config.network || 'mainnet'
2173
-
2174
- return new Promise((resolve, reject) => {
2175
- openSignatureRequestPopup({
2176
- message,
2177
- network,
2178
- ...options,
2179
- onFinish: (data: any) => {
2180
- onFinish?.(data)
2181
- resolve(data)
2182
- },
2183
- onCancel: () => {
2184
- onCancel?.()
2185
- reject(new Error('User cancelled message signing'))
2186
- }
2187
- })
2116
+
2117
+ return await request('stx_signMessage', {
2118
+ message,
2119
+ network,
2188
2120
  })
2189
2121
  },
2190
2122
  onError: (error) => {
@@ -2195,15 +2127,12 @@ function generateGenericHook(hookName) {
2195
2127
  const signMessage = useCallback(async (params: {
2196
2128
  message: string;
2197
2129
  network?: string;
2198
- onFinish?: (data: any) => void;
2199
- onCancel?: () => void;
2200
2130
  }) => {
2201
2131
  return mutation.mutateAsync(params)
2202
2132
  }, [mutation])
2203
2133
 
2204
2134
  return {
2205
2135
  signMessage,
2206
- // Expose mutation state
2207
2136
  isPending: mutation.isPending,
2208
2137
  isError: mutation.isError,
2209
2138
  isSuccess: mutation.isSuccess,
@@ -2216,38 +2145,23 @@ function generateGenericHook(hookName) {
2216
2145
  return `export function useDeployContract() {
2217
2146
  const config = useSecondLayerConfig()
2218
2147
  const queryClient = useQueryClient()
2219
-
2148
+
2220
2149
  const mutation = useMutation({
2221
2150
  mutationFn: async (params: {
2222
2151
  contractName: string;
2223
2152
  codeBody: string;
2224
2153
  network?: string;
2225
- postConditions?: PostCondition[];
2226
- onFinish?: (data: any) => void;
2227
- onCancel?: () => void;
2228
2154
  }) => {
2229
- const { contractName, codeBody, onFinish, onCancel, ...options } = params
2155
+ const { contractName, codeBody } = params
2230
2156
  const network = params.network || config.network || 'mainnet'
2231
-
2232
- return new Promise((resolve, reject) => {
2233
- openContractDeploy({
2234
- contractName,
2235
- codeBody,
2236
- network,
2237
- ...options,
2238
- onFinish: (data: any) => {
2239
- onFinish?.(data)
2240
- resolve(data)
2241
- },
2242
- onCancel: () => {
2243
- onCancel?.()
2244
- reject(new Error('User cancelled contract deployment'))
2245
- }
2246
- })
2157
+
2158
+ return await request('stx_deployContract', {
2159
+ name: contractName,
2160
+ clarityCode: codeBody,
2161
+ network,
2247
2162
  })
2248
2163
  },
2249
2164
  onSuccess: () => {
2250
- // Invalidate relevant queries on success
2251
2165
  queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
2252
2166
  },
2253
2167
  onError: (error) => {
@@ -2259,16 +2173,12 @@ function generateGenericHook(hookName) {
2259
2173
  contractName: string;
2260
2174
  codeBody: string;
2261
2175
  network?: string;
2262
- postConditions?: PostCondition[];
2263
- onFinish?: (data: any) => void;
2264
- onCancel?: () => void;
2265
2176
  }) => {
2266
2177
  return mutation.mutateAsync(params)
2267
2178
  }, [mutation])
2268
2179
 
2269
2180
  return {
2270
2181
  deployContract,
2271
- // Expose mutation state
2272
2182
  isPending: mutation.isPending,
2273
2183
  isError: mutation.isError,
2274
2184
  isSuccess: mutation.isSuccess,
@@ -2286,35 +2196,30 @@ function generateGenericHook(hookName) {
2286
2196
  init_format();
2287
2197
 
2288
2198
  // src/plugins/react/generators/utils.ts
2289
- import { toCamelCase as toCamelCase5 } from "@secondlayer/clarity-types";
2199
+ import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
2290
2200
  function capitalize(str) {
2291
2201
  return str.charAt(0).toUpperCase() + str.slice(1);
2292
2202
  }
2293
2203
  function generateHookArgsSignature(args) {
2294
2204
  if (args.length === 0)
2295
2205
  return "";
2296
- const argsList = args.map((arg) => `${toCamelCase5(arg.name)}: ${clarityTypeToTS(arg.type)}`).join(", ");
2206
+ const argsList = args.map((arg) => `${toCamelCase7(arg.name)}: ${clarityTypeToTS(arg.type)}`).join(", ");
2297
2207
  return `${argsList}`;
2298
2208
  }
2299
2209
  function generateArgsType(args) {
2300
2210
  if (args.length === 0)
2301
2211
  return "void";
2302
- const argsList = args.map((arg) => `${toCamelCase5(arg.name)}: ${clarityTypeToTS(arg.type)}`).join("; ");
2212
+ const argsList = args.map((arg) => `${toCamelCase7(arg.name)}: ${clarityTypeToTS(arg.type)}`).join("; ");
2303
2213
  return `{ ${argsList} }`;
2304
2214
  }
2305
- function generateQueryKeyArgs(args) {
2215
+ function generateArgNames(args) {
2306
2216
  if (args.length === 0)
2307
2217
  return "";
2308
- return args.map((arg) => toCamelCase5(arg.name)).join(", ");
2309
- }
2310
- function generateFunctionCallArgs(args) {
2311
- if (args.length === 0)
2312
- return "";
2313
- return args.map((arg) => toCamelCase5(arg.name)).join(", ");
2218
+ return args.map((arg) => toCamelCase7(arg.name)).join(", ");
2314
2219
  }
2315
2220
  function generateEnabledCondition(args) {
2316
2221
  return args.map((arg) => {
2317
- const camelName = toCamelCase5(arg.name);
2222
+ const camelName = toCamelCase7(arg.name);
2318
2223
  const type = clarityTypeToTS(arg.type);
2319
2224
  if (type === "string")
2320
2225
  return `!!${camelName}`;
@@ -2326,7 +2231,7 @@ function generateEnabledCondition(args) {
2326
2231
  function generateObjectArgs(args) {
2327
2232
  if (args.length === 0)
2328
2233
  return "";
2329
- return args.map((arg) => `${arg.name}: ${toCamelCase5(arg.name)}`).join(", ");
2234
+ return args.map((arg) => `${arg.name}: ${toCamelCase7(arg.name)}`).join(", ");
2330
2235
  }
2331
2236
 
2332
2237
  // src/plugins/react/generators/contract.ts
@@ -2334,8 +2239,8 @@ async function generateContractHooks(contracts, excludeList = []) {
2334
2239
  const imports = `import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
2335
2240
  import { useCallback } from 'react'
2336
2241
  import { useSecondLayerConfig } from './provider'
2337
- import { request, openContractCall as stacksOpenContractCall } from '@stacks/connect'
2338
- import type { PostCondition } from '@stacks/transactions'
2242
+ import { request } from '@secondlayer/stacks/connect'
2243
+ import type { PostCondition } from '@secondlayer/stacks'
2339
2244
  import { ${contracts.map((c) => c.name).join(", ")} } from './contracts'`;
2340
2245
  const header = `/**
2341
2246
  * Generated contract-specific React hooks
@@ -2356,24 +2261,24 @@ function generateContractHookMethods(contract, excludeList) {
2356
2261
  const functions = abi.functions || [];
2357
2262
  const maps = abi.maps || [];
2358
2263
  const variables = abi.variables || [];
2359
- const readOnlyFunctions = functions.filter((f) => f.access === "read_only" || f.access === "read-only");
2264
+ const readOnlyFunctions = functions.filter((f) => f.access === "read-only");
2360
2265
  const publicFunctions = functions.filter((f) => f.access === "public");
2361
2266
  const readHooks = readOnlyFunctions.map((func) => {
2362
- const hookName = `use${capitalize(name)}${capitalize(toCamelCase5(func.name))}`;
2267
+ const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(func.name))}`;
2363
2268
  if (excludeList.includes(hookName)) {
2364
2269
  return null;
2365
2270
  }
2366
2271
  return generateReadHook(func, name);
2367
2272
  }).filter(Boolean);
2368
2273
  const writeHooks = publicFunctions.map((func) => {
2369
- const hookName = `use${capitalize(name)}${capitalize(toCamelCase5(func.name))}`;
2274
+ const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(func.name))}`;
2370
2275
  if (excludeList.includes(hookName)) {
2371
2276
  return null;
2372
2277
  }
2373
2278
  return generateWriteHook(func, name);
2374
2279
  }).filter(Boolean);
2375
2280
  const mapHooks = maps.map((map) => {
2376
- const hookName = `use${capitalize(name)}${capitalize(toCamelCase5(map.name))}`;
2281
+ const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(map.name))}`;
2377
2282
  if (excludeList.includes(hookName)) {
2378
2283
  return null;
2379
2284
  }
@@ -2381,7 +2286,7 @@ function generateContractHookMethods(contract, excludeList) {
2381
2286
  }).filter(Boolean);
2382
2287
  const dataVars = variables.filter((v) => v.access === "variable");
2383
2288
  const varHooks = dataVars.map((variable) => {
2384
- const hookName = `use${capitalize(name)}${capitalize(toCamelCase5(variable.name))}`;
2289
+ const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(variable.name))}`;
2385
2290
  if (excludeList.includes(hookName)) {
2386
2291
  return null;
2387
2292
  }
@@ -2389,7 +2294,7 @@ function generateContractHookMethods(contract, excludeList) {
2389
2294
  }).filter(Boolean);
2390
2295
  const constants = variables.filter((v) => v.access === "constant");
2391
2296
  const constantHooks = constants.map((constant) => {
2392
- const hookName = `use${capitalize(name)}${capitalize(toCamelCase5(constant.name))}`;
2297
+ const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(constant.name))}`;
2393
2298
  if (excludeList.includes(hookName)) {
2394
2299
  return null;
2395
2300
  }
@@ -2404,7 +2309,7 @@ function generateContractHookMethods(contract, excludeList) {
2404
2309
  `);
2405
2310
  }
2406
2311
  function generateReadHook(func, contractName) {
2407
- const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase5(func.name))}`;
2312
+ const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase7(func.name))}`;
2408
2313
  const argsSignature = generateHookArgsSignature(func.args);
2409
2314
  const enabledParam = func.args.length > 0 ? ", options?: { enabled?: boolean }" : "options?: { enabled?: boolean }";
2410
2315
  const returnType = clarityTypeToTS(func.outputs);
@@ -2412,8 +2317,8 @@ function generateReadHook(func, contractName) {
2412
2317
  const config = useSecondLayerConfig()
2413
2318
 
2414
2319
  return useQuery<${returnType}>({
2415
- queryKey: ['${func.name}', ${contractName}.address, ${generateQueryKeyArgs(func.args)}],
2416
- queryFn: () => ${contractName}.read.${toCamelCase5(func.name)}(${generateFunctionCallArgs(func.args) ? `{ ${generateObjectArgs(func.args)} }, ` : ""}{
2320
+ queryKey: ['${func.name}', ${contractName}.address, ${generateArgNames(func.args)}],
2321
+ queryFn: () => ${contractName}.read.${toCamelCase7(func.name)}(${generateArgNames(func.args) ? `{ ${generateObjectArgs(func.args)} }, ` : ""}{
2417
2322
  network: config.network,
2418
2323
  senderAddress: config.senderAddress || 'SP000000000000000000002Q6VF78'
2419
2324
  }),
@@ -2423,7 +2328,7 @@ function generateReadHook(func, contractName) {
2423
2328
  }`;
2424
2329
  }
2425
2330
  function generateWriteHook(func, contractName) {
2426
- const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase5(func.name))}`;
2331
+ const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase7(func.name))}`;
2427
2332
  const argsType = generateArgsType(func.args);
2428
2333
  return `export function ${hookName}() {
2429
2334
  const config = useSecondLayerConfig()
@@ -2440,46 +2345,21 @@ function generateWriteHook(func, contractName) {
2440
2345
  };
2441
2346
  }) => {
2442
2347
  const { args, options = {} } = params
2443
- const contractCallData = ${contractName}.${toCamelCase5(func.name)}(args)
2348
+ const contractCallData = ${contractName}.${toCamelCase7(func.name)}(args)
2444
2349
  const { contractAddress, contractName: name, functionName, functionArgs } = contractCallData
2445
2350
  const network = config.network || 'mainnet'
2446
2351
  const contract = \`\${contractAddress}.\${name}\`
2447
-
2448
- // Try @stacks/connect v8 stx_callContract first (SIP-030)
2449
- try {
2450
- const result = await request('stx_callContract', {
2451
- contract,
2452
- functionName,
2453
- functionArgs,
2454
- network,
2455
- ...options
2456
- })
2457
-
2458
- options.onFinish?.(result)
2459
- return result
2460
- } catch (connectError) {
2461
- // Fallback to openContractCall for broader wallet compatibility
2462
- console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
2463
-
2464
- return new Promise((resolve, reject) => {
2465
- stacksOpenContractCall({
2466
- contractAddress,
2467
- contractName: name,
2468
- functionName,
2469
- functionArgs,
2470
- network,
2471
- ...options,
2472
- onFinish: (data: any) => {
2473
- options.onFinish?.(data)
2474
- resolve(data)
2475
- },
2476
- onCancel: () => {
2477
- options.onCancel?.()
2478
- reject(new Error('User cancelled transaction'))
2479
- }
2480
- })
2481
- })
2482
- }
2352
+
2353
+ const result = await request('stx_callContract', {
2354
+ contract,
2355
+ functionName,
2356
+ functionArgs,
2357
+ network,
2358
+ ...options
2359
+ })
2360
+
2361
+ options.onFinish?.(result)
2362
+ return result
2483
2363
  },
2484
2364
  onSuccess: () => {
2485
2365
  // Invalidate relevant queries on success
@@ -2490,7 +2370,7 @@ function generateWriteHook(func, contractName) {
2490
2370
  }
2491
2371
  })
2492
2372
 
2493
- const ${toCamelCase5(func.name)} = useCallback(async (
2373
+ const ${toCamelCase7(func.name)} = useCallback(async (
2494
2374
  args: ${argsType},
2495
2375
  options?: {
2496
2376
  postConditions?: PostCondition[];
@@ -2503,7 +2383,7 @@ function generateWriteHook(func, contractName) {
2503
2383
  }, [mutation])
2504
2384
 
2505
2385
  return {
2506
- ${toCamelCase5(func.name)},
2386
+ ${toCamelCase7(func.name)},
2507
2387
  // Expose mutation state
2508
2388
  isPending: mutation.isPending,
2509
2389
  isError: mutation.isError,
@@ -2515,7 +2395,7 @@ function generateWriteHook(func, contractName) {
2515
2395
  }`;
2516
2396
  }
2517
2397
  function generateMapHook(map, contractVarName, _address, _contractName) {
2518
- const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase5(map.name))}`;
2398
+ const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(map.name))}`;
2519
2399
  const keyType = clarityTypeToTS(map.key);
2520
2400
  const valueType = clarityTypeToTS(map.value);
2521
2401
  return `export function ${hookName}(key: ${keyType}, options?: { enabled?: boolean }) {
@@ -2524,14 +2404,14 @@ function generateMapHook(map, contractVarName, _address, _contractName) {
2524
2404
  return useQuery<${valueType} | null>({
2525
2405
  queryKey: ['${contractVarName}', '${map.name}', 'map', key, config.network],
2526
2406
  queryFn: async () => {
2527
- return ${contractVarName}.maps.${toCamelCase5(map.name)}.get(key, { network: config.network })
2407
+ return ${contractVarName}.maps.${toCamelCase7(map.name)}.get(key, { network: config.network })
2528
2408
  },
2529
2409
  enabled: options?.enabled ?? true
2530
2410
  })
2531
2411
  }`;
2532
2412
  }
2533
2413
  function generateVarHook(variable, contractVarName, _address, _contractName) {
2534
- const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase5(variable.name))}`;
2414
+ const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(variable.name))}`;
2535
2415
  const valueType = clarityTypeToTS(variable.type);
2536
2416
  return `export function ${hookName}(options?: { enabled?: boolean }) {
2537
2417
  const config = useSecondLayerConfig()
@@ -2539,14 +2419,14 @@ function generateVarHook(variable, contractVarName, _address, _contractName) {
2539
2419
  return useQuery<${valueType}>({
2540
2420
  queryKey: ['${contractVarName}', '${variable.name}', 'var', config.network],
2541
2421
  queryFn: async () => {
2542
- return ${contractVarName}.vars.${toCamelCase5(variable.name)}.get({ network: config.network })
2422
+ return ${contractVarName}.vars.${toCamelCase7(variable.name)}.get({ network: config.network })
2543
2423
  },
2544
2424
  enabled: options?.enabled ?? true
2545
2425
  })
2546
2426
  }`;
2547
2427
  }
2548
2428
  function generateConstantHook(constant, contractVarName, _address, _contractName) {
2549
- const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase5(constant.name))}`;
2429
+ const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(constant.name))}`;
2550
2430
  const valueType = clarityTypeToTS(constant.type);
2551
2431
  return `export function ${hookName}(options?: { enabled?: boolean }) {
2552
2432
  const config = useSecondLayerConfig()
@@ -2554,7 +2434,7 @@ function generateConstantHook(constant, contractVarName, _address, _contractName
2554
2434
  return useQuery<${valueType}>({
2555
2435
  queryKey: ['${contractVarName}', '${constant.name}', 'constant', config.network],
2556
2436
  queryFn: async () => {
2557
- return ${contractVarName}.constants.${toCamelCase5(constant.name)}.get({ network: config.network })
2437
+ return ${contractVarName}.constants.${toCamelCase7(constant.name)}.get({ network: config.network })
2558
2438
  },
2559
2439
  enabled: options?.enabled ?? true,
2560
2440
  staleTime: Infinity // Constants never change
@@ -2602,155 +2482,17 @@ var react = (options = {}) => {
2602
2482
  };
2603
2483
  // src/plugins/testing/generators.ts
2604
2484
  import {
2605
- toCamelCase as toCamelCase6
2606
- } from "@secondlayer/clarity-types";
2485
+ toCamelCase as toCamelCase8,
2486
+ isAbiTuple as isAbiTuple3
2487
+ } from "@secondlayer/stacks/clarity";
2607
2488
  function toPascalCase(str) {
2608
- const camel = toCamelCase6(str);
2489
+ const camel = toCamelCase8(str);
2609
2490
  return camel.charAt(0).toUpperCase() + camel.slice(1);
2610
2491
  }
2611
- function generateArgsSignature2(args) {
2612
- if (args.length === 0)
2613
- return "";
2614
- const argsTypes = args.map((arg) => {
2615
- const camelName = toCamelCase6(arg.name);
2616
- return `${camelName}: ${getTypeForArg(arg)}`;
2617
- }).join("; ");
2618
- return `args: { ${argsTypes} }, `;
2619
- }
2620
- function generateClarityConversion3(argName, argType) {
2621
- const type = argType.type;
2622
- if (typeof type === "string") {
2623
- switch (type) {
2624
- case "uint128":
2625
- return `Cl.uint(${argName})`;
2626
- case "int128":
2627
- return `Cl.int(${argName})`;
2628
- case "bool":
2629
- return `Cl.bool(${argName})`;
2630
- case "principal":
2631
- case "trait_reference":
2632
- return `(() => {
2633
- const principal = ${argName};
2634
- if (principal.includes(".")) {
2635
- const [address, contractName] = principal.split(".") as [string, string];
2636
- return Cl.contractPrincipal(address, contractName);
2637
- } else {
2638
- return Cl.standardPrincipal(principal);
2639
- }
2640
- })()`;
2641
- default:
2642
- return `${argName}`;
2643
- }
2644
- }
2645
- if (type["string-ascii"]) {
2646
- return `Cl.stringAscii(${argName})`;
2647
- }
2648
- if (type["string-utf8"]) {
2649
- return `Cl.stringUtf8(${argName})`;
2650
- }
2651
- if (type.buff) {
2652
- return `(() => {
2653
- const value = ${argName};
2654
- if (value instanceof Uint8Array) {
2655
- return Cl.buffer(value);
2656
- }
2657
- if (typeof value === 'object' && value !== null && 'type' in value && 'value' in value) {
2658
- switch (value.type) {
2659
- case 'ascii':
2660
- return Cl.bufferFromAscii(value.value);
2661
- case 'utf8':
2662
- return Cl.bufferFromUtf8(value.value);
2663
- case 'hex':
2664
- return Cl.bufferFromHex(value.value);
2665
- default:
2666
- throw new Error(\`Unsupported buffer type: \${value.type}\`);
2667
- }
2668
- }
2669
- if (typeof value === 'string') {
2670
- if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
2671
- return Cl.bufferFromHex(value);
2672
- }
2673
- if (!/^[\\x00-\\x7F]*$/.test(value)) {
2674
- return Cl.bufferFromUtf8(value);
2675
- }
2676
- return Cl.bufferFromAscii(value);
2677
- }
2678
- throw new Error(\`Invalid buffer value: \${value}\`);
2679
- })()`;
2680
- }
2681
- if (type.optional) {
2682
- const innerConversion = generateClarityConversion3("inner", {
2683
- type: type.optional
2684
- });
2685
- return `${argName} !== null ? Cl.some((() => { const inner = ${argName}; return ${innerConversion}; })()) : Cl.none()`;
2686
- }
2687
- if (type.list) {
2688
- const innerConversion = generateClarityConversion3("item", {
2689
- type: type.list.type
2690
- });
2691
- const maxLength = type.list.length || 100;
2692
- return `(() => {
2693
- const listValue = ${argName};
2694
- if (listValue.length > ${maxLength}) {
2695
- throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
2696
- }
2697
- return Cl.list(listValue.map(item => ${innerConversion}));
2698
- })()`;
2699
- }
2700
- if (type.tuple) {
2701
- const requiredFields = type.tuple.map((f) => f.name);
2702
- const fieldNames = JSON.stringify(requiredFields);
2703
- const fields = type.tuple.map((field) => {
2704
- const camelFieldName = toCamelCase6(field.name);
2705
- const fieldConversion = generateClarityConversion3(`tupleValue.${camelFieldName}`, { type: field.type });
2706
- return `"${field.name}": ${fieldConversion}`;
2707
- }).join(", ");
2708
- return `(() => {
2709
- const tupleValue = ${argName};
2710
- const requiredFields = ${fieldNames};
2711
- for (const fieldName of requiredFields) {
2712
- const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
2713
- if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
2714
- throw new Error(\`Missing tuple field: \${fieldName}\`);
2715
- }
2716
- }
2717
- return Cl.tuple({ ${fields} });
2718
- })()`;
2719
- }
2720
- if (type.response) {
2721
- const okConversion = generateClarityConversion3(`responseValue.ok`, {
2722
- type: type.response.ok
2723
- });
2724
- const errConversion = generateClarityConversion3(`responseValue.err`, {
2725
- type: type.response.error
2726
- });
2727
- return `(() => {
2728
- const responseValue = ${argName};
2729
- const hasOk = 'ok' in responseValue;
2730
- const hasErr = 'err' in responseValue;
2731
- if (hasOk && !hasErr) {
2732
- return Cl.ok(${okConversion});
2733
- }
2734
- if (hasErr && !hasOk) {
2735
- return Cl.error(${errConversion});
2736
- }
2737
- throw new Error("Response must have exactly 'ok' or 'err' property");
2738
- })()`;
2739
- }
2740
- return `${argName}`;
2741
- }
2742
- function generateClarityArgs2(args) {
2743
- if (args.length === 0)
2744
- return "";
2745
- return args.map((arg) => {
2746
- const argName = `args.${toCamelCase6(arg.name)}`;
2747
- return generateClarityConversion3(argName, arg);
2748
- }).join(", ");
2749
- }
2750
2492
  function generatePublicFunction(func, contractId) {
2751
- const methodName = toCamelCase6(func.name);
2752
- const argsSignature = generateArgsSignature2(func.args);
2753
- const clarityArgs = generateClarityArgs2(func.args);
2493
+ const methodName = toCamelCase8(func.name);
2494
+ const argsSignature = generateArgsSignature(func.args);
2495
+ const clarityArgs = generateClarityArgs(func.args);
2754
2496
  return `${methodName}: (${argsSignature}caller: string) => {
2755
2497
  const callerAddr = accounts.get(caller) ?? caller;
2756
2498
  return simnet.callPublicFn(
@@ -2762,9 +2504,9 @@ function generatePublicFunction(func, contractId) {
2762
2504
  }`;
2763
2505
  }
2764
2506
  function generateReadOnlyFunction(func, contractId) {
2765
- const methodName = toCamelCase6(func.name);
2766
- const argsSignature = generateArgsSignature2(func.args);
2767
- const clarityArgs = generateClarityArgs2(func.args);
2507
+ const methodName = toCamelCase8(func.name);
2508
+ const argsSignature = generateArgsSignature(func.args);
2509
+ const clarityArgs = generateClarityArgs(func.args);
2768
2510
  const hasArgs = func.args.length > 0;
2769
2511
  const argsParam = hasArgs ? argsSignature : "";
2770
2512
  return `${methodName}: (${argsParam}) => {
@@ -2777,9 +2519,9 @@ function generateReadOnlyFunction(func, contractId) {
2777
2519
  }`;
2778
2520
  }
2779
2521
  function generatePrivateFunction(func, contractId) {
2780
- const methodName = toCamelCase6(func.name);
2781
- const argsSignature = generateArgsSignature2(func.args);
2782
- const clarityArgs = generateClarityArgs2(func.args);
2522
+ const methodName = toCamelCase8(func.name);
2523
+ const argsSignature = generateArgsSignature(func.args);
2524
+ const clarityArgs = generateClarityArgs(func.args);
2783
2525
  return `${methodName}: (${argsSignature}caller: string) => {
2784
2526
  const callerAddr = accounts.get(caller) ?? caller;
2785
2527
  return simnet.callPrivateFn(
@@ -2791,31 +2533,31 @@ function generatePrivateFunction(func, contractId) {
2791
2533
  }`;
2792
2534
  }
2793
2535
  function generateDataVarHelper(variable, contractId) {
2794
- const methodName = toCamelCase6(variable.name);
2536
+ const methodName = toCamelCase8(variable.name);
2795
2537
  return `${methodName}: () => {
2796
2538
  return simnet.getDataVar('${contractId}', '${variable.name}');
2797
2539
  }`;
2798
2540
  }
2799
2541
  function getMapKeyType(keyType) {
2800
- if (keyType.tuple) {
2801
- const fields = keyType.tuple.map((field) => `${toCamelCase6(field.name)}: ${getTypeForArg({ type: field.type })}`).join("; ");
2542
+ if (isAbiTuple3(keyType)) {
2543
+ const fields = keyType.tuple.map((field) => `${toCamelCase8(field.name)}: ${getTypeForArg({ type: field.type })}`).join("; ");
2802
2544
  return `{ ${fields} }`;
2803
2545
  }
2804
2546
  return getTypeForArg({ type: keyType });
2805
2547
  }
2806
2548
  function generateMapKeyConversion2(keyType) {
2807
- if (keyType.tuple) {
2549
+ if (isAbiTuple3(keyType)) {
2808
2550
  const fields = keyType.tuple.map((field) => {
2809
- const camelFieldName = toCamelCase6(field.name);
2810
- const fieldConversion = generateClarityConversion3(`key.${camelFieldName}`, { type: field.type });
2551
+ const camelFieldName = toCamelCase8(field.name);
2552
+ const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
2811
2553
  return `"${field.name}": ${fieldConversion}`;
2812
2554
  }).join(", ");
2813
2555
  return `Cl.tuple({ ${fields} })`;
2814
2556
  }
2815
- return generateClarityConversion3("key", { type: keyType });
2557
+ return generateClarityConversion("key", { type: keyType });
2816
2558
  }
2817
2559
  function generateMapEntryHelper(map, contractId) {
2818
- const methodName = toCamelCase6(map.name);
2560
+ const methodName = toCamelCase8(map.name);
2819
2561
  const keyType = getMapKeyType(map.key);
2820
2562
  const keyConversion = generateMapKeyConversion2(map.key);
2821
2563
  return `${methodName}: (key: ${keyType}) => {
@@ -2856,7 +2598,7 @@ function generateContractHelper(contract, options) {
2856
2598
  const maps = abi.maps || [];
2857
2599
  const pascalName = toPascalCase(name);
2858
2600
  const publicFns = functions.filter((f) => f.access === "public");
2859
- const readOnlyFns = functions.filter((f) => f.access === "read_only" || f.access === "read-only");
2601
+ const readOnlyFns = functions.filter((f) => f.access === "read-only");
2860
2602
  const privateFns = options.includePrivate ? functions.filter((f) => f.access === "private") : [];
2861
2603
  const publicHelpers = publicFns.map((f) => generatePublicFunction(f, address));
2862
2604
  const readOnlyHelpers = readOnlyFns.map((f) => generateReadOnlyFunction(f, address));
@@ -2885,7 +2627,7 @@ function generateContractHelper(contract, options) {
2885
2627
  }
2886
2628
  function generateGetContracts(contracts) {
2887
2629
  const contractEntries = contracts.map((contract) => {
2888
- const camelName = toCamelCase6(contract.name);
2630
+ const camelName = toCamelCase8(contract.name);
2889
2631
  const pascalName = toPascalCase(contract.name);
2890
2632
  return `${camelName}: get${pascalName}(simnet)`;
2891
2633
  }).join(`,
@@ -3013,5 +2755,5 @@ export {
3013
2755
  PluginManager
3014
2756
  };
3015
2757
 
3016
- //# debugId=161C0B3D172075E364756E2164756E21
2758
+ //# debugId=217DF2A543BBE57564756E2164756E21
3017
2759
  //# sourceMappingURL=index.js.map