@secondlayer/cli 0.2.5 → 0.3.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 +207 -6
- package/dist/cli.js.map +4 -4
- package/dist/index.js +309 -41
- package/dist/index.js.map +8 -8
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -15702,6 +15702,8 @@ function inferReturnType(body) {
|
|
|
15702
15702
|
function parseApiResponse(apiResponse) {
|
|
15703
15703
|
try {
|
|
15704
15704
|
const functions = [];
|
|
15705
|
+
const maps = [];
|
|
15706
|
+
const variables = [];
|
|
15705
15707
|
if (apiResponse.functions) {
|
|
15706
15708
|
for (const func of apiResponse.functions) {
|
|
15707
15709
|
const access = func.access === "read_only" ? "read-only" : func.access;
|
|
@@ -15716,7 +15718,29 @@ function parseApiResponse(apiResponse) {
|
|
|
15716
15718
|
});
|
|
15717
15719
|
}
|
|
15718
15720
|
}
|
|
15719
|
-
|
|
15721
|
+
if (apiResponse.maps) {
|
|
15722
|
+
for (const map of apiResponse.maps) {
|
|
15723
|
+
maps.push({
|
|
15724
|
+
name: map.name,
|
|
15725
|
+
key: convertApiType(map.key),
|
|
15726
|
+
value: convertApiType(map.value)
|
|
15727
|
+
});
|
|
15728
|
+
}
|
|
15729
|
+
}
|
|
15730
|
+
if (apiResponse.variables) {
|
|
15731
|
+
for (const variable of apiResponse.variables) {
|
|
15732
|
+
variables.push({
|
|
15733
|
+
name: variable.name,
|
|
15734
|
+
type: convertApiType(variable.type),
|
|
15735
|
+
access: variable.access
|
|
15736
|
+
});
|
|
15737
|
+
}
|
|
15738
|
+
}
|
|
15739
|
+
return {
|
|
15740
|
+
functions,
|
|
15741
|
+
maps: maps.length > 0 ? maps : undefined,
|
|
15742
|
+
variables: variables.length > 0 ? variables : undefined
|
|
15743
|
+
};
|
|
15720
15744
|
} catch (error) {
|
|
15721
15745
|
throw new Error(`Failed to parse API response: ${error}`);
|
|
15722
15746
|
}
|
|
@@ -15787,12 +15811,44 @@ var init_clarity = () => {};
|
|
|
15787
15811
|
|
|
15788
15812
|
// src/generators/contract.ts
|
|
15789
15813
|
import { format } from "prettier";
|
|
15814
|
+
function generateNetworkUtils() {
|
|
15815
|
+
return `/**
|
|
15816
|
+
* API URLs for different networks
|
|
15817
|
+
*/
|
|
15818
|
+
const API_URLS: Record<'mainnet' | 'testnet' | 'devnet', string> = {
|
|
15819
|
+
mainnet: 'https://api.hiro.so',
|
|
15820
|
+
testnet: 'https://api.testnet.hiro.so',
|
|
15821
|
+
devnet: 'http://localhost:3999'
|
|
15822
|
+
};
|
|
15823
|
+
|
|
15824
|
+
/**
|
|
15825
|
+
* Infer network from Stacks address prefix
|
|
15826
|
+
* SP/SM = mainnet, ST/SN = testnet
|
|
15827
|
+
*/
|
|
15828
|
+
function inferNetworkFromAddress(address: string): 'mainnet' | 'testnet' | undefined {
|
|
15829
|
+
if (address.startsWith('SP') || address.startsWith('SM')) return 'mainnet';
|
|
15830
|
+
if (address.startsWith('ST') || address.startsWith('SN')) return 'testnet';
|
|
15831
|
+
return undefined;
|
|
15832
|
+
}
|
|
15833
|
+
|
|
15834
|
+
/**
|
|
15835
|
+
* Get API URL, inferring network from contract address if not specified
|
|
15836
|
+
*/
|
|
15837
|
+
function getApiUrl(
|
|
15838
|
+
contractAddress: string,
|
|
15839
|
+
explicitNetwork?: 'mainnet' | 'testnet' | 'devnet'
|
|
15840
|
+
): string {
|
|
15841
|
+
const network = explicitNetwork ?? inferNetworkFromAddress(contractAddress) ?? 'mainnet';
|
|
15842
|
+
return API_URLS[network];
|
|
15843
|
+
}`;
|
|
15844
|
+
}
|
|
15790
15845
|
async function generateContractInterface(contracts) {
|
|
15791
15846
|
const imports = `import { Cl, validateStacksAddress } from '@stacks/transactions'`;
|
|
15792
15847
|
const header = `/**
|
|
15793
15848
|
* Generated by @secondlayer/cli
|
|
15794
15849
|
* DO NOT EDIT MANUALLY
|
|
15795
15850
|
*/`;
|
|
15851
|
+
const networkUtils = generateNetworkUtils();
|
|
15796
15852
|
const contractsCode = contracts.map((contract) => generateContract(contract)).join(`
|
|
15797
15853
|
|
|
15798
15854
|
`);
|
|
@@ -15800,6 +15856,8 @@ async function generateContractInterface(contracts) {
|
|
|
15800
15856
|
|
|
15801
15857
|
${header}
|
|
15802
15858
|
|
|
15859
|
+
${networkUtils}
|
|
15860
|
+
|
|
15803
15861
|
${contractsCode}`;
|
|
15804
15862
|
const formatted = await format(code, {
|
|
15805
15863
|
parser: "typescript",
|
|
@@ -15816,12 +15874,18 @@ function generateContract(contract) {
|
|
|
15816
15874
|
const methods = abi.functions.filter((func) => func.access !== "private").map((func) => generateMethod(func, address, contractName)).join(`,
|
|
15817
15875
|
|
|
15818
15876
|
`);
|
|
15877
|
+
const mapsObject = generateMapsObject(abi.maps || [], address, contractName);
|
|
15878
|
+
const varsObject = generateVarsObject(abi.variables || [], address, contractName);
|
|
15879
|
+
const constantsObject = generateConstantsObject(abi.variables || [], address, contractName);
|
|
15880
|
+
const allMembers = [methods, mapsObject, varsObject, constantsObject].filter(Boolean);
|
|
15819
15881
|
const contractCode = `export const ${name} = {
|
|
15820
15882
|
address: '${address}',
|
|
15821
15883
|
contractAddress: '${address}',
|
|
15822
15884
|
contractName: '${contractName}',
|
|
15823
|
-
|
|
15824
|
-
${
|
|
15885
|
+
|
|
15886
|
+
${allMembers.join(`,
|
|
15887
|
+
|
|
15888
|
+
`)}
|
|
15825
15889
|
} as const`;
|
|
15826
15890
|
return `${abiCode}
|
|
15827
15891
|
|
|
@@ -16032,6 +16096,143 @@ function generateClarityConversion(argName, argType) {
|
|
|
16032
16096
|
}
|
|
16033
16097
|
return `${argName}`;
|
|
16034
16098
|
}
|
|
16099
|
+
function generateMapsObject(maps, address, contractName) {
|
|
16100
|
+
if (!maps || maps.length === 0) {
|
|
16101
|
+
return "";
|
|
16102
|
+
}
|
|
16103
|
+
const mapMethods = maps.map((map) => {
|
|
16104
|
+
const methodName = toCamelCase(map.name);
|
|
16105
|
+
const keyType = getTypeForArg({ type: map.key });
|
|
16106
|
+
const valueType = getTypeForArg({ type: map.value });
|
|
16107
|
+
const keyConversion = generateMapKeyConversion(map.key);
|
|
16108
|
+
return `${methodName}: {
|
|
16109
|
+
async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
|
|
16110
|
+
const { cvToJSON, serializeCV } = await import('@stacks/transactions');
|
|
16111
|
+
const baseUrl = getApiUrl('${address}', options?.network);
|
|
16112
|
+
const mapKey = ${keyConversion};
|
|
16113
|
+
const keyHex = serializeCV(mapKey).toString('hex');
|
|
16114
|
+
|
|
16115
|
+
const response = await fetch(
|
|
16116
|
+
\`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
|
|
16117
|
+
{
|
|
16118
|
+
method: 'POST',
|
|
16119
|
+
headers: { 'Content-Type': 'application/json' },
|
|
16120
|
+
body: JSON.stringify(keyHex)
|
|
16121
|
+
}
|
|
16122
|
+
);
|
|
16123
|
+
|
|
16124
|
+
if (!response.ok) {
|
|
16125
|
+
throw new Error(\`Failed to fetch map entry: \${response.statusText}\`);
|
|
16126
|
+
}
|
|
16127
|
+
|
|
16128
|
+
const result = await response.json();
|
|
16129
|
+
if (!result.data || result.data === '0x09') {
|
|
16130
|
+
return null; // none value
|
|
16131
|
+
}
|
|
16132
|
+
|
|
16133
|
+
const { deserializeCV } = await import('@stacks/transactions');
|
|
16134
|
+
const cv = deserializeCV(result.data);
|
|
16135
|
+
const parsed = cvToJSON(cv);
|
|
16136
|
+
// Unwrap the (some ...) wrapper
|
|
16137
|
+
return parsed.value?.value ?? parsed.value ?? null;
|
|
16138
|
+
},
|
|
16139
|
+
keyType: ${JSON.stringify(map.key)} as const,
|
|
16140
|
+
valueType: ${JSON.stringify(map.value)} as const
|
|
16141
|
+
}`;
|
|
16142
|
+
});
|
|
16143
|
+
return `maps: {
|
|
16144
|
+
${mapMethods.join(`,
|
|
16145
|
+
|
|
16146
|
+
`)}
|
|
16147
|
+
}`;
|
|
16148
|
+
}
|
|
16149
|
+
function generateVarsObject(variables, address, contractName) {
|
|
16150
|
+
if (!variables || variables.length === 0) {
|
|
16151
|
+
return "";
|
|
16152
|
+
}
|
|
16153
|
+
const dataVars = variables.filter((v) => v.access === "variable");
|
|
16154
|
+
if (dataVars.length === 0) {
|
|
16155
|
+
return "";
|
|
16156
|
+
}
|
|
16157
|
+
const varMethods = dataVars.map((variable) => {
|
|
16158
|
+
const methodName = toCamelCase(variable.name);
|
|
16159
|
+
const valueType = getTypeForArg({ type: variable.type });
|
|
16160
|
+
return `${methodName}: {
|
|
16161
|
+
async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
|
|
16162
|
+
const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
|
|
16163
|
+
const baseUrl = getApiUrl('${address}', options?.network);
|
|
16164
|
+
|
|
16165
|
+
const response = await fetch(
|
|
16166
|
+
\`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
|
|
16167
|
+
);
|
|
16168
|
+
|
|
16169
|
+
if (!response.ok) {
|
|
16170
|
+
throw new Error(\`Failed to fetch data var: \${response.statusText}\`);
|
|
16171
|
+
}
|
|
16172
|
+
|
|
16173
|
+
const result = await response.json();
|
|
16174
|
+
const cv = deserializeCV(result.data);
|
|
16175
|
+
const parsed = cvToJSON(cv);
|
|
16176
|
+
return parsed.value ?? parsed;
|
|
16177
|
+
},
|
|
16178
|
+
type: ${JSON.stringify(variable.type)} as const
|
|
16179
|
+
}`;
|
|
16180
|
+
});
|
|
16181
|
+
return `vars: {
|
|
16182
|
+
${varMethods.join(`,
|
|
16183
|
+
|
|
16184
|
+
`)}
|
|
16185
|
+
}`;
|
|
16186
|
+
}
|
|
16187
|
+
function generateConstantsObject(variables, address, contractName) {
|
|
16188
|
+
if (!variables || variables.length === 0) {
|
|
16189
|
+
return "";
|
|
16190
|
+
}
|
|
16191
|
+
const constants = variables.filter((v) => v.access === "constant");
|
|
16192
|
+
if (constants.length === 0) {
|
|
16193
|
+
return "";
|
|
16194
|
+
}
|
|
16195
|
+
const constMethods = constants.map((constant) => {
|
|
16196
|
+
const methodName = toCamelCase(constant.name);
|
|
16197
|
+
const valueType = getTypeForArg({ type: constant.type });
|
|
16198
|
+
return `${methodName}: {
|
|
16199
|
+
async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
|
|
16200
|
+
const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
|
|
16201
|
+
const baseUrl = getApiUrl('${address}', options?.network);
|
|
16202
|
+
|
|
16203
|
+
const response = await fetch(
|
|
16204
|
+
\`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
|
|
16205
|
+
);
|
|
16206
|
+
|
|
16207
|
+
if (!response.ok) {
|
|
16208
|
+
throw new Error(\`Failed to fetch constant: \${response.statusText}\`);
|
|
16209
|
+
}
|
|
16210
|
+
|
|
16211
|
+
const result = await response.json();
|
|
16212
|
+
const cv = deserializeCV(result.data);
|
|
16213
|
+
const parsed = cvToJSON(cv);
|
|
16214
|
+
return parsed.value ?? parsed;
|
|
16215
|
+
},
|
|
16216
|
+
type: ${JSON.stringify(constant.type)} as const
|
|
16217
|
+
}`;
|
|
16218
|
+
});
|
|
16219
|
+
return `constants: {
|
|
16220
|
+
${constMethods.join(`,
|
|
16221
|
+
|
|
16222
|
+
`)}
|
|
16223
|
+
}`;
|
|
16224
|
+
}
|
|
16225
|
+
function generateMapKeyConversion(keyType) {
|
|
16226
|
+
if (keyType.tuple) {
|
|
16227
|
+
const fields = keyType.tuple.map((field) => {
|
|
16228
|
+
const camelFieldName = toCamelCase(field.name);
|
|
16229
|
+
const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
|
|
16230
|
+
return `"${field.name}": ${fieldConversion}`;
|
|
16231
|
+
}).join(", ");
|
|
16232
|
+
return `Cl.tuple({ ${fields} })`;
|
|
16233
|
+
}
|
|
16234
|
+
return generateClarityConversion("key", { type: keyType });
|
|
16235
|
+
}
|
|
16035
16236
|
var init_contract = () => {};
|
|
16036
16237
|
|
|
16037
16238
|
// src/commands/generate.ts
|
|
@@ -16361,7 +16562,7 @@ var {
|
|
|
16361
16562
|
// package.json
|
|
16362
16563
|
var package_default = {
|
|
16363
16564
|
name: "@secondlayer/cli",
|
|
16364
|
-
version: "0.
|
|
16565
|
+
version: "0.3.1",
|
|
16365
16566
|
description: "CLI for generating type-safe contract interfaces for the Stacks blockchain",
|
|
16366
16567
|
type: "module",
|
|
16367
16568
|
bin: {
|
|
@@ -16400,7 +16601,7 @@ var package_default = {
|
|
|
16400
16601
|
author: "",
|
|
16401
16602
|
license: "MIT",
|
|
16402
16603
|
dependencies: {
|
|
16403
|
-
"@secondlayer/clarity-types": "^0.
|
|
16604
|
+
"@secondlayer/clarity-types": "^0.3.0",
|
|
16404
16605
|
"@stacks/transactions": "7.0.6",
|
|
16405
16606
|
esbuild: "^0.19.0",
|
|
16406
16607
|
prettier: "^3.1.0"
|
|
@@ -16437,5 +16638,5 @@ program.command("init").description("Initialize a new stacks.config.ts file").ac
|
|
|
16437
16638
|
});
|
|
16438
16639
|
program.parse();
|
|
16439
16640
|
|
|
16440
|
-
//# debugId=
|
|
16641
|
+
//# debugId=00D09707C564934264756E2164756E21
|
|
16441
16642
|
//# sourceMappingURL=cli.js.map
|