@secondlayer/cli 1.1.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/cli.js +25734 -17375
- package/dist/cli.js.map +76 -22
- package/dist/index.d.ts +5 -4
- package/dist/index.js +943 -1171
- package/dist/index.js.map +19 -14
- package/dist/plugin-manager.d.ts +5 -4
- package/dist/plugin-manager.js +74 -31
- package/dist/plugin-manager.js.map +6 -5
- package/package.json +12 -8
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, {
|
|
@@ -30,28 +15,70 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
30
15
|
// src/utils/format.ts
|
|
31
16
|
var exports_format = {};
|
|
32
17
|
__export(exports_format, {
|
|
33
|
-
formatCode: () => formatCode
|
|
34
|
-
PRETTIER_OPTIONS: () => PRETTIER_OPTIONS
|
|
18
|
+
formatCode: () => formatCode
|
|
35
19
|
});
|
|
36
|
-
import {
|
|
20
|
+
import { Biome, Distribution } from "@biomejs/js-api";
|
|
21
|
+
async function getBiome() {
|
|
22
|
+
if (!biome) {
|
|
23
|
+
biome = await Biome.create({
|
|
24
|
+
distribution: Distribution.NODE
|
|
25
|
+
});
|
|
26
|
+
biome.applyConfiguration({
|
|
27
|
+
formatter: {
|
|
28
|
+
enabled: true,
|
|
29
|
+
indentStyle: "tab",
|
|
30
|
+
lineWidth: 80
|
|
31
|
+
},
|
|
32
|
+
javascript: {
|
|
33
|
+
formatter: {
|
|
34
|
+
semicolons: "always",
|
|
35
|
+
quoteStyle: "single"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
organizeImports: {
|
|
39
|
+
enabled: true
|
|
40
|
+
},
|
|
41
|
+
linter: {
|
|
42
|
+
enabled: true
|
|
43
|
+
},
|
|
44
|
+
assists: {
|
|
45
|
+
enabled: true
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
biome.registerProjectFolder();
|
|
49
|
+
}
|
|
50
|
+
return biome;
|
|
51
|
+
}
|
|
37
52
|
async function formatCode(code) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
53
|
+
const b = await getBiome();
|
|
54
|
+
const linted = b.lintContent(code, {
|
|
55
|
+
filePath: "generated.ts",
|
|
56
|
+
fixFileMode: "SafeFixes"
|
|
57
|
+
});
|
|
58
|
+
const formatted = b.formatContent(linted.content, {
|
|
59
|
+
filePath: "generated.ts"
|
|
60
|
+
});
|
|
61
|
+
return formatted.content;
|
|
62
|
+
}
|
|
63
|
+
var biome = null;
|
|
64
|
+
var init_format = () => {};
|
|
50
65
|
|
|
51
66
|
// src/core/plugin-manager.ts
|
|
52
67
|
import { promises as fs } from "fs";
|
|
53
68
|
import path from "path";
|
|
54
|
-
import {
|
|
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
|
+
}
|
|
55
82
|
|
|
56
83
|
// src/types/plugin.ts
|
|
57
84
|
function isClarinetContract(c) {
|
|
@@ -62,6 +89,8 @@ function isDirectFileContract(c) {
|
|
|
62
89
|
}
|
|
63
90
|
|
|
64
91
|
// src/core/plugin-manager.ts
|
|
92
|
+
var validateStacksAddress = _validateStacksAddress;
|
|
93
|
+
|
|
65
94
|
class PluginManager {
|
|
66
95
|
plugins = [];
|
|
67
96
|
logger;
|
|
@@ -123,11 +152,11 @@ class PluginManager {
|
|
|
123
152
|
for (let contract of contracts) {
|
|
124
153
|
if (isClarinetContract(contract) && contract.abi) {
|
|
125
154
|
const address = typeof contract.address === "string" ? contract.address : "";
|
|
126
|
-
const
|
|
155
|
+
const parsed = parseContractId(address);
|
|
127
156
|
const processed = {
|
|
128
|
-
name: contract.name || contractName,
|
|
129
|
-
address:
|
|
130
|
-
contractName,
|
|
157
|
+
name: contract.name || parsed.contractName,
|
|
158
|
+
address: parsed.address,
|
|
159
|
+
contractName: parsed.contractName,
|
|
131
160
|
abi: contract.abi,
|
|
132
161
|
source: "local",
|
|
133
162
|
metadata: { source: "clarinet" }
|
|
@@ -137,11 +166,11 @@ class PluginManager {
|
|
|
137
166
|
}
|
|
138
167
|
if (isDirectFileContract(contract) && contract.abi) {
|
|
139
168
|
const address = typeof contract.address === "string" ? contract.address : "";
|
|
140
|
-
const
|
|
169
|
+
const parsed = parseContractId(address);
|
|
141
170
|
const processed = {
|
|
142
|
-
name: contract.name || contractName,
|
|
143
|
-
address:
|
|
144
|
-
contractName,
|
|
171
|
+
name: contract.name || parsed.contractName,
|
|
172
|
+
address: parsed.address,
|
|
173
|
+
contractName: parsed.contractName,
|
|
145
174
|
abi: contract.abi,
|
|
146
175
|
source: "local",
|
|
147
176
|
metadata: { source: "direct" }
|
|
@@ -169,11 +198,11 @@ class PluginManager {
|
|
|
169
198
|
}
|
|
170
199
|
if (contract.abi) {
|
|
171
200
|
const addressStr = typeof contract.address === "string" ? contract.address : "";
|
|
172
|
-
const
|
|
201
|
+
const parsed = parseContractId(addressStr);
|
|
173
202
|
const processed = {
|
|
174
|
-
name: contract.name ||
|
|
175
|
-
address:
|
|
176
|
-
contractName:
|
|
203
|
+
name: contract.name || parsed.contractName || "unknown",
|
|
204
|
+
address: parsed.address || "unknown",
|
|
205
|
+
contractName: parsed.contractName || contract.name || "unknown",
|
|
177
206
|
abi: contract.abi,
|
|
178
207
|
source: "api",
|
|
179
208
|
metadata: contract.metadata
|
|
@@ -314,11 +343,10 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
314
343
|
return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
315
344
|
},
|
|
316
345
|
validateAddress: (address) => {
|
|
317
|
-
return validateStacksAddress(address.
|
|
346
|
+
return validateStacksAddress(parseContractId(address).address);
|
|
318
347
|
},
|
|
319
348
|
parseContractId: (contractId) => {
|
|
320
|
-
|
|
321
|
-
return { address, contractName };
|
|
349
|
+
return parseContractId(contractId);
|
|
322
350
|
},
|
|
323
351
|
formatCode: async (code) => {
|
|
324
352
|
const { formatCode: formatCode2 } = await Promise.resolve().then(() => (init_format(), exports_format));
|
|
@@ -349,25 +377,25 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
349
377
|
}
|
|
350
378
|
// src/plugins/clarinet/index.ts
|
|
351
379
|
import { initSimnet } from "@hirosystems/clarinet-sdk";
|
|
352
|
-
import { toCamelCase as
|
|
380
|
+
import { toCamelCase as toCamelCase4 } from "@secondlayer/stacks/clarity";
|
|
353
381
|
|
|
354
382
|
// src/generators/contract.ts
|
|
355
383
|
init_format();
|
|
356
384
|
import {
|
|
357
|
-
toCamelCase as
|
|
358
|
-
} from "@secondlayer/clarity
|
|
385
|
+
toCamelCase as toCamelCase3
|
|
386
|
+
} from "@secondlayer/stacks/clarity";
|
|
359
387
|
|
|
360
388
|
// src/utils/type-mapping.ts
|
|
361
389
|
import {
|
|
362
390
|
toCamelCase,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
} from "@secondlayer/clarity
|
|
391
|
+
isAbiList,
|
|
392
|
+
isAbiTuple,
|
|
393
|
+
isAbiOptional,
|
|
394
|
+
isAbiResponse,
|
|
395
|
+
isAbiBuffer,
|
|
396
|
+
isAbiStringAscii,
|
|
397
|
+
isAbiStringUtf8
|
|
398
|
+
} from "@secondlayer/stacks/clarity";
|
|
371
399
|
function clarityTypeToTS(type) {
|
|
372
400
|
if (typeof type === "string") {
|
|
373
401
|
switch (type) {
|
|
@@ -397,31 +425,31 @@ function clarityTypeToTS(type) {
|
|
|
397
425
|
}
|
|
398
426
|
}
|
|
399
427
|
}
|
|
400
|
-
if (
|
|
428
|
+
if (isAbiBuffer(type)) {
|
|
401
429
|
return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
|
|
402
430
|
}
|
|
403
|
-
if (
|
|
431
|
+
if (isAbiStringAscii(type) || isAbiStringUtf8(type)) {
|
|
404
432
|
return "string";
|
|
405
433
|
}
|
|
406
|
-
if (
|
|
434
|
+
if (isAbiOptional(type)) {
|
|
407
435
|
const innerType = clarityTypeToTS(type.optional);
|
|
408
436
|
if (innerType.includes(" | ") && !innerType.startsWith("(")) {
|
|
409
437
|
return `(${innerType}) | null`;
|
|
410
438
|
}
|
|
411
439
|
return `${innerType} | null`;
|
|
412
440
|
}
|
|
413
|
-
if (
|
|
441
|
+
if (isAbiList(type)) {
|
|
414
442
|
const innerType = clarityTypeToTS(type.list.type);
|
|
415
443
|
if (innerType.includes(" | ") && !innerType.startsWith("(")) {
|
|
416
444
|
return `(${innerType})[]`;
|
|
417
445
|
}
|
|
418
446
|
return `${innerType}[]`;
|
|
419
447
|
}
|
|
420
|
-
if (
|
|
448
|
+
if (isAbiTuple(type)) {
|
|
421
449
|
const fields = type.tuple.map((field) => `${toCamelCase(field.name)}: ${clarityTypeToTS(field.type)}`).join("; ");
|
|
422
450
|
return `{ ${fields} }`;
|
|
423
451
|
}
|
|
424
|
-
if (
|
|
452
|
+
if (isAbiResponse(type)) {
|
|
425
453
|
const okType = clarityTypeToTS(type.response.ok);
|
|
426
454
|
const errType = clarityTypeToTS(type.response.error);
|
|
427
455
|
return `{ ok: ${okType} } | { err: ${errType} }`;
|
|
@@ -432,6 +460,145 @@ function getTypeForArg(arg) {
|
|
|
432
460
|
return clarityTypeToTS(arg.type);
|
|
433
461
|
}
|
|
434
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
|
+
|
|
435
602
|
// src/generators/contract.ts
|
|
436
603
|
function generateNetworkUtils() {
|
|
437
604
|
return `/**
|
|
@@ -472,12 +639,12 @@ function generateValidationUtils() {
|
|
|
472
639
|
const CONTRACT_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9\\-]{0,127}$/;`;
|
|
473
640
|
}
|
|
474
641
|
async function generateContractInterface(contracts) {
|
|
475
|
-
const imports = `import { Cl, validateStacksAddress } from '@stacks
|
|
642
|
+
const imports = `import { Cl, validateStacksAddress } from '@secondlayer/stacks'`;
|
|
476
643
|
const header = `/**
|
|
477
644
|
* Generated by @secondlayer/cli
|
|
478
645
|
* DO NOT EDIT MANUALLY
|
|
479
646
|
*
|
|
480
|
-
* @requires @stacks
|
|
647
|
+
* @requires @secondlayer/stacks - Install with: npm install @secondlayer/stacks
|
|
481
648
|
*/`;
|
|
482
649
|
const validationUtils = generateValidationUtils();
|
|
483
650
|
const networkUtils = generateNetworkUtils();
|
|
@@ -523,7 +690,7 @@ function generateAbiConstant(name, abi) {
|
|
|
523
690
|
return `export const ${name}Abi = ${abiJson} as const`;
|
|
524
691
|
}
|
|
525
692
|
function generateMethod(func, address, contractName) {
|
|
526
|
-
const methodName =
|
|
693
|
+
const methodName = toCamelCase3(func.name);
|
|
527
694
|
if (func.args.length === 0) {
|
|
528
695
|
return `${methodName}() {
|
|
529
696
|
return {
|
|
@@ -536,7 +703,7 @@ function generateMethod(func, address, contractName) {
|
|
|
536
703
|
}
|
|
537
704
|
if (func.args.length === 1) {
|
|
538
705
|
const originalArgName = func.args[0].name;
|
|
539
|
-
const argName =
|
|
706
|
+
const argName = toCamelCase3(originalArgName);
|
|
540
707
|
const argType = getTypeForArg(func.args[0]);
|
|
541
708
|
const clarityConversion = generateClarityConversion(argName, func.args[0]);
|
|
542
709
|
return `${methodName}(...args: [{ ${argName}: ${argType} }] | [${argType}]) {
|
|
@@ -552,17 +719,17 @@ function generateMethod(func, address, contractName) {
|
|
|
552
719
|
}
|
|
553
720
|
}`;
|
|
554
721
|
}
|
|
555
|
-
const argsList = func.args.map((arg) =>
|
|
722
|
+
const argsList = func.args.map((arg) => toCamelCase3(arg.name)).join(", ");
|
|
556
723
|
const argsTypes = func.args.map((arg) => {
|
|
557
|
-
const camelName =
|
|
724
|
+
const camelName = toCamelCase3(arg.name);
|
|
558
725
|
return `${camelName}: ${getTypeForArg(arg)}`;
|
|
559
726
|
}).join("; ");
|
|
560
727
|
const argsArray = func.args.map((arg) => {
|
|
561
|
-
const argName =
|
|
728
|
+
const argName = toCamelCase3(arg.name);
|
|
562
729
|
return generateClarityConversion(argName, arg);
|
|
563
730
|
}).join(", ");
|
|
564
731
|
const objectAccess = func.args.map((arg) => {
|
|
565
|
-
const camelName =
|
|
732
|
+
const camelName = toCamelCase3(arg.name);
|
|
566
733
|
return `args[0].${camelName}`;
|
|
567
734
|
}).join(", ");
|
|
568
735
|
const positionTypes = func.args.map((arg) => getTypeForArg(arg)).join(", ");
|
|
@@ -579,211 +746,78 @@ function generateMethod(func, address, contractName) {
|
|
|
579
746
|
}
|
|
580
747
|
}`;
|
|
581
748
|
}
|
|
582
|
-
function
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
749
|
+
function generateMapsObject(maps, address, contractName) {
|
|
750
|
+
if (!maps || maps.length === 0) {
|
|
751
|
+
return "";
|
|
752
|
+
}
|
|
753
|
+
const mapMethods = maps.map((map) => {
|
|
754
|
+
const methodName = toCamelCase3(map.name);
|
|
755
|
+
const keyType = getTypeForArg({ type: map.key });
|
|
756
|
+
const valueType = getTypeForArg({ type: map.value });
|
|
757
|
+
const keyConversion = generateMapKeyConversion(map.key);
|
|
758
|
+
return `${methodName}: {
|
|
759
|
+
async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
|
|
760
|
+
try {
|
|
761
|
+
const { cvToJSON, serializeCV } = await import('@secondlayer/stacks/clarity');
|
|
762
|
+
const baseUrl = getApiUrl('${address}', options?.network);
|
|
763
|
+
const mapKey = ${keyConversion};
|
|
764
|
+
const keyHex = serializeCV(mapKey).toString('hex');
|
|
765
|
+
|
|
766
|
+
const response = await fetch(
|
|
767
|
+
\`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
|
|
768
|
+
{
|
|
769
|
+
method: 'POST',
|
|
770
|
+
headers: { 'Content-Type': 'application/json' },
|
|
771
|
+
body: JSON.stringify(keyHex)
|
|
602
772
|
}
|
|
603
|
-
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
if (!response.ok) {
|
|
776
|
+
throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
|
|
604
777
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
778
|
+
|
|
779
|
+
const result = await response.json();
|
|
780
|
+
if (!result.data || result.data === '0x09') {
|
|
781
|
+
return null; // none value
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const { deserializeCV } = await import('@secondlayer/stacks/clarity');
|
|
785
|
+
const cv = deserializeCV(result.data);
|
|
786
|
+
const parsed = cvToJSON(cv);
|
|
787
|
+
// Unwrap the (some ...) wrapper
|
|
788
|
+
return parsed.value?.value ?? parsed.value ?? null;
|
|
789
|
+
} catch (error) {
|
|
790
|
+
if (error instanceof Error) {
|
|
791
|
+
throw new Error(\`Map access failed for '${map.name}': \${error.message}\`);
|
|
792
|
+
}
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
keyType: ${JSON.stringify(map.key)} as const,
|
|
797
|
+
valueType: ${JSON.stringify(map.value)} as const
|
|
798
|
+
}`;
|
|
799
|
+
});
|
|
800
|
+
return `maps: {
|
|
801
|
+
${mapMethods.join(`,
|
|
802
|
+
|
|
803
|
+
`)}
|
|
804
|
+
}`;
|
|
805
|
+
}
|
|
806
|
+
function generateVarsObject(variables, address, contractName) {
|
|
807
|
+
if (!variables || variables.length === 0) {
|
|
808
|
+
return "";
|
|
613
809
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if (type.buff) {
|
|
618
|
-
return `(() => {
|
|
619
|
-
const value = ${argName};
|
|
620
|
-
// Direct Uint8Array
|
|
621
|
-
if (value instanceof Uint8Array) {
|
|
622
|
-
return Cl.buffer(value);
|
|
623
|
-
}
|
|
624
|
-
// Object notation with explicit type
|
|
625
|
-
if (typeof value === 'object' && value !== null && value.type && value.value) {
|
|
626
|
-
switch (value.type) {
|
|
627
|
-
case 'ascii':
|
|
628
|
-
return Cl.bufferFromAscii(value.value);
|
|
629
|
-
case 'utf8':
|
|
630
|
-
return Cl.bufferFromUtf8(value.value);
|
|
631
|
-
case 'hex':
|
|
632
|
-
return Cl.bufferFromHex(value.value);
|
|
633
|
-
default:
|
|
634
|
-
throw new Error(\`Unsupported buffer type: \${value.type}\`);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
// Auto-detect string type
|
|
638
|
-
if (typeof value === 'string') {
|
|
639
|
-
// Check for hex (0x prefix or pure hex pattern)
|
|
640
|
-
if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
|
|
641
|
-
return Cl.bufferFromHex(value);
|
|
642
|
-
}
|
|
643
|
-
// Check for non-ASCII characters (UTF-8) using char code comparison
|
|
644
|
-
const hasNonAscii = value.split('').some(char => char.charCodeAt(0) > 127);
|
|
645
|
-
if (hasNonAscii) {
|
|
646
|
-
return Cl.bufferFromUtf8(value);
|
|
647
|
-
}
|
|
648
|
-
// Default to ASCII for simple ASCII strings
|
|
649
|
-
return Cl.bufferFromAscii(value);
|
|
650
|
-
}
|
|
651
|
-
throw new Error(\`Invalid buffer value: \${value}\`);
|
|
652
|
-
})()`;
|
|
653
|
-
}
|
|
654
|
-
if (type.optional) {
|
|
655
|
-
const innerConversion = generateClarityConversion(argName, {
|
|
656
|
-
type: type.optional
|
|
657
|
-
});
|
|
658
|
-
return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
|
|
659
|
-
}
|
|
660
|
-
if (type.list) {
|
|
661
|
-
const innerConversion = generateClarityConversion("item", {
|
|
662
|
-
type: type.list.type
|
|
663
|
-
});
|
|
664
|
-
const maxLength = type.list.length || 100;
|
|
665
|
-
return `(() => {
|
|
666
|
-
const listValue = ${argName};
|
|
667
|
-
if (listValue.length > ${maxLength}) {
|
|
668
|
-
throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
|
|
669
|
-
}
|
|
670
|
-
return Cl.list(listValue.map(item => ${innerConversion}));
|
|
671
|
-
})()`;
|
|
672
|
-
}
|
|
673
|
-
if (type.tuple) {
|
|
674
|
-
const requiredFields = type.tuple.map((f) => f.name);
|
|
675
|
-
const fieldNames = JSON.stringify(requiredFields);
|
|
676
|
-
const fields = type.tuple.map((field) => {
|
|
677
|
-
const camelFieldName = toCamelCase2(field.name);
|
|
678
|
-
const fieldConversion = generateClarityConversion(`tupleValue.${camelFieldName}`, { type: field.type });
|
|
679
|
-
return `"${field.name}": ${fieldConversion}`;
|
|
680
|
-
}).join(", ");
|
|
681
|
-
return `(() => {
|
|
682
|
-
const tupleValue = ${argName};
|
|
683
|
-
const requiredFields = ${fieldNames};
|
|
684
|
-
for (const fieldName of requiredFields) {
|
|
685
|
-
const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
|
|
686
|
-
if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
|
|
687
|
-
throw new Error(\`Missing tuple field: \${fieldName}\`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
return Cl.tuple({ ${fields} });
|
|
691
|
-
})()`;
|
|
692
|
-
}
|
|
693
|
-
if (type.response) {
|
|
694
|
-
const okConversion = generateClarityConversion(`responseValue.ok`, {
|
|
695
|
-
type: type.response.ok
|
|
696
|
-
});
|
|
697
|
-
const errConversion = generateClarityConversion(`responseValue.err`, {
|
|
698
|
-
type: type.response.error
|
|
699
|
-
});
|
|
700
|
-
return `(() => {
|
|
701
|
-
const responseValue = ${argName};
|
|
702
|
-
const hasOk = 'ok' in responseValue;
|
|
703
|
-
const hasErr = 'err' in responseValue;
|
|
704
|
-
if (hasOk && !hasErr) {
|
|
705
|
-
return Cl.ok(${okConversion});
|
|
706
|
-
}
|
|
707
|
-
if (hasErr && !hasOk) {
|
|
708
|
-
return Cl.error(${errConversion});
|
|
709
|
-
}
|
|
710
|
-
throw new Error("Response must have exactly 'ok' or 'err' property");
|
|
711
|
-
})()`;
|
|
712
|
-
}
|
|
713
|
-
return `${argName}`;
|
|
714
|
-
}
|
|
715
|
-
function generateMapsObject(maps, address, contractName) {
|
|
716
|
-
if (!maps || maps.length === 0) {
|
|
717
|
-
return "";
|
|
718
|
-
}
|
|
719
|
-
const mapMethods = maps.map((map) => {
|
|
720
|
-
const methodName = toCamelCase2(map.name);
|
|
721
|
-
const keyType = getTypeForArg({ type: map.key });
|
|
722
|
-
const valueType = getTypeForArg({ type: map.value });
|
|
723
|
-
const keyConversion = generateMapKeyConversion(map.key);
|
|
724
|
-
return `${methodName}: {
|
|
725
|
-
async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
|
|
726
|
-
try {
|
|
727
|
-
const { cvToJSON, serializeCV } = await import('@stacks/transactions');
|
|
728
|
-
const baseUrl = getApiUrl('${address}', options?.network);
|
|
729
|
-
const mapKey = ${keyConversion};
|
|
730
|
-
const keyHex = serializeCV(mapKey).toString('hex');
|
|
731
|
-
|
|
732
|
-
const response = await fetch(
|
|
733
|
-
\`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
|
|
734
|
-
{
|
|
735
|
-
method: 'POST',
|
|
736
|
-
headers: { 'Content-Type': 'application/json' },
|
|
737
|
-
body: JSON.stringify(keyHex)
|
|
738
|
-
}
|
|
739
|
-
);
|
|
740
|
-
|
|
741
|
-
if (!response.ok) {
|
|
742
|
-
throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
const result = await response.json();
|
|
746
|
-
if (!result.data || result.data === '0x09') {
|
|
747
|
-
return null; // none value
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
const { deserializeCV } = await import('@stacks/transactions');
|
|
751
|
-
const cv = deserializeCV(result.data);
|
|
752
|
-
const parsed = cvToJSON(cv);
|
|
753
|
-
// Unwrap the (some ...) wrapper
|
|
754
|
-
return parsed.value?.value ?? parsed.value ?? null;
|
|
755
|
-
} catch (error) {
|
|
756
|
-
if (error instanceof Error) {
|
|
757
|
-
throw new Error(\`Map access failed for '${map.name}': \${error.message}\`);
|
|
758
|
-
}
|
|
759
|
-
throw error;
|
|
760
|
-
}
|
|
761
|
-
},
|
|
762
|
-
keyType: ${JSON.stringify(map.key)} as const,
|
|
763
|
-
valueType: ${JSON.stringify(map.value)} as const
|
|
764
|
-
}`;
|
|
765
|
-
});
|
|
766
|
-
return `maps: {
|
|
767
|
-
${mapMethods.join(`,
|
|
768
|
-
|
|
769
|
-
`)}
|
|
770
|
-
}`;
|
|
771
|
-
}
|
|
772
|
-
function generateVarsObject(variables, address, contractName) {
|
|
773
|
-
if (!variables || variables.length === 0) {
|
|
774
|
-
return "";
|
|
775
|
-
}
|
|
776
|
-
const dataVars = variables.filter((v) => v.access === "variable");
|
|
777
|
-
if (dataVars.length === 0) {
|
|
778
|
-
return "";
|
|
810
|
+
const dataVars = variables.filter((v) => v.access === "variable");
|
|
811
|
+
if (dataVars.length === 0) {
|
|
812
|
+
return "";
|
|
779
813
|
}
|
|
780
814
|
const varMethods = dataVars.map((variable) => {
|
|
781
|
-
const methodName =
|
|
815
|
+
const methodName = toCamelCase3(variable.name);
|
|
782
816
|
const valueType = getTypeForArg({ type: variable.type });
|
|
783
817
|
return `${methodName}: {
|
|
784
818
|
async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
|
|
785
819
|
try {
|
|
786
|
-
const { cvToJSON, deserializeCV } = await import('@stacks/
|
|
820
|
+
const { cvToJSON, deserializeCV } = await import('@secondlayer/stacks/clarity');
|
|
787
821
|
const baseUrl = getApiUrl('${address}', options?.network);
|
|
788
822
|
|
|
789
823
|
const response = await fetch(
|
|
@@ -823,12 +857,12 @@ function generateConstantsObject(variables, address, contractName) {
|
|
|
823
857
|
return "";
|
|
824
858
|
}
|
|
825
859
|
const constMethods = constants.map((constant) => {
|
|
826
|
-
const methodName =
|
|
860
|
+
const methodName = toCamelCase3(constant.name);
|
|
827
861
|
const valueType = getTypeForArg({ type: constant.type });
|
|
828
862
|
return `${methodName}: {
|
|
829
863
|
async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
|
|
830
864
|
try {
|
|
831
|
-
const { cvToJSON, deserializeCV } = await import('@stacks/
|
|
865
|
+
const { cvToJSON, deserializeCV } = await import('@secondlayer/stacks/clarity');
|
|
832
866
|
const baseUrl = getApiUrl('${address}', options?.network);
|
|
833
867
|
|
|
834
868
|
const response = await fetch(
|
|
@@ -862,7 +896,7 @@ function generateConstantsObject(variables, address, contractName) {
|
|
|
862
896
|
function generateMapKeyConversion(keyType) {
|
|
863
897
|
if (keyType.tuple) {
|
|
864
898
|
const fields = keyType.tuple.map((field) => {
|
|
865
|
-
const camelFieldName =
|
|
899
|
+
const camelFieldName = toCamelCase3(field.name);
|
|
866
900
|
const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
|
|
867
901
|
return `"${field.name}": ${fieldConversion}`;
|
|
868
902
|
}).join(", ");
|
|
@@ -871,12 +905,165 @@ function generateMapKeyConversion(keyType) {
|
|
|
871
905
|
return generateClarityConversion("key", { type: keyType });
|
|
872
906
|
}
|
|
873
907
|
|
|
908
|
+
// src/utils/abi-compat.ts
|
|
909
|
+
function normalizeAccess(access) {
|
|
910
|
+
if (access === "read_only")
|
|
911
|
+
return "read-only";
|
|
912
|
+
return access;
|
|
913
|
+
}
|
|
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
|
+
|
|
874
1061
|
// src/plugins/clarinet/index.ts
|
|
875
1062
|
function sanitizeContractName(name) {
|
|
876
|
-
return
|
|
1063
|
+
return toCamelCase4(name);
|
|
877
1064
|
}
|
|
878
1065
|
async function isUserDefinedContract(contractId, manifestPath) {
|
|
879
|
-
const
|
|
1066
|
+
const { address, contractName } = parseContractId(contractId);
|
|
880
1067
|
try {
|
|
881
1068
|
const { promises: fs2 } = await import("fs");
|
|
882
1069
|
const tomlContent = await fs2.readFile(manifestPath, "utf-8");
|
|
@@ -920,7 +1107,7 @@ var clarinet = (options = {}) => {
|
|
|
920
1107
|
const contractInterfaces = simnet.getContractsInterfaces();
|
|
921
1108
|
const contracts = [];
|
|
922
1109
|
for (const [contractId, abi] of contractInterfaces) {
|
|
923
|
-
const
|
|
1110
|
+
const { contractName } = parseContractId(contractId);
|
|
924
1111
|
if (!await isUserDefinedContract(contractId, manifestPath)) {
|
|
925
1112
|
if (options.debug) {
|
|
926
1113
|
console.log(`\uD83D\uDEAB Skipping system contract: ${contractId}`);
|
|
@@ -937,7 +1124,7 @@ var clarinet = (options = {}) => {
|
|
|
937
1124
|
contracts.push({
|
|
938
1125
|
name: sanitizedName,
|
|
939
1126
|
address: contractId,
|
|
940
|
-
abi,
|
|
1127
|
+
abi: normalizeAbi(abi),
|
|
941
1128
|
_clarinetSource: true
|
|
942
1129
|
});
|
|
943
1130
|
}
|
|
@@ -983,174 +1170,52 @@ async function hasClarinetProject(path2 = "./Clarinet.toml") {
|
|
|
983
1170
|
}
|
|
984
1171
|
}
|
|
985
1172
|
// src/plugins/actions/generators.ts
|
|
986
|
-
import { toCamelCase as
|
|
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";
|
|
987
1177
|
function generateArgsSignature(args) {
|
|
988
1178
|
if (args.length === 0)
|
|
989
1179
|
return "";
|
|
990
1180
|
const argsTypes = args.map((arg) => {
|
|
991
|
-
const camelName =
|
|
1181
|
+
const camelName = toCamelCase5(arg.name);
|
|
992
1182
|
return `${camelName}: ${getTypeForArg(arg)}`;
|
|
993
1183
|
}).join("; ");
|
|
994
1184
|
return `args: { ${argsTypes} }, `;
|
|
995
1185
|
}
|
|
996
|
-
function generateClarityArgs(args
|
|
1186
|
+
function generateClarityArgs(args) {
|
|
997
1187
|
if (args.length === 0)
|
|
998
1188
|
return "";
|
|
999
1189
|
return args.map((arg) => {
|
|
1000
|
-
const argName = `args.${
|
|
1001
|
-
return
|
|
1190
|
+
const argName = `args.${toCamelCase5(arg.name)}`;
|
|
1191
|
+
return generateClarityConversion(argName, arg);
|
|
1002
1192
|
}).join(", ");
|
|
1003
1193
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
case "bool":
|
|
1013
|
-
return `Cl.bool(${argName})`;
|
|
1014
|
-
case "principal":
|
|
1015
|
-
case "trait_reference":
|
|
1016
|
-
return `(() => {
|
|
1017
|
-
const [address, contractName] = ${argName}.split(".") as [string, string | undefined];
|
|
1018
|
-
if (!validateStacksAddress(address)) {
|
|
1019
|
-
throw new Error("Invalid Stacks address format");
|
|
1020
|
-
}
|
|
1021
|
-
if (contractName !== undefined) {
|
|
1022
|
-
if (!CONTRACT_NAME_REGEX.test(contractName)) {
|
|
1023
|
-
throw new Error("Invalid contract name format: must start with letter and contain only letters, numbers, and hyphens");
|
|
1024
|
-
}
|
|
1025
|
-
return Cl.contractPrincipal(address, contractName);
|
|
1026
|
-
}
|
|
1027
|
-
return Cl.standardPrincipal(${argName});
|
|
1028
|
-
})()`;
|
|
1029
|
-
default:
|
|
1030
|
-
return `${argName}`;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
if (type["string-ascii"]) {
|
|
1034
|
-
return `Cl.stringAscii(${argName})`;
|
|
1194
|
+
|
|
1195
|
+
// src/plugins/actions/generators.ts
|
|
1196
|
+
function generateReadHelpers(contract, options) {
|
|
1197
|
+
const { abi } = contract;
|
|
1198
|
+
const functions = abi.functions || [];
|
|
1199
|
+
const readOnlyFunctions = functions.filter((f) => f.access === "read-only");
|
|
1200
|
+
if (readOnlyFunctions.length === 0) {
|
|
1201
|
+
return "";
|
|
1035
1202
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
case 'ascii':
|
|
1048
|
-
return Cl.bufferFromAscii(value.value);
|
|
1049
|
-
case 'utf8':
|
|
1050
|
-
return Cl.bufferFromUtf8(value.value);
|
|
1051
|
-
case 'hex':
|
|
1052
|
-
return Cl.bufferFromHex(value.value);
|
|
1053
|
-
default:
|
|
1054
|
-
throw new Error(\`Unsupported buffer type: \${value.type}\`);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
if (typeof value === 'string') {
|
|
1058
|
-
if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
|
|
1059
|
-
return Cl.bufferFromHex(value);
|
|
1060
|
-
}
|
|
1061
|
-
const hasNonAscii = value.split('').some(char => char.charCodeAt(0) > 127);
|
|
1062
|
-
if (hasNonAscii) {
|
|
1063
|
-
return Cl.bufferFromUtf8(value);
|
|
1064
|
-
}
|
|
1065
|
-
return Cl.bufferFromAscii(value);
|
|
1066
|
-
}
|
|
1067
|
-
throw new Error(\`Invalid buffer value: \${value}\`);
|
|
1068
|
-
})()`;
|
|
1069
|
-
}
|
|
1070
|
-
if (type.optional) {
|
|
1071
|
-
const innerConversion = generateClarityConversion2(argName, {
|
|
1072
|
-
type: type.optional
|
|
1073
|
-
});
|
|
1074
|
-
return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
|
|
1075
|
-
}
|
|
1076
|
-
if (type.list) {
|
|
1077
|
-
const innerConversion = generateClarityConversion2("item", {
|
|
1078
|
-
type: type.list.type
|
|
1079
|
-
});
|
|
1080
|
-
const maxLength = type.list.length || 100;
|
|
1081
|
-
return `(() => {
|
|
1082
|
-
const listValue = ${argName};
|
|
1083
|
-
if (listValue.length > ${maxLength}) {
|
|
1084
|
-
throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
|
|
1085
|
-
}
|
|
1086
|
-
return Cl.list(listValue.map(item => ${innerConversion}));
|
|
1087
|
-
})()`;
|
|
1088
|
-
}
|
|
1089
|
-
if (type.tuple) {
|
|
1090
|
-
const requiredFields = type.tuple.map((f) => f.name);
|
|
1091
|
-
const fieldNames = JSON.stringify(requiredFields);
|
|
1092
|
-
const fields = type.tuple.map((field) => {
|
|
1093
|
-
const camelFieldName = toCamelCase4(field.name);
|
|
1094
|
-
const fieldConversion = generateClarityConversion2(`tupleValue.${camelFieldName}`, { type: field.type });
|
|
1095
|
-
return `"${field.name}": ${fieldConversion}`;
|
|
1096
|
-
}).join(", ");
|
|
1097
|
-
return `(() => {
|
|
1098
|
-
const tupleValue = ${argName};
|
|
1099
|
-
const requiredFields = ${fieldNames};
|
|
1100
|
-
for (const fieldName of requiredFields) {
|
|
1101
|
-
const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
|
|
1102
|
-
if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
|
|
1103
|
-
throw new Error(\`Missing tuple field: \${fieldName}\`);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
return Cl.tuple({ ${fields} });
|
|
1107
|
-
})()`;
|
|
1108
|
-
}
|
|
1109
|
-
if (type.response) {
|
|
1110
|
-
const okConversion = generateClarityConversion2(`responseValue.ok`, {
|
|
1111
|
-
type: type.response.ok
|
|
1112
|
-
});
|
|
1113
|
-
const errConversion = generateClarityConversion2(`responseValue.err`, {
|
|
1114
|
-
type: type.response.error
|
|
1115
|
-
});
|
|
1116
|
-
return `(() => {
|
|
1117
|
-
const responseValue = ${argName};
|
|
1118
|
-
const hasOk = 'ok' in responseValue;
|
|
1119
|
-
const hasErr = 'err' in responseValue;
|
|
1120
|
-
if (hasOk && !hasErr) {
|
|
1121
|
-
return Cl.ok(${okConversion});
|
|
1122
|
-
}
|
|
1123
|
-
if (hasErr && !hasOk) {
|
|
1124
|
-
return Cl.error(${errConversion});
|
|
1125
|
-
}
|
|
1126
|
-
throw new Error("Response must have exactly 'ok' or 'err' property");
|
|
1127
|
-
})()`;
|
|
1128
|
-
}
|
|
1129
|
-
return `${argName}`;
|
|
1130
|
-
}
|
|
1131
|
-
function generateReadHelpers(contract, options) {
|
|
1132
|
-
const { abi, name } = contract;
|
|
1133
|
-
const functions = abi.functions || [];
|
|
1134
|
-
const readOnlyFunctions = functions.filter((f) => f.access === "read_only" || f.access === "read-only");
|
|
1135
|
-
if (readOnlyFunctions.length === 0) {
|
|
1136
|
-
return "";
|
|
1137
|
-
}
|
|
1138
|
-
const filteredFunctions = readOnlyFunctions.filter((func) => {
|
|
1139
|
-
if (options.includeFunctions && !options.includeFunctions.includes(func.name)) {
|
|
1140
|
-
return false;
|
|
1141
|
-
}
|
|
1142
|
-
if (options.excludeFunctions && options.excludeFunctions.includes(func.name)) {
|
|
1143
|
-
return false;
|
|
1144
|
-
}
|
|
1145
|
-
return true;
|
|
1146
|
-
});
|
|
1147
|
-
if (filteredFunctions.length === 0) {
|
|
1148
|
-
return "";
|
|
1203
|
+
const filteredFunctions = readOnlyFunctions.filter((func) => {
|
|
1204
|
+
if (options.includeFunctions && !options.includeFunctions.includes(func.name)) {
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1207
|
+
if (options.excludeFunctions && options.excludeFunctions.includes(func.name)) {
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
return true;
|
|
1211
|
+
});
|
|
1212
|
+
if (filteredFunctions.length === 0) {
|
|
1213
|
+
return "";
|
|
1149
1214
|
}
|
|
1150
1215
|
const helpers = filteredFunctions.map((func) => {
|
|
1151
|
-
const methodName =
|
|
1216
|
+
const methodName = toCamelCase6(func.name);
|
|
1152
1217
|
const argsSignature = generateArgsSignature(func.args);
|
|
1153
|
-
const clarityArgs = generateClarityArgs(func.args
|
|
1218
|
+
const clarityArgs = generateClarityArgs(func.args);
|
|
1154
1219
|
return `async ${methodName}(${argsSignature}options?: {
|
|
1155
1220
|
network?: 'mainnet' | 'testnet' | 'devnet';
|
|
1156
1221
|
senderAddress?: string;
|
|
@@ -1172,7 +1237,7 @@ function generateReadHelpers(contract, options) {
|
|
|
1172
1237
|
}`;
|
|
1173
1238
|
}
|
|
1174
1239
|
function generateWriteHelpers(contract, options) {
|
|
1175
|
-
const { abi
|
|
1240
|
+
const { abi } = contract;
|
|
1176
1241
|
const functions = abi.functions || [];
|
|
1177
1242
|
const envVarName = options.senderKeyEnv ?? "STX_SENDER_KEY";
|
|
1178
1243
|
const publicFunctions = functions.filter((f) => f.access === "public");
|
|
@@ -1192,9 +1257,9 @@ function generateWriteHelpers(contract, options) {
|
|
|
1192
1257
|
return "";
|
|
1193
1258
|
}
|
|
1194
1259
|
const helpers = filteredFunctions.map((func) => {
|
|
1195
|
-
const methodName =
|
|
1260
|
+
const methodName = toCamelCase6(func.name);
|
|
1196
1261
|
const argsSignature = generateArgsSignature(func.args);
|
|
1197
|
-
const clarityArgs = generateClarityArgs(func.args
|
|
1262
|
+
const clarityArgs = generateClarityArgs(func.args);
|
|
1198
1263
|
return `async ${methodName}(${argsSignature}senderKey?: string, options?: {
|
|
1199
1264
|
network?: 'mainnet' | 'testnet' | 'devnet';
|
|
1200
1265
|
fee?: string | number | undefined;
|
|
@@ -1288,28 +1353,19 @@ var actions = (options = {}) => {
|
|
|
1288
1353
|
};
|
|
1289
1354
|
};
|
|
1290
1355
|
function addRequiredImports(content) {
|
|
1291
|
-
const
|
|
1292
|
-
const match = content.match(
|
|
1356
|
+
const stacksImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]@secondlayer\/stacks['"];/;
|
|
1357
|
+
const match = content.match(stacksImportRegex);
|
|
1293
1358
|
if (match) {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
];
|
|
1299
|
-
const newImports = requiredImports.filter((imp) => !existingImports.includes(imp)).join(", ");
|
|
1300
|
-
if (newImports) {
|
|
1301
|
-
const updatedImport = `import { ${existingImports}, ${newImports} } from '@stacks/transactions';`;
|
|
1302
|
-
let updatedContent = content.replace(transactionsImportRegex, updatedImport);
|
|
1303
|
-
if (!updatedContent.includes("type PostCondition")) {
|
|
1304
|
-
updatedContent = updatedContent.replace(updatedImport, `${updatedImport}
|
|
1305
|
-
import type { PostCondition } from '@stacks/transactions';`);
|
|
1306
|
-
}
|
|
1307
|
-
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';`);
|
|
1308
1363
|
}
|
|
1309
|
-
if (!
|
|
1310
|
-
|
|
1311
|
-
import type { PostCondition } from '@stacks
|
|
1364
|
+
if (!updatedContent.includes("type PostCondition")) {
|
|
1365
|
+
updatedContent = updatedContent.replace(stacksImportRegex, `${match[0]}
|
|
1366
|
+
import type { PostCondition } from '@secondlayer/stacks';`);
|
|
1312
1367
|
}
|
|
1368
|
+
return updatedContent;
|
|
1313
1369
|
}
|
|
1314
1370
|
return content;
|
|
1315
1371
|
}
|
|
@@ -1425,329 +1481,93 @@ export function useSecondLayerConfig(): SecondLayerReactConfig {
|
|
|
1425
1481
|
|
|
1426
1482
|
// src/plugins/react/generators/generic.ts
|
|
1427
1483
|
init_format();
|
|
1428
|
-
var GENERIC_HOOKS = [
|
|
1429
|
-
"useAccount",
|
|
1430
|
-
"useConnect",
|
|
1431
|
-
"useDisconnect",
|
|
1432
|
-
"useNetwork",
|
|
1433
|
-
"useContract",
|
|
1434
|
-
"useOpenSTXTransfer",
|
|
1435
|
-
"useSignMessage",
|
|
1436
|
-
"useDeployContract",
|
|
1437
|
-
"useReadContract",
|
|
1438
|
-
"useTransaction",
|
|
1439
|
-
"useBlock",
|
|
1440
|
-
"useAccountTransactions",
|
|
1441
|
-
"useWaitForTransaction"
|
|
1442
|
-
];
|
|
1443
|
-
async function generateGenericHooks(excludeList = []) {
|
|
1444
|
-
const hooksToGenerate = GENERIC_HOOKS.filter((hookName) => !excludeList.includes(hookName));
|
|
1445
|
-
const imports = `import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
1446
|
-
import { useState, useCallback } from 'react'
|
|
1447
|
-
import { useSecondLayerConfig } from './provider'
|
|
1448
|
-
import { connect, disconnect, isConnected, request, openContractCall as stacksOpenContractCall, openSTXTransfer, openSignatureRequestPopup, openContractDeploy } from '@stacks/connect'
|
|
1449
|
-
import { Cl, validateStacksAddress } from '@stacks/transactions'
|
|
1450
|
-
import type { PostCondition } from '@stacks/transactions'
|
|
1451
|
-
import type { ExtractFunctionArgs, ExtractFunctionNames, ClarityContract } from '@secondlayer/clarity-types'
|
|
1452
1484
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
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)
|
|
1458
1490
|
|
|
1459
|
-
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
if (!response.ok) throw new Error(\`Failed to fetch transaction: \${response.statusText}\`)
|
|
1463
|
-
return response.json()
|
|
1464
|
-
}
|
|
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 []
|
|
1465
1494
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
}
|
|
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
|
+
}
|
|
1472
1502
|
|
|
1473
|
-
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}
|
|
1479
|
-
const header = `/**
|
|
1480
|
-
* Generated generic Stacks React hooks
|
|
1481
|
-
* DO NOT EDIT MANUALLY
|
|
1482
|
-
*/`;
|
|
1483
|
-
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
|
+
}
|
|
1484
1509
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1510
|
+
// Object notation with explicit type
|
|
1511
|
+
if (typeof value === 'object' && value !== null && value.type && value.value) {
|
|
1512
|
+
switch (value.type) {
|
|
1513
|
+
case 'ascii':
|
|
1514
|
+
return Cl.bufferFromAscii(value.value)
|
|
1515
|
+
case 'utf8':
|
|
1516
|
+
return Cl.bufferFromUtf8(value.value)
|
|
1517
|
+
case 'hex':
|
|
1518
|
+
return Cl.bufferFromHex(value.value)
|
|
1519
|
+
default:
|
|
1520
|
+
throw new Error(\`Unsupported buffer type: \${value.type}\`)
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1487
1523
|
|
|
1488
|
-
|
|
1524
|
+
// Auto-detect string type
|
|
1525
|
+
if (typeof value === 'string') {
|
|
1526
|
+
// 1. Check for hex (0x prefix or pure hex pattern)
|
|
1527
|
+
if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
|
|
1528
|
+
return Cl.bufferFromHex(value)
|
|
1529
|
+
}
|
|
1489
1530
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
switch (hookName) {
|
|
1495
|
-
case "useAccount":
|
|
1496
|
-
return `export function useAccount() {
|
|
1497
|
-
const config = useSecondLayerConfig()
|
|
1498
|
-
|
|
1499
|
-
return useQuery({
|
|
1500
|
-
queryKey: ['stacks-account', config.network],
|
|
1501
|
-
queryFn: async () => {
|
|
1502
|
-
try {
|
|
1503
|
-
// Check if already connected using @stacks/connect v8
|
|
1504
|
-
const connected = isConnected()
|
|
1505
|
-
|
|
1506
|
-
if (!connected) {
|
|
1507
|
-
return {
|
|
1508
|
-
address: undefined,
|
|
1509
|
-
addresses: undefined,
|
|
1510
|
-
isConnected: false,
|
|
1511
|
-
isConnecting: false,
|
|
1512
|
-
isDisconnected: true,
|
|
1513
|
-
status: 'disconnected' as const
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1531
|
+
// 2. Check for non-ASCII characters (UTF-8)
|
|
1532
|
+
if (!/^[\\x00-\\x7F]*$/.test(value)) {
|
|
1533
|
+
return Cl.bufferFromUtf8(value)
|
|
1534
|
+
}
|
|
1516
1535
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1536
|
+
// 3. Default to ASCII for simple ASCII strings
|
|
1537
|
+
return Cl.bufferFromAscii(value)
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
throw new Error(\`Invalid buffer value: \${value}\`)
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// Helper function to convert a single JS value to ClarityValue
|
|
1544
|
+
const convertJSValueToClarityValue = (value: any, type: any): any => {
|
|
1545
|
+
if (typeof type === 'string') {
|
|
1546
|
+
switch (type) {
|
|
1547
|
+
case 'uint128':
|
|
1548
|
+
return Cl.uint(value)
|
|
1549
|
+
case 'int128':
|
|
1550
|
+
return Cl.int(value)
|
|
1551
|
+
case 'bool':
|
|
1552
|
+
return Cl.bool(value)
|
|
1553
|
+
case 'principal':
|
|
1554
|
+
if (!validateStacksAddress(value.split('.')[0])) {
|
|
1555
|
+
throw new Error('Invalid Stacks address format')
|
|
1528
1556
|
}
|
|
1529
|
-
|
|
1557
|
+
if (value.includes('.')) {
|
|
1558
|
+
const [address, contractName] = value.split('.')
|
|
1559
|
+
return Cl.contractPrincipal(address, contractName)
|
|
1560
|
+
} else {
|
|
1561
|
+
return Cl.standardPrincipal(value)
|
|
1562
|
+
}
|
|
1563
|
+
default:
|
|
1564
|
+
return value
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1530
1567
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
.map((addr: any) => addr.address)
|
|
1535
|
-
|
|
1536
|
-
return {
|
|
1537
|
-
address: stxAddresses[0] || undefined,
|
|
1538
|
-
addresses: stxAddresses,
|
|
1539
|
-
isConnected: true,
|
|
1540
|
-
isConnecting: false,
|
|
1541
|
-
isDisconnected: false,
|
|
1542
|
-
status: 'connected' as const
|
|
1543
|
-
}
|
|
1544
|
-
} catch (error) {
|
|
1545
|
-
// Handle case where wallet is not available or user rejected
|
|
1546
|
-
return {
|
|
1547
|
-
address: undefined,
|
|
1548
|
-
addresses: undefined,
|
|
1549
|
-
isConnected: false,
|
|
1550
|
-
isConnecting: false,
|
|
1551
|
-
isDisconnected: true,
|
|
1552
|
-
status: 'disconnected' as const
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
},
|
|
1556
|
-
refetchOnWindowFocus: false,
|
|
1557
|
-
retry: false,
|
|
1558
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
1559
|
-
refetchInterval: 1000 * 30, // Refetch every 30 seconds to detect wallet changes
|
|
1560
|
-
})
|
|
1561
|
-
}`;
|
|
1562
|
-
case "useConnect":
|
|
1563
|
-
return `export function useConnect() {
|
|
1564
|
-
const queryClient = useQueryClient()
|
|
1565
|
-
|
|
1566
|
-
const mutation = useMutation({
|
|
1567
|
-
mutationFn: async (options: { forceWalletSelect?: boolean } = {}) => {
|
|
1568
|
-
// Use @stacks/connect v8 connect method
|
|
1569
|
-
return await connect(options)
|
|
1570
|
-
},
|
|
1571
|
-
onSuccess: () => {
|
|
1572
|
-
// Invalidate account queries to refetch connection state
|
|
1573
|
-
queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
|
|
1574
|
-
},
|
|
1575
|
-
onError: (error) => {
|
|
1576
|
-
console.error('Connection failed:', error)
|
|
1577
|
-
}
|
|
1578
|
-
})
|
|
1579
|
-
|
|
1580
|
-
return {
|
|
1581
|
-
// Custom connect function that works without arguments
|
|
1582
|
-
connect: (options?: { forceWalletSelect?: boolean }) => {
|
|
1583
|
-
return mutation.mutate(options || {})
|
|
1584
|
-
},
|
|
1585
|
-
connectAsync: async (options?: { forceWalletSelect?: boolean }) => {
|
|
1586
|
-
return mutation.mutateAsync(options || {})
|
|
1587
|
-
},
|
|
1588
|
-
// Expose all the mutation state
|
|
1589
|
-
isPending: mutation.isPending,
|
|
1590
|
-
isError: mutation.isError,
|
|
1591
|
-
isSuccess: mutation.isSuccess,
|
|
1592
|
-
error: mutation.error,
|
|
1593
|
-
data: mutation.data,
|
|
1594
|
-
reset: mutation.reset,
|
|
1595
|
-
// Keep the original mutate/mutateAsync for advanced users
|
|
1596
|
-
mutate: mutation.mutate,
|
|
1597
|
-
mutateAsync: mutation.mutateAsync
|
|
1598
|
-
}
|
|
1599
|
-
}`;
|
|
1600
|
-
case "useDisconnect":
|
|
1601
|
-
return `export function useDisconnect() {
|
|
1602
|
-
const queryClient = useQueryClient()
|
|
1603
|
-
|
|
1604
|
-
const mutation = useMutation({
|
|
1605
|
-
mutationFn: async () => {
|
|
1606
|
-
// Use @stacks/connect v8 disconnect method
|
|
1607
|
-
return await disconnect()
|
|
1608
|
-
},
|
|
1609
|
-
onSuccess: () => {
|
|
1610
|
-
// Clear all cached data on disconnect
|
|
1611
|
-
queryClient.clear()
|
|
1612
|
-
},
|
|
1613
|
-
onError: (error) => {
|
|
1614
|
-
console.error('Disconnect failed:', error)
|
|
1615
|
-
}
|
|
1616
|
-
})
|
|
1617
|
-
|
|
1618
|
-
return {
|
|
1619
|
-
// Custom disconnect function
|
|
1620
|
-
disconnect: () => {
|
|
1621
|
-
return mutation.mutate()
|
|
1622
|
-
},
|
|
1623
|
-
disconnectAsync: async () => {
|
|
1624
|
-
return mutation.mutateAsync()
|
|
1625
|
-
},
|
|
1626
|
-
// Expose all the mutation state
|
|
1627
|
-
isPending: mutation.isPending,
|
|
1628
|
-
isError: mutation.isError,
|
|
1629
|
-
isSuccess: mutation.isSuccess,
|
|
1630
|
-
error: mutation.error,
|
|
1631
|
-
data: mutation.data,
|
|
1632
|
-
reset: mutation.reset,
|
|
1633
|
-
// Keep the original mutate/mutateAsync for advanced users
|
|
1634
|
-
mutate: mutation.mutate,
|
|
1635
|
-
mutateAsync: mutation.mutateAsync
|
|
1636
|
-
}
|
|
1637
|
-
}`;
|
|
1638
|
-
case "useNetwork":
|
|
1639
|
-
return `export function useNetwork() {
|
|
1640
|
-
const config = useSecondLayerConfig()
|
|
1641
|
-
|
|
1642
|
-
return useQuery({
|
|
1643
|
-
queryKey: ['stacks-network', config.network],
|
|
1644
|
-
queryFn: async () => {
|
|
1645
|
-
// Currently read-only from config
|
|
1646
|
-
// Future: Use request('stx_getNetworks') when wallet support improves
|
|
1647
|
-
const network = config.network
|
|
1648
|
-
|
|
1649
|
-
return {
|
|
1650
|
-
network,
|
|
1651
|
-
isMainnet: network === 'mainnet',
|
|
1652
|
-
isTestnet: network === 'testnet',
|
|
1653
|
-
isDevnet: network === 'devnet',
|
|
1654
|
-
// Future: Add switchNetwork when wallets support stx_networkChange
|
|
1655
|
-
// switchNetwork: async (newNetwork: string) => {
|
|
1656
|
-
// return await request('wallet_changeNetwork', { network: newNetwork })
|
|
1657
|
-
// }
|
|
1658
|
-
}
|
|
1659
|
-
},
|
|
1660
|
-
staleTime: Infinity, // Network config rarely changes
|
|
1661
|
-
refetchOnWindowFocus: false,
|
|
1662
|
-
retry: false
|
|
1663
|
-
})
|
|
1664
|
-
}`;
|
|
1665
|
-
case "useContract":
|
|
1666
|
-
return `export function useContract() {
|
|
1667
|
-
const config = useSecondLayerConfig()
|
|
1668
|
-
const queryClient = useQueryClient()
|
|
1669
|
-
const [isRequestPending, setIsRequestPending] = useState(false)
|
|
1670
|
-
|
|
1671
|
-
// Helper function to convert JS values to Clarity values based on ABI
|
|
1672
|
-
const convertArgsWithAbi = (args: any, abiArgs: any[]): any[] => {
|
|
1673
|
-
if (!abiArgs || abiArgs.length === 0) return []
|
|
1674
|
-
|
|
1675
|
-
return abiArgs.map((abiArg, index) => {
|
|
1676
|
-
const argValue = Array.isArray(args)
|
|
1677
|
-
? args[index]
|
|
1678
|
-
: args[abiArg.name] || args[abiArg.name.replace(/-/g, '').replace(/_/g, '')]
|
|
1679
|
-
return convertJSValueToClarityValue(argValue, abiArg.type)
|
|
1680
|
-
})
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
// Helper function to convert buffer values with auto-detection
|
|
1684
|
-
const convertBufferValue = (value: any): any => {
|
|
1685
|
-
// Direct Uint8Array
|
|
1686
|
-
if (value instanceof Uint8Array) {
|
|
1687
|
-
return Cl.buffer(value)
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
// Object notation with explicit type
|
|
1691
|
-
if (typeof value === 'object' && value !== null && value.type && value.value) {
|
|
1692
|
-
switch (value.type) {
|
|
1693
|
-
case 'ascii':
|
|
1694
|
-
return Cl.bufferFromAscii(value.value)
|
|
1695
|
-
case 'utf8':
|
|
1696
|
-
return Cl.bufferFromUtf8(value.value)
|
|
1697
|
-
case 'hex':
|
|
1698
|
-
return Cl.bufferFromHex(value.value)
|
|
1699
|
-
default:
|
|
1700
|
-
throw new Error(\`Unsupported buffer type: \${value.type}\`)
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
// Auto-detect string type
|
|
1705
|
-
if (typeof value === 'string') {
|
|
1706
|
-
// 1. Check for hex (0x prefix or pure hex pattern)
|
|
1707
|
-
if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
|
|
1708
|
-
return Cl.bufferFromHex(value)
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
// 2. Check for non-ASCII characters (UTF-8)
|
|
1712
|
-
if (!/^[\\x00-\\x7F]*$/.test(value)) {
|
|
1713
|
-
return Cl.bufferFromUtf8(value)
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
// 3. Default to ASCII for simple ASCII strings
|
|
1717
|
-
return Cl.bufferFromAscii(value)
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
throw new Error(\`Invalid buffer value: \${value}\`)
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
// Helper function to convert a single JS value to ClarityValue
|
|
1724
|
-
const convertJSValueToClarityValue = (value: any, type: any): any => {
|
|
1725
|
-
if (typeof type === 'string') {
|
|
1726
|
-
switch (type) {
|
|
1727
|
-
case 'uint128':
|
|
1728
|
-
return Cl.uint(value)
|
|
1729
|
-
case 'int128':
|
|
1730
|
-
return Cl.int(value)
|
|
1731
|
-
case 'bool':
|
|
1732
|
-
return Cl.bool(value)
|
|
1733
|
-
case 'principal':
|
|
1734
|
-
if (!validateStacksAddress(value.split('.')[0])) {
|
|
1735
|
-
throw new Error('Invalid Stacks address format')
|
|
1736
|
-
}
|
|
1737
|
-
if (value.includes('.')) {
|
|
1738
|
-
const [address, contractName] = value.split('.')
|
|
1739
|
-
return Cl.contractPrincipal(address, contractName)
|
|
1740
|
-
} else {
|
|
1741
|
-
return Cl.standardPrincipal(value)
|
|
1742
|
-
}
|
|
1743
|
-
default:
|
|
1744
|
-
return value
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
if (type['string-ascii']) {
|
|
1749
|
-
return Cl.stringAscii(value)
|
|
1750
|
-
}
|
|
1568
|
+
if (type['string-ascii']) {
|
|
1569
|
+
return Cl.stringAscii(value)
|
|
1570
|
+
}
|
|
1751
1571
|
|
|
1752
1572
|
if (type['string-utf8']) {
|
|
1753
1573
|
return Cl.stringUtf8(value)
|
|
@@ -1774,7 +1594,7 @@ function generateGenericHook(hookName) {
|
|
|
1774
1594
|
}
|
|
1775
1595
|
|
|
1776
1596
|
if (type.response) {
|
|
1777
|
-
return 'ok' in value
|
|
1597
|
+
return 'ok' in value
|
|
1778
1598
|
? Cl.ok(convertJSValueToClarityValue(value.ok, type.response.ok))
|
|
1779
1599
|
: Cl.error(convertJSValueToClarityValue(value.err, type.response.error))
|
|
1780
1600
|
}
|
|
@@ -1787,13 +1607,13 @@ function generateGenericHook(hookName) {
|
|
|
1787
1607
|
if (!abi || !abi.functions) return null
|
|
1788
1608
|
return abi.functions.find((func: any) => func.name === functionName)
|
|
1789
1609
|
}
|
|
1790
|
-
|
|
1791
|
-
// Legacy function -
|
|
1610
|
+
|
|
1611
|
+
// Legacy function - pre-converted Clarity values
|
|
1792
1612
|
const legacyOpenContractCall = useCallback(async (params: {
|
|
1793
1613
|
contractAddress: string;
|
|
1794
1614
|
contractName: string;
|
|
1795
1615
|
functionName: string;
|
|
1796
|
-
functionArgs: any[];
|
|
1616
|
+
functionArgs: any[];
|
|
1797
1617
|
network?: string;
|
|
1798
1618
|
postConditions?: PostCondition[];
|
|
1799
1619
|
attachment?: string;
|
|
@@ -1801,57 +1621,23 @@ function generateGenericHook(hookName) {
|
|
|
1801
1621
|
onCancel?: () => void;
|
|
1802
1622
|
}) => {
|
|
1803
1623
|
setIsRequestPending(true)
|
|
1804
|
-
|
|
1624
|
+
|
|
1805
1625
|
try {
|
|
1806
1626
|
const { contractAddress, contractName, functionName, functionArgs, onFinish, onCancel, ...options } = params
|
|
1807
1627
|
const network = params.network || config.network || 'mainnet'
|
|
1808
1628
|
const contract = \`\${contractAddress}.\${contractName}\`
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
queryClient.invalidateQueries({
|
|
1822
|
-
queryKey: ['stacks-account']
|
|
1823
|
-
})
|
|
1824
|
-
|
|
1825
|
-
onFinish?.(result)
|
|
1826
|
-
return result
|
|
1827
|
-
} catch (connectError) {
|
|
1828
|
-
// Fallback to openContractCall for broader wallet compatibility
|
|
1829
|
-
console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
|
|
1830
|
-
|
|
1831
|
-
return new Promise((resolve, reject) => {
|
|
1832
|
-
stacksOpenContractCall({
|
|
1833
|
-
contractAddress,
|
|
1834
|
-
contractName,
|
|
1835
|
-
functionName,
|
|
1836
|
-
functionArgs,
|
|
1837
|
-
network,
|
|
1838
|
-
...options,
|
|
1839
|
-
onFinish: (data: any) => {
|
|
1840
|
-
// Invalidate relevant queries on success
|
|
1841
|
-
queryClient.invalidateQueries({
|
|
1842
|
-
queryKey: ['stacks-account']
|
|
1843
|
-
})
|
|
1844
|
-
|
|
1845
|
-
onFinish?.(data)
|
|
1846
|
-
resolve(data)
|
|
1847
|
-
},
|
|
1848
|
-
onCancel: () => {
|
|
1849
|
-
onCancel?.()
|
|
1850
|
-
reject(new Error('User cancelled transaction'))
|
|
1851
|
-
}
|
|
1852
|
-
})
|
|
1853
|
-
})
|
|
1854
|
-
}
|
|
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
|
|
1855
1641
|
} catch (error) {
|
|
1856
1642
|
console.error('Contract call failed:', error)
|
|
1857
1643
|
throw error instanceof Error ? error : new Error('Contract call failed')
|
|
@@ -1862,7 +1648,7 @@ function generateGenericHook(hookName) {
|
|
|
1862
1648
|
|
|
1863
1649
|
// Enhanced function - requires ABI, auto-converts JS values
|
|
1864
1650
|
const openContractCall = useCallback(async <
|
|
1865
|
-
T extends
|
|
1651
|
+
T extends AbiContract,
|
|
1866
1652
|
FN extends ExtractFunctionNames<T>
|
|
1867
1653
|
>(params: {
|
|
1868
1654
|
contractAddress: string;
|
|
@@ -1877,79 +1663,285 @@ function generateGenericHook(hookName) {
|
|
|
1877
1663
|
onCancel?: () => void;
|
|
1878
1664
|
}) => {
|
|
1879
1665
|
setIsRequestPending(true)
|
|
1880
|
-
|
|
1666
|
+
|
|
1881
1667
|
try {
|
|
1882
1668
|
const { contractAddress, contractName, functionName, functionArgs, abi, onFinish, onCancel, ...options } = params
|
|
1883
1669
|
const network = params.network || config.network || 'mainnet'
|
|
1884
1670
|
const contract = \`\${contractAddress}.\${contractName}\`
|
|
1885
|
-
|
|
1886
|
-
// Find the function in the ABI and convert args
|
|
1671
|
+
|
|
1887
1672
|
const abiFunction = findFunctionInAbi(abi, functionName)
|
|
1888
1673
|
if (!abiFunction) {
|
|
1889
1674
|
throw new Error(\`Function '\${functionName}' not found in ABI\`)
|
|
1890
1675
|
}
|
|
1891
|
-
|
|
1676
|
+
|
|
1892
1677
|
const processedArgs = convertArgsWithAbi(functionArgs, abiFunction.args || [])
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
queryClient.invalidateQueries({
|
|
1906
|
-
queryKey: ['stacks-account']
|
|
1907
|
-
})
|
|
1908
|
-
|
|
1909
|
-
onFinish?.(result)
|
|
1910
|
-
return result
|
|
1911
|
-
} catch (connectError) {
|
|
1912
|
-
// Fallback to openContractCall for broader wallet compatibility
|
|
1913
|
-
console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
|
|
1914
|
-
|
|
1915
|
-
return new Promise((resolve, reject) => {
|
|
1916
|
-
stacksOpenContractCall({
|
|
1917
|
-
contractAddress,
|
|
1918
|
-
contractName,
|
|
1919
|
-
functionName,
|
|
1920
|
-
functionArgs: processedArgs,
|
|
1921
|
-
network,
|
|
1922
|
-
...options,
|
|
1923
|
-
onFinish: (data: any) => {
|
|
1924
|
-
// Invalidate relevant queries on success
|
|
1925
|
-
queryClient.invalidateQueries({
|
|
1926
|
-
queryKey: ['stacks-account']
|
|
1927
|
-
})
|
|
1928
|
-
|
|
1929
|
-
onFinish?.(data)
|
|
1930
|
-
resolve(data)
|
|
1931
|
-
},
|
|
1932
|
-
onCancel: () => {
|
|
1933
|
-
onCancel?.()
|
|
1934
|
-
reject(new Error('User cancelled transaction'))
|
|
1935
|
-
}
|
|
1936
|
-
})
|
|
1937
|
-
})
|
|
1938
|
-
}
|
|
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
|
|
1939
1690
|
} catch (error) {
|
|
1940
1691
|
console.error('Contract call failed:', error)
|
|
1941
1692
|
throw error instanceof Error ? error : new Error('Contract call failed')
|
|
1942
1693
|
} finally {
|
|
1943
1694
|
setIsRequestPending(false)
|
|
1944
1695
|
}
|
|
1945
|
-
}, [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
|
+
})
|
|
1946
1857
|
|
|
1947
1858
|
return {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
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
|
|
1951
1914
|
}
|
|
1952
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;
|
|
1953
1945
|
case "useReadContract":
|
|
1954
1946
|
return `export function useReadContract<TArgs = any, TResult = any>(params: {
|
|
1955
1947
|
contractAddress: string;
|
|
@@ -1964,7 +1956,7 @@ function generateGenericHook(hookName) {
|
|
|
1964
1956
|
return useQuery<TResult>({
|
|
1965
1957
|
queryKey: ['read-contract', params.contractAddress, params.contractName, params.functionName, params.args, params.network || config.network],
|
|
1966
1958
|
queryFn: async () => {
|
|
1967
|
-
const { fetchCallReadOnlyFunction } = await import('@stacks/
|
|
1959
|
+
const { fetchCallReadOnlyFunction } = await import('@secondlayer/stacks/clarity')
|
|
1968
1960
|
|
|
1969
1961
|
// For now, we'll need to handle the args conversion here
|
|
1970
1962
|
// In the future, we could integrate with the contract interface for automatic conversion
|
|
@@ -2065,39 +2057,25 @@ function generateGenericHook(hookName) {
|
|
|
2065
2057
|
return `export function useOpenSTXTransfer() {
|
|
2066
2058
|
const config = useSecondLayerConfig()
|
|
2067
2059
|
const queryClient = useQueryClient()
|
|
2068
|
-
|
|
2060
|
+
|
|
2069
2061
|
const mutation = useMutation({
|
|
2070
2062
|
mutationFn: async (params: {
|
|
2071
2063
|
recipient: string;
|
|
2072
2064
|
amount: string | number;
|
|
2073
2065
|
memo?: string;
|
|
2074
2066
|
network?: string;
|
|
2075
|
-
onFinish?: (data: any) => void;
|
|
2076
|
-
onCancel?: () => void;
|
|
2077
2067
|
}) => {
|
|
2078
|
-
const { recipient, amount, memo
|
|
2068
|
+
const { recipient, amount, memo } = params
|
|
2079
2069
|
const network = params.network || config.network || 'mainnet'
|
|
2080
|
-
|
|
2081
|
-
return
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
network,
|
|
2087
|
-
...options,
|
|
2088
|
-
onFinish: (data: any) => {
|
|
2089
|
-
onFinish?.(data)
|
|
2090
|
-
resolve(data)
|
|
2091
|
-
},
|
|
2092
|
-
onCancel: () => {
|
|
2093
|
-
onCancel?.()
|
|
2094
|
-
reject(new Error('User cancelled transaction'))
|
|
2095
|
-
}
|
|
2096
|
-
})
|
|
2070
|
+
|
|
2071
|
+
return await request('stx_transferStx', {
|
|
2072
|
+
recipient,
|
|
2073
|
+
amount: amount.toString(),
|
|
2074
|
+
memo,
|
|
2075
|
+
network,
|
|
2097
2076
|
})
|
|
2098
2077
|
},
|
|
2099
2078
|
onSuccess: () => {
|
|
2100
|
-
// Invalidate relevant queries on success
|
|
2101
2079
|
queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
|
|
2102
2080
|
},
|
|
2103
2081
|
onError: (error) => {
|
|
@@ -2110,15 +2088,12 @@ function generateGenericHook(hookName) {
|
|
|
2110
2088
|
amount: string | number;
|
|
2111
2089
|
memo?: string;
|
|
2112
2090
|
network?: string;
|
|
2113
|
-
onFinish?: (data: any) => void;
|
|
2114
|
-
onCancel?: () => void;
|
|
2115
2091
|
}) => {
|
|
2116
2092
|
return mutation.mutateAsync(params)
|
|
2117
2093
|
}, [mutation])
|
|
2118
2094
|
|
|
2119
2095
|
return {
|
|
2120
2096
|
openSTXTransfer,
|
|
2121
|
-
// Expose mutation state
|
|
2122
2097
|
isPending: mutation.isPending,
|
|
2123
2098
|
isError: mutation.isError,
|
|
2124
2099
|
isSuccess: mutation.isSuccess,
|
|
@@ -2130,31 +2105,18 @@ function generateGenericHook(hookName) {
|
|
|
2130
2105
|
case "useSignMessage":
|
|
2131
2106
|
return `export function useSignMessage() {
|
|
2132
2107
|
const config = useSecondLayerConfig()
|
|
2133
|
-
|
|
2108
|
+
|
|
2134
2109
|
const mutation = useMutation({
|
|
2135
2110
|
mutationFn: async (params: {
|
|
2136
2111
|
message: string;
|
|
2137
2112
|
network?: string;
|
|
2138
|
-
onFinish?: (data: any) => void;
|
|
2139
|
-
onCancel?: () => void;
|
|
2140
2113
|
}) => {
|
|
2141
|
-
const { message
|
|
2114
|
+
const { message } = params
|
|
2142
2115
|
const network = params.network || config.network || 'mainnet'
|
|
2143
|
-
|
|
2144
|
-
return
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
network,
|
|
2148
|
-
...options,
|
|
2149
|
-
onFinish: (data: any) => {
|
|
2150
|
-
onFinish?.(data)
|
|
2151
|
-
resolve(data)
|
|
2152
|
-
},
|
|
2153
|
-
onCancel: () => {
|
|
2154
|
-
onCancel?.()
|
|
2155
|
-
reject(new Error('User cancelled message signing'))
|
|
2156
|
-
}
|
|
2157
|
-
})
|
|
2116
|
+
|
|
2117
|
+
return await request('stx_signMessage', {
|
|
2118
|
+
message,
|
|
2119
|
+
network,
|
|
2158
2120
|
})
|
|
2159
2121
|
},
|
|
2160
2122
|
onError: (error) => {
|
|
@@ -2165,15 +2127,12 @@ function generateGenericHook(hookName) {
|
|
|
2165
2127
|
const signMessage = useCallback(async (params: {
|
|
2166
2128
|
message: string;
|
|
2167
2129
|
network?: string;
|
|
2168
|
-
onFinish?: (data: any) => void;
|
|
2169
|
-
onCancel?: () => void;
|
|
2170
2130
|
}) => {
|
|
2171
2131
|
return mutation.mutateAsync(params)
|
|
2172
2132
|
}, [mutation])
|
|
2173
2133
|
|
|
2174
2134
|
return {
|
|
2175
2135
|
signMessage,
|
|
2176
|
-
// Expose mutation state
|
|
2177
2136
|
isPending: mutation.isPending,
|
|
2178
2137
|
isError: mutation.isError,
|
|
2179
2138
|
isSuccess: mutation.isSuccess,
|
|
@@ -2186,38 +2145,23 @@ function generateGenericHook(hookName) {
|
|
|
2186
2145
|
return `export function useDeployContract() {
|
|
2187
2146
|
const config = useSecondLayerConfig()
|
|
2188
2147
|
const queryClient = useQueryClient()
|
|
2189
|
-
|
|
2148
|
+
|
|
2190
2149
|
const mutation = useMutation({
|
|
2191
2150
|
mutationFn: async (params: {
|
|
2192
2151
|
contractName: string;
|
|
2193
2152
|
codeBody: string;
|
|
2194
2153
|
network?: string;
|
|
2195
|
-
postConditions?: PostCondition[];
|
|
2196
|
-
onFinish?: (data: any) => void;
|
|
2197
|
-
onCancel?: () => void;
|
|
2198
2154
|
}) => {
|
|
2199
|
-
const { contractName, codeBody
|
|
2155
|
+
const { contractName, codeBody } = params
|
|
2200
2156
|
const network = params.network || config.network || 'mainnet'
|
|
2201
|
-
|
|
2202
|
-
return
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
network,
|
|
2207
|
-
...options,
|
|
2208
|
-
onFinish: (data: any) => {
|
|
2209
|
-
onFinish?.(data)
|
|
2210
|
-
resolve(data)
|
|
2211
|
-
},
|
|
2212
|
-
onCancel: () => {
|
|
2213
|
-
onCancel?.()
|
|
2214
|
-
reject(new Error('User cancelled contract deployment'))
|
|
2215
|
-
}
|
|
2216
|
-
})
|
|
2157
|
+
|
|
2158
|
+
return await request('stx_deployContract', {
|
|
2159
|
+
name: contractName,
|
|
2160
|
+
clarityCode: codeBody,
|
|
2161
|
+
network,
|
|
2217
2162
|
})
|
|
2218
2163
|
},
|
|
2219
2164
|
onSuccess: () => {
|
|
2220
|
-
// Invalidate relevant queries on success
|
|
2221
2165
|
queryClient.invalidateQueries({ queryKey: ['stacks-account'] })
|
|
2222
2166
|
},
|
|
2223
2167
|
onError: (error) => {
|
|
@@ -2229,16 +2173,12 @@ function generateGenericHook(hookName) {
|
|
|
2229
2173
|
contractName: string;
|
|
2230
2174
|
codeBody: string;
|
|
2231
2175
|
network?: string;
|
|
2232
|
-
postConditions?: PostCondition[];
|
|
2233
|
-
onFinish?: (data: any) => void;
|
|
2234
|
-
onCancel?: () => void;
|
|
2235
2176
|
}) => {
|
|
2236
2177
|
return mutation.mutateAsync(params)
|
|
2237
2178
|
}, [mutation])
|
|
2238
2179
|
|
|
2239
2180
|
return {
|
|
2240
2181
|
deployContract,
|
|
2241
|
-
// Expose mutation state
|
|
2242
2182
|
isPending: mutation.isPending,
|
|
2243
2183
|
isError: mutation.isError,
|
|
2244
2184
|
isSuccess: mutation.isSuccess,
|
|
@@ -2256,35 +2196,30 @@ function generateGenericHook(hookName) {
|
|
|
2256
2196
|
init_format();
|
|
2257
2197
|
|
|
2258
2198
|
// src/plugins/react/generators/utils.ts
|
|
2259
|
-
import { toCamelCase as
|
|
2199
|
+
import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
|
|
2260
2200
|
function capitalize(str) {
|
|
2261
2201
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2262
2202
|
}
|
|
2263
2203
|
function generateHookArgsSignature(args) {
|
|
2264
2204
|
if (args.length === 0)
|
|
2265
2205
|
return "";
|
|
2266
|
-
const argsList = args.map((arg) => `${
|
|
2206
|
+
const argsList = args.map((arg) => `${toCamelCase7(arg.name)}: ${clarityTypeToTS(arg.type)}`).join(", ");
|
|
2267
2207
|
return `${argsList}`;
|
|
2268
2208
|
}
|
|
2269
2209
|
function generateArgsType(args) {
|
|
2270
2210
|
if (args.length === 0)
|
|
2271
2211
|
return "void";
|
|
2272
|
-
const argsList = args.map((arg) => `${
|
|
2212
|
+
const argsList = args.map((arg) => `${toCamelCase7(arg.name)}: ${clarityTypeToTS(arg.type)}`).join("; ");
|
|
2273
2213
|
return `{ ${argsList} }`;
|
|
2274
2214
|
}
|
|
2275
|
-
function
|
|
2215
|
+
function generateArgNames(args) {
|
|
2276
2216
|
if (args.length === 0)
|
|
2277
2217
|
return "";
|
|
2278
|
-
return args.map((arg) =>
|
|
2279
|
-
}
|
|
2280
|
-
function generateFunctionCallArgs(args) {
|
|
2281
|
-
if (args.length === 0)
|
|
2282
|
-
return "";
|
|
2283
|
-
return args.map((arg) => toCamelCase5(arg.name)).join(", ");
|
|
2218
|
+
return args.map((arg) => toCamelCase7(arg.name)).join(", ");
|
|
2284
2219
|
}
|
|
2285
2220
|
function generateEnabledCondition(args) {
|
|
2286
2221
|
return args.map((arg) => {
|
|
2287
|
-
const camelName =
|
|
2222
|
+
const camelName = toCamelCase7(arg.name);
|
|
2288
2223
|
const type = clarityTypeToTS(arg.type);
|
|
2289
2224
|
if (type === "string")
|
|
2290
2225
|
return `!!${camelName}`;
|
|
@@ -2296,7 +2231,7 @@ function generateEnabledCondition(args) {
|
|
|
2296
2231
|
function generateObjectArgs(args) {
|
|
2297
2232
|
if (args.length === 0)
|
|
2298
2233
|
return "";
|
|
2299
|
-
return args.map((arg) => `${arg.name}: ${
|
|
2234
|
+
return args.map((arg) => `${arg.name}: ${toCamelCase7(arg.name)}`).join(", ");
|
|
2300
2235
|
}
|
|
2301
2236
|
|
|
2302
2237
|
// src/plugins/react/generators/contract.ts
|
|
@@ -2304,8 +2239,8 @@ async function generateContractHooks(contracts, excludeList = []) {
|
|
|
2304
2239
|
const imports = `import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
2305
2240
|
import { useCallback } from 'react'
|
|
2306
2241
|
import { useSecondLayerConfig } from './provider'
|
|
2307
|
-
import { request
|
|
2308
|
-
import type { PostCondition } from '@stacks
|
|
2242
|
+
import { request } from '@secondlayer/stacks/connect'
|
|
2243
|
+
import type { PostCondition } from '@secondlayer/stacks'
|
|
2309
2244
|
import { ${contracts.map((c) => c.name).join(", ")} } from './contracts'`;
|
|
2310
2245
|
const header = `/**
|
|
2311
2246
|
* Generated contract-specific React hooks
|
|
@@ -2326,24 +2261,24 @@ function generateContractHookMethods(contract, excludeList) {
|
|
|
2326
2261
|
const functions = abi.functions || [];
|
|
2327
2262
|
const maps = abi.maps || [];
|
|
2328
2263
|
const variables = abi.variables || [];
|
|
2329
|
-
const readOnlyFunctions = functions.filter((f) => f.access === "
|
|
2264
|
+
const readOnlyFunctions = functions.filter((f) => f.access === "read-only");
|
|
2330
2265
|
const publicFunctions = functions.filter((f) => f.access === "public");
|
|
2331
2266
|
const readHooks = readOnlyFunctions.map((func) => {
|
|
2332
|
-
const hookName = `use${capitalize(name)}${capitalize(
|
|
2267
|
+
const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(func.name))}`;
|
|
2333
2268
|
if (excludeList.includes(hookName)) {
|
|
2334
2269
|
return null;
|
|
2335
2270
|
}
|
|
2336
2271
|
return generateReadHook(func, name);
|
|
2337
2272
|
}).filter(Boolean);
|
|
2338
2273
|
const writeHooks = publicFunctions.map((func) => {
|
|
2339
|
-
const hookName = `use${capitalize(name)}${capitalize(
|
|
2274
|
+
const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(func.name))}`;
|
|
2340
2275
|
if (excludeList.includes(hookName)) {
|
|
2341
2276
|
return null;
|
|
2342
2277
|
}
|
|
2343
2278
|
return generateWriteHook(func, name);
|
|
2344
2279
|
}).filter(Boolean);
|
|
2345
2280
|
const mapHooks = maps.map((map) => {
|
|
2346
|
-
const hookName = `use${capitalize(name)}${capitalize(
|
|
2281
|
+
const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(map.name))}`;
|
|
2347
2282
|
if (excludeList.includes(hookName)) {
|
|
2348
2283
|
return null;
|
|
2349
2284
|
}
|
|
@@ -2351,7 +2286,7 @@ function generateContractHookMethods(contract, excludeList) {
|
|
|
2351
2286
|
}).filter(Boolean);
|
|
2352
2287
|
const dataVars = variables.filter((v) => v.access === "variable");
|
|
2353
2288
|
const varHooks = dataVars.map((variable) => {
|
|
2354
|
-
const hookName = `use${capitalize(name)}${capitalize(
|
|
2289
|
+
const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(variable.name))}`;
|
|
2355
2290
|
if (excludeList.includes(hookName)) {
|
|
2356
2291
|
return null;
|
|
2357
2292
|
}
|
|
@@ -2359,7 +2294,7 @@ function generateContractHookMethods(contract, excludeList) {
|
|
|
2359
2294
|
}).filter(Boolean);
|
|
2360
2295
|
const constants = variables.filter((v) => v.access === "constant");
|
|
2361
2296
|
const constantHooks = constants.map((constant) => {
|
|
2362
|
-
const hookName = `use${capitalize(name)}${capitalize(
|
|
2297
|
+
const hookName = `use${capitalize(name)}${capitalize(toCamelCase7(constant.name))}`;
|
|
2363
2298
|
if (excludeList.includes(hookName)) {
|
|
2364
2299
|
return null;
|
|
2365
2300
|
}
|
|
@@ -2374,7 +2309,7 @@ function generateContractHookMethods(contract, excludeList) {
|
|
|
2374
2309
|
`);
|
|
2375
2310
|
}
|
|
2376
2311
|
function generateReadHook(func, contractName) {
|
|
2377
|
-
const hookName = `use${capitalize(contractName)}${capitalize(
|
|
2312
|
+
const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase7(func.name))}`;
|
|
2378
2313
|
const argsSignature = generateHookArgsSignature(func.args);
|
|
2379
2314
|
const enabledParam = func.args.length > 0 ? ", options?: { enabled?: boolean }" : "options?: { enabled?: boolean }";
|
|
2380
2315
|
const returnType = clarityTypeToTS(func.outputs);
|
|
@@ -2382,8 +2317,8 @@ function generateReadHook(func, contractName) {
|
|
|
2382
2317
|
const config = useSecondLayerConfig()
|
|
2383
2318
|
|
|
2384
2319
|
return useQuery<${returnType}>({
|
|
2385
|
-
queryKey: ['${func.name}', ${contractName}.address, ${
|
|
2386
|
-
queryFn: () => ${contractName}.read.${
|
|
2320
|
+
queryKey: ['${func.name}', ${contractName}.address, ${generateArgNames(func.args)}],
|
|
2321
|
+
queryFn: () => ${contractName}.read.${toCamelCase7(func.name)}(${generateArgNames(func.args) ? `{ ${generateObjectArgs(func.args)} }, ` : ""}{
|
|
2387
2322
|
network: config.network,
|
|
2388
2323
|
senderAddress: config.senderAddress || 'SP000000000000000000002Q6VF78'
|
|
2389
2324
|
}),
|
|
@@ -2393,7 +2328,7 @@ function generateReadHook(func, contractName) {
|
|
|
2393
2328
|
}`;
|
|
2394
2329
|
}
|
|
2395
2330
|
function generateWriteHook(func, contractName) {
|
|
2396
|
-
const hookName = `use${capitalize(contractName)}${capitalize(
|
|
2331
|
+
const hookName = `use${capitalize(contractName)}${capitalize(toCamelCase7(func.name))}`;
|
|
2397
2332
|
const argsType = generateArgsType(func.args);
|
|
2398
2333
|
return `export function ${hookName}() {
|
|
2399
2334
|
const config = useSecondLayerConfig()
|
|
@@ -2410,46 +2345,21 @@ function generateWriteHook(func, contractName) {
|
|
|
2410
2345
|
};
|
|
2411
2346
|
}) => {
|
|
2412
2347
|
const { args, options = {} } = params
|
|
2413
|
-
const contractCallData = ${contractName}.${
|
|
2348
|
+
const contractCallData = ${contractName}.${toCamelCase7(func.name)}(args)
|
|
2414
2349
|
const { contractAddress, contractName: name, functionName, functionArgs } = contractCallData
|
|
2415
2350
|
const network = config.network || 'mainnet'
|
|
2416
2351
|
const contract = \`\${contractAddress}.\${name}\`
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
options.onFinish?.(result)
|
|
2429
|
-
return result
|
|
2430
|
-
} catch (connectError) {
|
|
2431
|
-
// Fallback to openContractCall for broader wallet compatibility
|
|
2432
|
-
console.warn('stx_callContract not supported, falling back to openContractCall:', connectError)
|
|
2433
|
-
|
|
2434
|
-
return new Promise((resolve, reject) => {
|
|
2435
|
-
stacksOpenContractCall({
|
|
2436
|
-
contractAddress,
|
|
2437
|
-
contractName: name,
|
|
2438
|
-
functionName,
|
|
2439
|
-
functionArgs,
|
|
2440
|
-
network,
|
|
2441
|
-
...options,
|
|
2442
|
-
onFinish: (data: any) => {
|
|
2443
|
-
options.onFinish?.(data)
|
|
2444
|
-
resolve(data)
|
|
2445
|
-
},
|
|
2446
|
-
onCancel: () => {
|
|
2447
|
-
options.onCancel?.()
|
|
2448
|
-
reject(new Error('User cancelled transaction'))
|
|
2449
|
-
}
|
|
2450
|
-
})
|
|
2451
|
-
})
|
|
2452
|
-
}
|
|
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
|
|
2453
2363
|
},
|
|
2454
2364
|
onSuccess: () => {
|
|
2455
2365
|
// Invalidate relevant queries on success
|
|
@@ -2460,7 +2370,7 @@ function generateWriteHook(func, contractName) {
|
|
|
2460
2370
|
}
|
|
2461
2371
|
})
|
|
2462
2372
|
|
|
2463
|
-
const ${
|
|
2373
|
+
const ${toCamelCase7(func.name)} = useCallback(async (
|
|
2464
2374
|
args: ${argsType},
|
|
2465
2375
|
options?: {
|
|
2466
2376
|
postConditions?: PostCondition[];
|
|
@@ -2473,7 +2383,7 @@ function generateWriteHook(func, contractName) {
|
|
|
2473
2383
|
}, [mutation])
|
|
2474
2384
|
|
|
2475
2385
|
return {
|
|
2476
|
-
${
|
|
2386
|
+
${toCamelCase7(func.name)},
|
|
2477
2387
|
// Expose mutation state
|
|
2478
2388
|
isPending: mutation.isPending,
|
|
2479
2389
|
isError: mutation.isError,
|
|
@@ -2485,7 +2395,7 @@ function generateWriteHook(func, contractName) {
|
|
|
2485
2395
|
}`;
|
|
2486
2396
|
}
|
|
2487
2397
|
function generateMapHook(map, contractVarName, _address, _contractName) {
|
|
2488
|
-
const hookName = `use${capitalize(contractVarName)}${capitalize(
|
|
2398
|
+
const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(map.name))}`;
|
|
2489
2399
|
const keyType = clarityTypeToTS(map.key);
|
|
2490
2400
|
const valueType = clarityTypeToTS(map.value);
|
|
2491
2401
|
return `export function ${hookName}(key: ${keyType}, options?: { enabled?: boolean }) {
|
|
@@ -2494,14 +2404,14 @@ function generateMapHook(map, contractVarName, _address, _contractName) {
|
|
|
2494
2404
|
return useQuery<${valueType} | null>({
|
|
2495
2405
|
queryKey: ['${contractVarName}', '${map.name}', 'map', key, config.network],
|
|
2496
2406
|
queryFn: async () => {
|
|
2497
|
-
return ${contractVarName}.maps.${
|
|
2407
|
+
return ${contractVarName}.maps.${toCamelCase7(map.name)}.get(key, { network: config.network })
|
|
2498
2408
|
},
|
|
2499
2409
|
enabled: options?.enabled ?? true
|
|
2500
2410
|
})
|
|
2501
2411
|
}`;
|
|
2502
2412
|
}
|
|
2503
2413
|
function generateVarHook(variable, contractVarName, _address, _contractName) {
|
|
2504
|
-
const hookName = `use${capitalize(contractVarName)}${capitalize(
|
|
2414
|
+
const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(variable.name))}`;
|
|
2505
2415
|
const valueType = clarityTypeToTS(variable.type);
|
|
2506
2416
|
return `export function ${hookName}(options?: { enabled?: boolean }) {
|
|
2507
2417
|
const config = useSecondLayerConfig()
|
|
@@ -2509,14 +2419,14 @@ function generateVarHook(variable, contractVarName, _address, _contractName) {
|
|
|
2509
2419
|
return useQuery<${valueType}>({
|
|
2510
2420
|
queryKey: ['${contractVarName}', '${variable.name}', 'var', config.network],
|
|
2511
2421
|
queryFn: async () => {
|
|
2512
|
-
return ${contractVarName}.vars.${
|
|
2422
|
+
return ${contractVarName}.vars.${toCamelCase7(variable.name)}.get({ network: config.network })
|
|
2513
2423
|
},
|
|
2514
2424
|
enabled: options?.enabled ?? true
|
|
2515
2425
|
})
|
|
2516
2426
|
}`;
|
|
2517
2427
|
}
|
|
2518
2428
|
function generateConstantHook(constant, contractVarName, _address, _contractName) {
|
|
2519
|
-
const hookName = `use${capitalize(contractVarName)}${capitalize(
|
|
2429
|
+
const hookName = `use${capitalize(contractVarName)}${capitalize(toCamelCase7(constant.name))}`;
|
|
2520
2430
|
const valueType = clarityTypeToTS(constant.type);
|
|
2521
2431
|
return `export function ${hookName}(options?: { enabled?: boolean }) {
|
|
2522
2432
|
const config = useSecondLayerConfig()
|
|
@@ -2524,7 +2434,7 @@ function generateConstantHook(constant, contractVarName, _address, _contractName
|
|
|
2524
2434
|
return useQuery<${valueType}>({
|
|
2525
2435
|
queryKey: ['${contractVarName}', '${constant.name}', 'constant', config.network],
|
|
2526
2436
|
queryFn: async () => {
|
|
2527
|
-
return ${contractVarName}.constants.${
|
|
2437
|
+
return ${contractVarName}.constants.${toCamelCase7(constant.name)}.get({ network: config.network })
|
|
2528
2438
|
},
|
|
2529
2439
|
enabled: options?.enabled ?? true,
|
|
2530
2440
|
staleTime: Infinity // Constants never change
|
|
@@ -2572,155 +2482,17 @@ var react = (options = {}) => {
|
|
|
2572
2482
|
};
|
|
2573
2483
|
// src/plugins/testing/generators.ts
|
|
2574
2484
|
import {
|
|
2575
|
-
toCamelCase as
|
|
2576
|
-
|
|
2485
|
+
toCamelCase as toCamelCase8,
|
|
2486
|
+
isAbiTuple as isAbiTuple3
|
|
2487
|
+
} from "@secondlayer/stacks/clarity";
|
|
2577
2488
|
function toPascalCase(str) {
|
|
2578
|
-
const camel =
|
|
2489
|
+
const camel = toCamelCase8(str);
|
|
2579
2490
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
2580
2491
|
}
|
|
2581
|
-
function generateArgsSignature2(args) {
|
|
2582
|
-
if (args.length === 0)
|
|
2583
|
-
return "";
|
|
2584
|
-
const argsTypes = args.map((arg) => {
|
|
2585
|
-
const camelName = toCamelCase6(arg.name);
|
|
2586
|
-
return `${camelName}: ${getTypeForArg(arg)}`;
|
|
2587
|
-
}).join("; ");
|
|
2588
|
-
return `args: { ${argsTypes} }, `;
|
|
2589
|
-
}
|
|
2590
|
-
function generateClarityConversion3(argName, argType) {
|
|
2591
|
-
const type = argType.type;
|
|
2592
|
-
if (typeof type === "string") {
|
|
2593
|
-
switch (type) {
|
|
2594
|
-
case "uint128":
|
|
2595
|
-
return `Cl.uint(${argName})`;
|
|
2596
|
-
case "int128":
|
|
2597
|
-
return `Cl.int(${argName})`;
|
|
2598
|
-
case "bool":
|
|
2599
|
-
return `Cl.bool(${argName})`;
|
|
2600
|
-
case "principal":
|
|
2601
|
-
case "trait_reference":
|
|
2602
|
-
return `(() => {
|
|
2603
|
-
const principal = ${argName};
|
|
2604
|
-
if (principal.includes(".")) {
|
|
2605
|
-
const [address, contractName] = principal.split(".") as [string, string];
|
|
2606
|
-
return Cl.contractPrincipal(address, contractName);
|
|
2607
|
-
} else {
|
|
2608
|
-
return Cl.standardPrincipal(principal);
|
|
2609
|
-
}
|
|
2610
|
-
})()`;
|
|
2611
|
-
default:
|
|
2612
|
-
return `${argName}`;
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
if (type["string-ascii"]) {
|
|
2616
|
-
return `Cl.stringAscii(${argName})`;
|
|
2617
|
-
}
|
|
2618
|
-
if (type["string-utf8"]) {
|
|
2619
|
-
return `Cl.stringUtf8(${argName})`;
|
|
2620
|
-
}
|
|
2621
|
-
if (type.buff) {
|
|
2622
|
-
return `(() => {
|
|
2623
|
-
const value = ${argName};
|
|
2624
|
-
if (value instanceof Uint8Array) {
|
|
2625
|
-
return Cl.buffer(value);
|
|
2626
|
-
}
|
|
2627
|
-
if (typeof value === 'object' && value !== null && 'type' in value && 'value' in value) {
|
|
2628
|
-
switch (value.type) {
|
|
2629
|
-
case 'ascii':
|
|
2630
|
-
return Cl.bufferFromAscii(value.value);
|
|
2631
|
-
case 'utf8':
|
|
2632
|
-
return Cl.bufferFromUtf8(value.value);
|
|
2633
|
-
case 'hex':
|
|
2634
|
-
return Cl.bufferFromHex(value.value);
|
|
2635
|
-
default:
|
|
2636
|
-
throw new Error(\`Unsupported buffer type: \${value.type}\`);
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
if (typeof value === 'string') {
|
|
2640
|
-
if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
|
|
2641
|
-
return Cl.bufferFromHex(value);
|
|
2642
|
-
}
|
|
2643
|
-
if (!/^[\\x00-\\x7F]*$/.test(value)) {
|
|
2644
|
-
return Cl.bufferFromUtf8(value);
|
|
2645
|
-
}
|
|
2646
|
-
return Cl.bufferFromAscii(value);
|
|
2647
|
-
}
|
|
2648
|
-
throw new Error(\`Invalid buffer value: \${value}\`);
|
|
2649
|
-
})()`;
|
|
2650
|
-
}
|
|
2651
|
-
if (type.optional) {
|
|
2652
|
-
const innerConversion = generateClarityConversion3("inner", {
|
|
2653
|
-
type: type.optional
|
|
2654
|
-
});
|
|
2655
|
-
return `${argName} !== null ? Cl.some((() => { const inner = ${argName}; return ${innerConversion}; })()) : Cl.none()`;
|
|
2656
|
-
}
|
|
2657
|
-
if (type.list) {
|
|
2658
|
-
const innerConversion = generateClarityConversion3("item", {
|
|
2659
|
-
type: type.list.type
|
|
2660
|
-
});
|
|
2661
|
-
const maxLength = type.list.length || 100;
|
|
2662
|
-
return `(() => {
|
|
2663
|
-
const listValue = ${argName};
|
|
2664
|
-
if (listValue.length > ${maxLength}) {
|
|
2665
|
-
throw new Error(\`List length \${listValue.length} exceeds max ${maxLength}\`);
|
|
2666
|
-
}
|
|
2667
|
-
return Cl.list(listValue.map(item => ${innerConversion}));
|
|
2668
|
-
})()`;
|
|
2669
|
-
}
|
|
2670
|
-
if (type.tuple) {
|
|
2671
|
-
const requiredFields = type.tuple.map((f) => f.name);
|
|
2672
|
-
const fieldNames = JSON.stringify(requiredFields);
|
|
2673
|
-
const fields = type.tuple.map((field) => {
|
|
2674
|
-
const camelFieldName = toCamelCase6(field.name);
|
|
2675
|
-
const fieldConversion = generateClarityConversion3(`tupleValue.${camelFieldName}`, { type: field.type });
|
|
2676
|
-
return `"${field.name}": ${fieldConversion}`;
|
|
2677
|
-
}).join(", ");
|
|
2678
|
-
return `(() => {
|
|
2679
|
-
const tupleValue = ${argName};
|
|
2680
|
-
const requiredFields = ${fieldNames};
|
|
2681
|
-
for (const fieldName of requiredFields) {
|
|
2682
|
-
const camelName = fieldName.replace(/-([a-z])/g, (_: string, l: string) => l.toUpperCase());
|
|
2683
|
-
if (!(fieldName in tupleValue) && !(camelName in tupleValue)) {
|
|
2684
|
-
throw new Error(\`Missing tuple field: \${fieldName}\`);
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
return Cl.tuple({ ${fields} });
|
|
2688
|
-
})()`;
|
|
2689
|
-
}
|
|
2690
|
-
if (type.response) {
|
|
2691
|
-
const okConversion = generateClarityConversion3(`responseValue.ok`, {
|
|
2692
|
-
type: type.response.ok
|
|
2693
|
-
});
|
|
2694
|
-
const errConversion = generateClarityConversion3(`responseValue.err`, {
|
|
2695
|
-
type: type.response.error
|
|
2696
|
-
});
|
|
2697
|
-
return `(() => {
|
|
2698
|
-
const responseValue = ${argName};
|
|
2699
|
-
const hasOk = 'ok' in responseValue;
|
|
2700
|
-
const hasErr = 'err' in responseValue;
|
|
2701
|
-
if (hasOk && !hasErr) {
|
|
2702
|
-
return Cl.ok(${okConversion});
|
|
2703
|
-
}
|
|
2704
|
-
if (hasErr && !hasOk) {
|
|
2705
|
-
return Cl.error(${errConversion});
|
|
2706
|
-
}
|
|
2707
|
-
throw new Error("Response must have exactly 'ok' or 'err' property");
|
|
2708
|
-
})()`;
|
|
2709
|
-
}
|
|
2710
|
-
return `${argName}`;
|
|
2711
|
-
}
|
|
2712
|
-
function generateClarityArgs2(args) {
|
|
2713
|
-
if (args.length === 0)
|
|
2714
|
-
return "";
|
|
2715
|
-
return args.map((arg) => {
|
|
2716
|
-
const argName = `args.${toCamelCase6(arg.name)}`;
|
|
2717
|
-
return generateClarityConversion3(argName, arg);
|
|
2718
|
-
}).join(", ");
|
|
2719
|
-
}
|
|
2720
2492
|
function generatePublicFunction(func, contractId) {
|
|
2721
|
-
const methodName =
|
|
2722
|
-
const argsSignature =
|
|
2723
|
-
const clarityArgs =
|
|
2493
|
+
const methodName = toCamelCase8(func.name);
|
|
2494
|
+
const argsSignature = generateArgsSignature(func.args);
|
|
2495
|
+
const clarityArgs = generateClarityArgs(func.args);
|
|
2724
2496
|
return `${methodName}: (${argsSignature}caller: string) => {
|
|
2725
2497
|
const callerAddr = accounts.get(caller) ?? caller;
|
|
2726
2498
|
return simnet.callPublicFn(
|
|
@@ -2732,9 +2504,9 @@ function generatePublicFunction(func, contractId) {
|
|
|
2732
2504
|
}`;
|
|
2733
2505
|
}
|
|
2734
2506
|
function generateReadOnlyFunction(func, contractId) {
|
|
2735
|
-
const methodName =
|
|
2736
|
-
const argsSignature =
|
|
2737
|
-
const clarityArgs =
|
|
2507
|
+
const methodName = toCamelCase8(func.name);
|
|
2508
|
+
const argsSignature = generateArgsSignature(func.args);
|
|
2509
|
+
const clarityArgs = generateClarityArgs(func.args);
|
|
2738
2510
|
const hasArgs = func.args.length > 0;
|
|
2739
2511
|
const argsParam = hasArgs ? argsSignature : "";
|
|
2740
2512
|
return `${methodName}: (${argsParam}) => {
|
|
@@ -2747,9 +2519,9 @@ function generateReadOnlyFunction(func, contractId) {
|
|
|
2747
2519
|
}`;
|
|
2748
2520
|
}
|
|
2749
2521
|
function generatePrivateFunction(func, contractId) {
|
|
2750
|
-
const methodName =
|
|
2751
|
-
const argsSignature =
|
|
2752
|
-
const clarityArgs =
|
|
2522
|
+
const methodName = toCamelCase8(func.name);
|
|
2523
|
+
const argsSignature = generateArgsSignature(func.args);
|
|
2524
|
+
const clarityArgs = generateClarityArgs(func.args);
|
|
2753
2525
|
return `${methodName}: (${argsSignature}caller: string) => {
|
|
2754
2526
|
const callerAddr = accounts.get(caller) ?? caller;
|
|
2755
2527
|
return simnet.callPrivateFn(
|
|
@@ -2761,31 +2533,31 @@ function generatePrivateFunction(func, contractId) {
|
|
|
2761
2533
|
}`;
|
|
2762
2534
|
}
|
|
2763
2535
|
function generateDataVarHelper(variable, contractId) {
|
|
2764
|
-
const methodName =
|
|
2536
|
+
const methodName = toCamelCase8(variable.name);
|
|
2765
2537
|
return `${methodName}: () => {
|
|
2766
2538
|
return simnet.getDataVar('${contractId}', '${variable.name}');
|
|
2767
2539
|
}`;
|
|
2768
2540
|
}
|
|
2769
2541
|
function getMapKeyType(keyType) {
|
|
2770
|
-
if (keyType
|
|
2771
|
-
const fields = keyType.tuple.map((field) => `${
|
|
2542
|
+
if (isAbiTuple3(keyType)) {
|
|
2543
|
+
const fields = keyType.tuple.map((field) => `${toCamelCase8(field.name)}: ${getTypeForArg({ type: field.type })}`).join("; ");
|
|
2772
2544
|
return `{ ${fields} }`;
|
|
2773
2545
|
}
|
|
2774
2546
|
return getTypeForArg({ type: keyType });
|
|
2775
2547
|
}
|
|
2776
2548
|
function generateMapKeyConversion2(keyType) {
|
|
2777
|
-
if (keyType
|
|
2549
|
+
if (isAbiTuple3(keyType)) {
|
|
2778
2550
|
const fields = keyType.tuple.map((field) => {
|
|
2779
|
-
const camelFieldName =
|
|
2780
|
-
const fieldConversion =
|
|
2551
|
+
const camelFieldName = toCamelCase8(field.name);
|
|
2552
|
+
const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
|
|
2781
2553
|
return `"${field.name}": ${fieldConversion}`;
|
|
2782
2554
|
}).join(", ");
|
|
2783
2555
|
return `Cl.tuple({ ${fields} })`;
|
|
2784
2556
|
}
|
|
2785
|
-
return
|
|
2557
|
+
return generateClarityConversion("key", { type: keyType });
|
|
2786
2558
|
}
|
|
2787
2559
|
function generateMapEntryHelper(map, contractId) {
|
|
2788
|
-
const methodName =
|
|
2560
|
+
const methodName = toCamelCase8(map.name);
|
|
2789
2561
|
const keyType = getMapKeyType(map.key);
|
|
2790
2562
|
const keyConversion = generateMapKeyConversion2(map.key);
|
|
2791
2563
|
return `${methodName}: (key: ${keyType}) => {
|
|
@@ -2826,7 +2598,7 @@ function generateContractHelper(contract, options) {
|
|
|
2826
2598
|
const maps = abi.maps || [];
|
|
2827
2599
|
const pascalName = toPascalCase(name);
|
|
2828
2600
|
const publicFns = functions.filter((f) => f.access === "public");
|
|
2829
|
-
const readOnlyFns = functions.filter((f) => f.access === "
|
|
2601
|
+
const readOnlyFns = functions.filter((f) => f.access === "read-only");
|
|
2830
2602
|
const privateFns = options.includePrivate ? functions.filter((f) => f.access === "private") : [];
|
|
2831
2603
|
const publicHelpers = publicFns.map((f) => generatePublicFunction(f, address));
|
|
2832
2604
|
const readOnlyHelpers = readOnlyFns.map((f) => generateReadOnlyFunction(f, address));
|
|
@@ -2855,7 +2627,7 @@ function generateContractHelper(contract, options) {
|
|
|
2855
2627
|
}
|
|
2856
2628
|
function generateGetContracts(contracts) {
|
|
2857
2629
|
const contractEntries = contracts.map((contract) => {
|
|
2858
|
-
const camelName =
|
|
2630
|
+
const camelName = toCamelCase8(contract.name);
|
|
2859
2631
|
const pascalName = toPascalCase(contract.name);
|
|
2860
2632
|
return `${camelName}: get${pascalName}(simnet)`;
|
|
2861
2633
|
}).join(`,
|
|
@@ -2983,5 +2755,5 @@ export {
|
|
|
2983
2755
|
PluginManager
|
|
2984
2756
|
};
|
|
2985
2757
|
|
|
2986
|
-
//# debugId=
|
|
2758
|
+
//# debugId=217DF2A543BBE57564756E2164756E21
|
|
2987
2759
|
//# sourceMappingURL=index.js.map
|