@movebridge/codegen 0.0.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/LICENSE +0 -0
- package/dist/chunk-NMH4XS2A.mjs +230 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +260 -0
- package/dist/cli.mjs +35 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +257 -0
- package/dist/index.mjs +8 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
File without changes
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// src/parser.ts
|
|
2
|
+
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
|
|
3
|
+
import { NETWORK_CONFIG, Errors } from "@movebridge/core";
|
|
4
|
+
var ABIParser = class {
|
|
5
|
+
aptosClient;
|
|
6
|
+
constructor(network) {
|
|
7
|
+
const config = NETWORK_CONFIG[network];
|
|
8
|
+
const aptosConfig = new AptosConfig({
|
|
9
|
+
network: Network.CUSTOM,
|
|
10
|
+
fullnode: config.rpcUrl
|
|
11
|
+
});
|
|
12
|
+
this.aptosClient = new Aptos(aptosConfig);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fetches the ABI for a module
|
|
16
|
+
* @param moduleAddress - Full module address (e.g., '0x1::coin')
|
|
17
|
+
* @returns Module ABI
|
|
18
|
+
*/
|
|
19
|
+
async fetchABI(moduleAddress) {
|
|
20
|
+
const parts = moduleAddress.split("::");
|
|
21
|
+
if (parts.length !== 2) {
|
|
22
|
+
throw Errors.invalidArgument(
|
|
23
|
+
"moduleAddress",
|
|
24
|
+
"Expected format: 0xADDRESS::module_name"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const address = parts[0];
|
|
28
|
+
const moduleName = parts[1];
|
|
29
|
+
if (!address || !moduleName) {
|
|
30
|
+
throw Errors.invalidArgument(
|
|
31
|
+
"moduleAddress",
|
|
32
|
+
"Expected format: 0xADDRESS::module_name"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const client = this.aptosClient;
|
|
37
|
+
const modules = await client.getAccountModules({
|
|
38
|
+
accountAddress: address
|
|
39
|
+
});
|
|
40
|
+
const targetModule = modules.find((m) => {
|
|
41
|
+
const abi2 = m.abi;
|
|
42
|
+
return abi2?.name === moduleName;
|
|
43
|
+
});
|
|
44
|
+
if (!targetModule || !targetModule.abi) {
|
|
45
|
+
throw Errors.abiFetchFailed(moduleAddress, "Module not found");
|
|
46
|
+
}
|
|
47
|
+
const abi = targetModule.abi;
|
|
48
|
+
const exposedFunctions = abi.exposed_functions.map((fn) => ({
|
|
49
|
+
name: fn.name,
|
|
50
|
+
visibility: fn.visibility,
|
|
51
|
+
isEntry: fn.is_entry,
|
|
52
|
+
isView: fn.is_view,
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
genericTypeParams: fn.generic_type_params.map((p) => ({
|
|
55
|
+
constraints: p.constraints
|
|
56
|
+
})),
|
|
57
|
+
params: fn.params,
|
|
58
|
+
return: fn.return
|
|
59
|
+
}));
|
|
60
|
+
return {
|
|
61
|
+
address,
|
|
62
|
+
name: moduleName,
|
|
63
|
+
exposedFunctions
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error instanceof Error && error.message.includes("Module not found")) {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
throw Errors.abiFetchFailed(moduleAddress, "network", error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parses a Move type to TypeScript type
|
|
74
|
+
* @param moveType - Move type string
|
|
75
|
+
* @returns TypeScript type string
|
|
76
|
+
*/
|
|
77
|
+
parseType(moveType) {
|
|
78
|
+
const typeMap = {
|
|
79
|
+
"bool": "boolean",
|
|
80
|
+
"u8": "number",
|
|
81
|
+
"u16": "number",
|
|
82
|
+
"u32": "number",
|
|
83
|
+
"u64": "string",
|
|
84
|
+
"u128": "string",
|
|
85
|
+
"u256": "string",
|
|
86
|
+
"address": "string",
|
|
87
|
+
"&signer": "void",
|
|
88
|
+
"signer": "void",
|
|
89
|
+
"0x1::string::String": "string"
|
|
90
|
+
};
|
|
91
|
+
if (typeMap[moveType]) {
|
|
92
|
+
return typeMap[moveType];
|
|
93
|
+
}
|
|
94
|
+
if (moveType.startsWith("vector<")) {
|
|
95
|
+
const innerType = moveType.slice(7, -1);
|
|
96
|
+
return `${this.parseType(innerType)}[]`;
|
|
97
|
+
}
|
|
98
|
+
if (/^T\d+$/.test(moveType)) {
|
|
99
|
+
return moveType;
|
|
100
|
+
}
|
|
101
|
+
return "unknown";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/generator.ts
|
|
106
|
+
var TypeGenerator = class {
|
|
107
|
+
parser;
|
|
108
|
+
constructor(parser) {
|
|
109
|
+
this.parser = parser;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Generates a TypeScript class from a module ABI
|
|
113
|
+
* @param abi - Module ABI
|
|
114
|
+
* @returns TypeScript code string
|
|
115
|
+
*/
|
|
116
|
+
generateClass(abi) {
|
|
117
|
+
const className = this.toPascalCase(abi.name) + "Contract";
|
|
118
|
+
const lines = [];
|
|
119
|
+
lines.push("/**");
|
|
120
|
+
lines.push(` * Auto-generated TypeScript bindings for ${abi.address}::${abi.name}`);
|
|
121
|
+
lines.push(" * Generated by @movebridge/codegen");
|
|
122
|
+
lines.push(" */");
|
|
123
|
+
lines.push("");
|
|
124
|
+
lines.push("import type { Movement } from '@movebridge/core';");
|
|
125
|
+
lines.push("");
|
|
126
|
+
lines.push(`export class ${className} {`);
|
|
127
|
+
lines.push(` private readonly movement: Movement;`);
|
|
128
|
+
lines.push(` private readonly address = '${abi.address}';`);
|
|
129
|
+
lines.push(` private readonly module = '${abi.name}';`);
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push(" /**");
|
|
132
|
+
lines.push(` * Creates a new ${className} instance`);
|
|
133
|
+
lines.push(" * @param movement - Movement SDK instance");
|
|
134
|
+
lines.push(" */");
|
|
135
|
+
lines.push(" constructor(movement: Movement) {");
|
|
136
|
+
lines.push(" this.movement = movement;");
|
|
137
|
+
lines.push(" }");
|
|
138
|
+
lines.push("");
|
|
139
|
+
for (const fn of abi.exposedFunctions) {
|
|
140
|
+
if (fn.visibility === "private") continue;
|
|
141
|
+
if (fn.params.length === 1 && fn.params[0] === "&signer") continue;
|
|
142
|
+
const method = this.generateMethod(fn);
|
|
143
|
+
lines.push(method);
|
|
144
|
+
lines.push("");
|
|
145
|
+
}
|
|
146
|
+
lines.push("}");
|
|
147
|
+
return lines.join("\n");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generates a method from a function ABI
|
|
151
|
+
* @param fn - Function ABI
|
|
152
|
+
* @returns Method code string
|
|
153
|
+
*/
|
|
154
|
+
generateMethod(fn) {
|
|
155
|
+
const lines = [];
|
|
156
|
+
const methodName = this.toCamelCase(fn.name);
|
|
157
|
+
const params = fn.params.filter((p) => p !== "&signer" && p !== "signer");
|
|
158
|
+
const paramNames = params.map((_, i) => `arg${i}`);
|
|
159
|
+
const paramTypes = params.map((p) => this.parser.parseType(p));
|
|
160
|
+
let genericParams = "";
|
|
161
|
+
if (fn.genericTypeParams.length > 0) {
|
|
162
|
+
const typeParams = fn.genericTypeParams.map((_, i) => `T${i} = unknown`);
|
|
163
|
+
genericParams = `<${typeParams.join(", ")}>`;
|
|
164
|
+
}
|
|
165
|
+
const paramList = paramNames.map((name, i) => `${name}: ${paramTypes[i]}`).join(", ");
|
|
166
|
+
const hasTypeArgs = fn.genericTypeParams.length > 0;
|
|
167
|
+
const typeArgsParam = hasTypeArgs ? ", typeArgs: string[]" : "";
|
|
168
|
+
let returnType;
|
|
169
|
+
if (fn.isView) {
|
|
170
|
+
if (fn.return.length === 0) {
|
|
171
|
+
returnType = "void";
|
|
172
|
+
} else if (fn.return.length === 1) {
|
|
173
|
+
returnType = this.parser.parseType(fn.return[0] ?? "unknown");
|
|
174
|
+
} else {
|
|
175
|
+
returnType = `[${fn.return.map((r) => this.parser.parseType(r ?? "unknown")).join(", ")}]`;
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
returnType = "string";
|
|
179
|
+
}
|
|
180
|
+
lines.push(" /**");
|
|
181
|
+
lines.push(` * ${fn.isView ? "View" : "Entry"} function: ${fn.name}`);
|
|
182
|
+
paramNames.forEach((name, i) => {
|
|
183
|
+
lines.push(` * @param ${name} - Parameter of type ${params[i]}`);
|
|
184
|
+
});
|
|
185
|
+
if (hasTypeArgs) {
|
|
186
|
+
lines.push(" * @param typeArgs - Type arguments");
|
|
187
|
+
}
|
|
188
|
+
lines.push(` * @returns ${fn.isView ? "Function result" : "Transaction hash"}`);
|
|
189
|
+
lines.push(" */");
|
|
190
|
+
const asyncKeyword = "async";
|
|
191
|
+
lines.push(
|
|
192
|
+
` ${asyncKeyword} ${methodName}${genericParams}(${paramList}${typeArgsParam}): Promise<${returnType}> {`
|
|
193
|
+
);
|
|
194
|
+
const argsArray = paramNames.length > 0 ? `[${paramNames.join(", ")}]` : "[]";
|
|
195
|
+
const typeArgsArray = hasTypeArgs ? "typeArgs" : "[]";
|
|
196
|
+
if (fn.isView) {
|
|
197
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
198
|
+
lines.push(` address: this.address,`);
|
|
199
|
+
lines.push(` module: this.module,`);
|
|
200
|
+
lines.push(` });`);
|
|
201
|
+
lines.push(` return contract.view<${returnType}>('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
202
|
+
} else {
|
|
203
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
204
|
+
lines.push(` address: this.address,`);
|
|
205
|
+
lines.push(` module: this.module,`);
|
|
206
|
+
lines.push(` });`);
|
|
207
|
+
lines.push(` return contract.call('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
208
|
+
}
|
|
209
|
+
lines.push(" }");
|
|
210
|
+
return lines.join("\n");
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Converts a snake_case string to PascalCase
|
|
214
|
+
*/
|
|
215
|
+
toPascalCase(str) {
|
|
216
|
+
return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Converts a snake_case string to camelCase
|
|
220
|
+
*/
|
|
221
|
+
toCamelCase(str) {
|
|
222
|
+
const pascal = this.toPascalCase(str);
|
|
223
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
ABIParser,
|
|
229
|
+
TypeGenerator
|
|
230
|
+
};
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
var import_commander = require("commander");
|
|
6
|
+
var import_promises = require("fs/promises");
|
|
7
|
+
var import_path = require("path");
|
|
8
|
+
|
|
9
|
+
// src/parser.ts
|
|
10
|
+
var import_ts_sdk = require("@aptos-labs/ts-sdk");
|
|
11
|
+
var import_core = require("@movebridge/core");
|
|
12
|
+
var ABIParser = class {
|
|
13
|
+
aptosClient;
|
|
14
|
+
constructor(network) {
|
|
15
|
+
const config = import_core.NETWORK_CONFIG[network];
|
|
16
|
+
const aptosConfig = new import_ts_sdk.AptosConfig({
|
|
17
|
+
network: import_ts_sdk.Network.CUSTOM,
|
|
18
|
+
fullnode: config.rpcUrl
|
|
19
|
+
});
|
|
20
|
+
this.aptosClient = new import_ts_sdk.Aptos(aptosConfig);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fetches the ABI for a module
|
|
24
|
+
* @param moduleAddress - Full module address (e.g., '0x1::coin')
|
|
25
|
+
* @returns Module ABI
|
|
26
|
+
*/
|
|
27
|
+
async fetchABI(moduleAddress) {
|
|
28
|
+
const parts = moduleAddress.split("::");
|
|
29
|
+
if (parts.length !== 2) {
|
|
30
|
+
throw import_core.Errors.invalidArgument(
|
|
31
|
+
"moduleAddress",
|
|
32
|
+
"Expected format: 0xADDRESS::module_name"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const address = parts[0];
|
|
36
|
+
const moduleName = parts[1];
|
|
37
|
+
if (!address || !moduleName) {
|
|
38
|
+
throw import_core.Errors.invalidArgument(
|
|
39
|
+
"moduleAddress",
|
|
40
|
+
"Expected format: 0xADDRESS::module_name"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const client = this.aptosClient;
|
|
45
|
+
const modules = await client.getAccountModules({
|
|
46
|
+
accountAddress: address
|
|
47
|
+
});
|
|
48
|
+
const targetModule = modules.find((m) => {
|
|
49
|
+
const abi2 = m.abi;
|
|
50
|
+
return abi2?.name === moduleName;
|
|
51
|
+
});
|
|
52
|
+
if (!targetModule || !targetModule.abi) {
|
|
53
|
+
throw import_core.Errors.abiFetchFailed(moduleAddress, "Module not found");
|
|
54
|
+
}
|
|
55
|
+
const abi = targetModule.abi;
|
|
56
|
+
const exposedFunctions = abi.exposed_functions.map((fn) => ({
|
|
57
|
+
name: fn.name,
|
|
58
|
+
visibility: fn.visibility,
|
|
59
|
+
isEntry: fn.is_entry,
|
|
60
|
+
isView: fn.is_view,
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
genericTypeParams: fn.generic_type_params.map((p) => ({
|
|
63
|
+
constraints: p.constraints
|
|
64
|
+
})),
|
|
65
|
+
params: fn.params,
|
|
66
|
+
return: fn.return
|
|
67
|
+
}));
|
|
68
|
+
return {
|
|
69
|
+
address,
|
|
70
|
+
name: moduleName,
|
|
71
|
+
exposedFunctions
|
|
72
|
+
};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error instanceof Error && error.message.includes("Module not found")) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
throw import_core.Errors.abiFetchFailed(moduleAddress, "network", error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parses a Move type to TypeScript type
|
|
82
|
+
* @param moveType - Move type string
|
|
83
|
+
* @returns TypeScript type string
|
|
84
|
+
*/
|
|
85
|
+
parseType(moveType) {
|
|
86
|
+
const typeMap = {
|
|
87
|
+
"bool": "boolean",
|
|
88
|
+
"u8": "number",
|
|
89
|
+
"u16": "number",
|
|
90
|
+
"u32": "number",
|
|
91
|
+
"u64": "string",
|
|
92
|
+
"u128": "string",
|
|
93
|
+
"u256": "string",
|
|
94
|
+
"address": "string",
|
|
95
|
+
"&signer": "void",
|
|
96
|
+
"signer": "void",
|
|
97
|
+
"0x1::string::String": "string"
|
|
98
|
+
};
|
|
99
|
+
if (typeMap[moveType]) {
|
|
100
|
+
return typeMap[moveType];
|
|
101
|
+
}
|
|
102
|
+
if (moveType.startsWith("vector<")) {
|
|
103
|
+
const innerType = moveType.slice(7, -1);
|
|
104
|
+
return `${this.parseType(innerType)}[]`;
|
|
105
|
+
}
|
|
106
|
+
if (/^T\d+$/.test(moveType)) {
|
|
107
|
+
return moveType;
|
|
108
|
+
}
|
|
109
|
+
return "unknown";
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/generator.ts
|
|
114
|
+
var TypeGenerator = class {
|
|
115
|
+
parser;
|
|
116
|
+
constructor(parser) {
|
|
117
|
+
this.parser = parser;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Generates a TypeScript class from a module ABI
|
|
121
|
+
* @param abi - Module ABI
|
|
122
|
+
* @returns TypeScript code string
|
|
123
|
+
*/
|
|
124
|
+
generateClass(abi) {
|
|
125
|
+
const className = this.toPascalCase(abi.name) + "Contract";
|
|
126
|
+
const lines = [];
|
|
127
|
+
lines.push("/**");
|
|
128
|
+
lines.push(` * Auto-generated TypeScript bindings for ${abi.address}::${abi.name}`);
|
|
129
|
+
lines.push(" * Generated by @movebridge/codegen");
|
|
130
|
+
lines.push(" */");
|
|
131
|
+
lines.push("");
|
|
132
|
+
lines.push("import type { Movement } from '@movebridge/core';");
|
|
133
|
+
lines.push("");
|
|
134
|
+
lines.push(`export class ${className} {`);
|
|
135
|
+
lines.push(` private readonly movement: Movement;`);
|
|
136
|
+
lines.push(` private readonly address = '${abi.address}';`);
|
|
137
|
+
lines.push(` private readonly module = '${abi.name}';`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
lines.push(" /**");
|
|
140
|
+
lines.push(` * Creates a new ${className} instance`);
|
|
141
|
+
lines.push(" * @param movement - Movement SDK instance");
|
|
142
|
+
lines.push(" */");
|
|
143
|
+
lines.push(" constructor(movement: Movement) {");
|
|
144
|
+
lines.push(" this.movement = movement;");
|
|
145
|
+
lines.push(" }");
|
|
146
|
+
lines.push("");
|
|
147
|
+
for (const fn of abi.exposedFunctions) {
|
|
148
|
+
if (fn.visibility === "private") continue;
|
|
149
|
+
if (fn.params.length === 1 && fn.params[0] === "&signer") continue;
|
|
150
|
+
const method = this.generateMethod(fn);
|
|
151
|
+
lines.push(method);
|
|
152
|
+
lines.push("");
|
|
153
|
+
}
|
|
154
|
+
lines.push("}");
|
|
155
|
+
return lines.join("\n");
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Generates a method from a function ABI
|
|
159
|
+
* @param fn - Function ABI
|
|
160
|
+
* @returns Method code string
|
|
161
|
+
*/
|
|
162
|
+
generateMethod(fn) {
|
|
163
|
+
const lines = [];
|
|
164
|
+
const methodName = this.toCamelCase(fn.name);
|
|
165
|
+
const params = fn.params.filter((p) => p !== "&signer" && p !== "signer");
|
|
166
|
+
const paramNames = params.map((_, i) => `arg${i}`);
|
|
167
|
+
const paramTypes = params.map((p) => this.parser.parseType(p));
|
|
168
|
+
let genericParams = "";
|
|
169
|
+
if (fn.genericTypeParams.length > 0) {
|
|
170
|
+
const typeParams = fn.genericTypeParams.map((_, i) => `T${i} = unknown`);
|
|
171
|
+
genericParams = `<${typeParams.join(", ")}>`;
|
|
172
|
+
}
|
|
173
|
+
const paramList = paramNames.map((name, i) => `${name}: ${paramTypes[i]}`).join(", ");
|
|
174
|
+
const hasTypeArgs = fn.genericTypeParams.length > 0;
|
|
175
|
+
const typeArgsParam = hasTypeArgs ? ", typeArgs: string[]" : "";
|
|
176
|
+
let returnType;
|
|
177
|
+
if (fn.isView) {
|
|
178
|
+
if (fn.return.length === 0) {
|
|
179
|
+
returnType = "void";
|
|
180
|
+
} else if (fn.return.length === 1) {
|
|
181
|
+
returnType = this.parser.parseType(fn.return[0] ?? "unknown");
|
|
182
|
+
} else {
|
|
183
|
+
returnType = `[${fn.return.map((r) => this.parser.parseType(r ?? "unknown")).join(", ")}]`;
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
returnType = "string";
|
|
187
|
+
}
|
|
188
|
+
lines.push(" /**");
|
|
189
|
+
lines.push(` * ${fn.isView ? "View" : "Entry"} function: ${fn.name}`);
|
|
190
|
+
paramNames.forEach((name, i) => {
|
|
191
|
+
lines.push(` * @param ${name} - Parameter of type ${params[i]}`);
|
|
192
|
+
});
|
|
193
|
+
if (hasTypeArgs) {
|
|
194
|
+
lines.push(" * @param typeArgs - Type arguments");
|
|
195
|
+
}
|
|
196
|
+
lines.push(` * @returns ${fn.isView ? "Function result" : "Transaction hash"}`);
|
|
197
|
+
lines.push(" */");
|
|
198
|
+
const asyncKeyword = "async";
|
|
199
|
+
lines.push(
|
|
200
|
+
` ${asyncKeyword} ${methodName}${genericParams}(${paramList}${typeArgsParam}): Promise<${returnType}> {`
|
|
201
|
+
);
|
|
202
|
+
const argsArray = paramNames.length > 0 ? `[${paramNames.join(", ")}]` : "[]";
|
|
203
|
+
const typeArgsArray = hasTypeArgs ? "typeArgs" : "[]";
|
|
204
|
+
if (fn.isView) {
|
|
205
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
206
|
+
lines.push(` address: this.address,`);
|
|
207
|
+
lines.push(` module: this.module,`);
|
|
208
|
+
lines.push(` });`);
|
|
209
|
+
lines.push(` return contract.view<${returnType}>('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
210
|
+
} else {
|
|
211
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
212
|
+
lines.push(` address: this.address,`);
|
|
213
|
+
lines.push(` module: this.module,`);
|
|
214
|
+
lines.push(` });`);
|
|
215
|
+
lines.push(` return contract.call('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
216
|
+
}
|
|
217
|
+
lines.push(" }");
|
|
218
|
+
return lines.join("\n");
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Converts a snake_case string to PascalCase
|
|
222
|
+
*/
|
|
223
|
+
toPascalCase(str) {
|
|
224
|
+
return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Converts a snake_case string to camelCase
|
|
228
|
+
*/
|
|
229
|
+
toCamelCase(str) {
|
|
230
|
+
const pascal = this.toPascalCase(str);
|
|
231
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/cli.ts
|
|
236
|
+
var program = new import_commander.Command();
|
|
237
|
+
program.name("movebridge-gen").description("Generate TypeScript bindings from Move modules").version("0.0.1").requiredOption("-a, --address <address>", "Module address (e.g., 0x1::coin)").requiredOption("-n, --network <network>", "Network (mainnet or testnet)").requiredOption("-o, --output <path>", "Output file path").action(async (options) => {
|
|
238
|
+
const { address, network, output } = options;
|
|
239
|
+
if (network !== "mainnet" && network !== "testnet") {
|
|
240
|
+
console.error('Error: Network must be "mainnet" or "testnet"');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
console.log(`Fetching ABI for ${address} on ${network}...`);
|
|
245
|
+
const parser = new ABIParser(network);
|
|
246
|
+
const abi = await parser.fetchABI(address);
|
|
247
|
+
console.log(`Found module: ${abi.name} with ${abi.exposedFunctions.length} functions`);
|
|
248
|
+
const generator = new TypeGenerator(parser);
|
|
249
|
+
const code = generator.generateClass(abi);
|
|
250
|
+
const outputDir = (0, import_path.dirname)(output);
|
|
251
|
+
await (0, import_promises.mkdir)(outputDir, { recursive: true });
|
|
252
|
+
await (0, import_promises.writeFile)(output, code, "utf-8");
|
|
253
|
+
console.log(`Generated: ${output}`);
|
|
254
|
+
console.log("Done!");
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
program.parse();
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ABIParser,
|
|
4
|
+
TypeGenerator
|
|
5
|
+
} from "./chunk-NMH4XS2A.mjs";
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
10
|
+
import { dirname } from "path";
|
|
11
|
+
var program = new Command();
|
|
12
|
+
program.name("movebridge-gen").description("Generate TypeScript bindings from Move modules").version("0.0.1").requiredOption("-a, --address <address>", "Module address (e.g., 0x1::coin)").requiredOption("-n, --network <network>", "Network (mainnet or testnet)").requiredOption("-o, --output <path>", "Output file path").action(async (options) => {
|
|
13
|
+
const { address, network, output } = options;
|
|
14
|
+
if (network !== "mainnet" && network !== "testnet") {
|
|
15
|
+
console.error('Error: Network must be "mainnet" or "testnet"');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
console.log(`Fetching ABI for ${address} on ${network}...`);
|
|
20
|
+
const parser = new ABIParser(network);
|
|
21
|
+
const abi = await parser.fetchABI(address);
|
|
22
|
+
console.log(`Found module: ${abi.name} with ${abi.exposedFunctions.length} functions`);
|
|
23
|
+
const generator = new TypeGenerator(parser);
|
|
24
|
+
const code = generator.generateClass(abi);
|
|
25
|
+
const outputDir = dirname(output);
|
|
26
|
+
await mkdir(outputDir, { recursive: true });
|
|
27
|
+
await writeFile(output, code, "utf-8");
|
|
28
|
+
console.log(`Generated: ${output}`);
|
|
29
|
+
console.log("Done!");
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
program.parse();
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { NetworkType } from '@movebridge/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @movebridge/codegen - ABI Parser
|
|
5
|
+
* Fetches and parses Move module ABIs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type parameter definition
|
|
10
|
+
*/
|
|
11
|
+
interface TypeParam {
|
|
12
|
+
constraints: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Function ABI definition
|
|
16
|
+
*/
|
|
17
|
+
interface FunctionABI {
|
|
18
|
+
name: string;
|
|
19
|
+
visibility: 'public' | 'private' | 'friend';
|
|
20
|
+
isEntry: boolean;
|
|
21
|
+
isView: boolean;
|
|
22
|
+
genericTypeParams: TypeParam[];
|
|
23
|
+
params: string[];
|
|
24
|
+
return: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Module ABI definition
|
|
28
|
+
*/
|
|
29
|
+
interface ModuleABI {
|
|
30
|
+
address: string;
|
|
31
|
+
name: string;
|
|
32
|
+
exposedFunctions: FunctionABI[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* ABI Parser
|
|
36
|
+
* Fetches and parses Move module ABIs from the network
|
|
37
|
+
*/
|
|
38
|
+
declare class ABIParser {
|
|
39
|
+
private aptosClient;
|
|
40
|
+
constructor(network: NetworkType);
|
|
41
|
+
/**
|
|
42
|
+
* Fetches the ABI for a module
|
|
43
|
+
* @param moduleAddress - Full module address (e.g., '0x1::coin')
|
|
44
|
+
* @returns Module ABI
|
|
45
|
+
*/
|
|
46
|
+
fetchABI(moduleAddress: string): Promise<ModuleABI>;
|
|
47
|
+
/**
|
|
48
|
+
* Parses a Move type to TypeScript type
|
|
49
|
+
* @param moveType - Move type string
|
|
50
|
+
* @returns TypeScript type string
|
|
51
|
+
*/
|
|
52
|
+
parseType(moveType: string): string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @movebridge/codegen - Type Generator
|
|
57
|
+
* Generates TypeScript code from Move module ABIs
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Type Generator
|
|
62
|
+
* Generates TypeScript classes from Move module ABIs
|
|
63
|
+
*/
|
|
64
|
+
declare class TypeGenerator {
|
|
65
|
+
private parser;
|
|
66
|
+
constructor(parser: ABIParser);
|
|
67
|
+
/**
|
|
68
|
+
* Generates a TypeScript class from a module ABI
|
|
69
|
+
* @param abi - Module ABI
|
|
70
|
+
* @returns TypeScript code string
|
|
71
|
+
*/
|
|
72
|
+
generateClass(abi: ModuleABI): string;
|
|
73
|
+
/**
|
|
74
|
+
* Generates a method from a function ABI
|
|
75
|
+
* @param fn - Function ABI
|
|
76
|
+
* @returns Method code string
|
|
77
|
+
*/
|
|
78
|
+
private generateMethod;
|
|
79
|
+
/**
|
|
80
|
+
* Converts a snake_case string to PascalCase
|
|
81
|
+
*/
|
|
82
|
+
private toPascalCase;
|
|
83
|
+
/**
|
|
84
|
+
* Converts a snake_case string to camelCase
|
|
85
|
+
*/
|
|
86
|
+
private toCamelCase;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { ABIParser, type FunctionABI, type ModuleABI, TypeGenerator, type TypeParam };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { NetworkType } from '@movebridge/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @movebridge/codegen - ABI Parser
|
|
5
|
+
* Fetches and parses Move module ABIs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type parameter definition
|
|
10
|
+
*/
|
|
11
|
+
interface TypeParam {
|
|
12
|
+
constraints: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Function ABI definition
|
|
16
|
+
*/
|
|
17
|
+
interface FunctionABI {
|
|
18
|
+
name: string;
|
|
19
|
+
visibility: 'public' | 'private' | 'friend';
|
|
20
|
+
isEntry: boolean;
|
|
21
|
+
isView: boolean;
|
|
22
|
+
genericTypeParams: TypeParam[];
|
|
23
|
+
params: string[];
|
|
24
|
+
return: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Module ABI definition
|
|
28
|
+
*/
|
|
29
|
+
interface ModuleABI {
|
|
30
|
+
address: string;
|
|
31
|
+
name: string;
|
|
32
|
+
exposedFunctions: FunctionABI[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* ABI Parser
|
|
36
|
+
* Fetches and parses Move module ABIs from the network
|
|
37
|
+
*/
|
|
38
|
+
declare class ABIParser {
|
|
39
|
+
private aptosClient;
|
|
40
|
+
constructor(network: NetworkType);
|
|
41
|
+
/**
|
|
42
|
+
* Fetches the ABI for a module
|
|
43
|
+
* @param moduleAddress - Full module address (e.g., '0x1::coin')
|
|
44
|
+
* @returns Module ABI
|
|
45
|
+
*/
|
|
46
|
+
fetchABI(moduleAddress: string): Promise<ModuleABI>;
|
|
47
|
+
/**
|
|
48
|
+
* Parses a Move type to TypeScript type
|
|
49
|
+
* @param moveType - Move type string
|
|
50
|
+
* @returns TypeScript type string
|
|
51
|
+
*/
|
|
52
|
+
parseType(moveType: string): string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @movebridge/codegen - Type Generator
|
|
57
|
+
* Generates TypeScript code from Move module ABIs
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Type Generator
|
|
62
|
+
* Generates TypeScript classes from Move module ABIs
|
|
63
|
+
*/
|
|
64
|
+
declare class TypeGenerator {
|
|
65
|
+
private parser;
|
|
66
|
+
constructor(parser: ABIParser);
|
|
67
|
+
/**
|
|
68
|
+
* Generates a TypeScript class from a module ABI
|
|
69
|
+
* @param abi - Module ABI
|
|
70
|
+
* @returns TypeScript code string
|
|
71
|
+
*/
|
|
72
|
+
generateClass(abi: ModuleABI): string;
|
|
73
|
+
/**
|
|
74
|
+
* Generates a method from a function ABI
|
|
75
|
+
* @param fn - Function ABI
|
|
76
|
+
* @returns Method code string
|
|
77
|
+
*/
|
|
78
|
+
private generateMethod;
|
|
79
|
+
/**
|
|
80
|
+
* Converts a snake_case string to PascalCase
|
|
81
|
+
*/
|
|
82
|
+
private toPascalCase;
|
|
83
|
+
/**
|
|
84
|
+
* Converts a snake_case string to camelCase
|
|
85
|
+
*/
|
|
86
|
+
private toCamelCase;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { ABIParser, type FunctionABI, type ModuleABI, TypeGenerator, type TypeParam };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ABIParser: () => ABIParser,
|
|
24
|
+
TypeGenerator: () => TypeGenerator
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/parser.ts
|
|
29
|
+
var import_ts_sdk = require("@aptos-labs/ts-sdk");
|
|
30
|
+
var import_core = require("@movebridge/core");
|
|
31
|
+
var ABIParser = class {
|
|
32
|
+
aptosClient;
|
|
33
|
+
constructor(network) {
|
|
34
|
+
const config = import_core.NETWORK_CONFIG[network];
|
|
35
|
+
const aptosConfig = new import_ts_sdk.AptosConfig({
|
|
36
|
+
network: import_ts_sdk.Network.CUSTOM,
|
|
37
|
+
fullnode: config.rpcUrl
|
|
38
|
+
});
|
|
39
|
+
this.aptosClient = new import_ts_sdk.Aptos(aptosConfig);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fetches the ABI for a module
|
|
43
|
+
* @param moduleAddress - Full module address (e.g., '0x1::coin')
|
|
44
|
+
* @returns Module ABI
|
|
45
|
+
*/
|
|
46
|
+
async fetchABI(moduleAddress) {
|
|
47
|
+
const parts = moduleAddress.split("::");
|
|
48
|
+
if (parts.length !== 2) {
|
|
49
|
+
throw import_core.Errors.invalidArgument(
|
|
50
|
+
"moduleAddress",
|
|
51
|
+
"Expected format: 0xADDRESS::module_name"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const address = parts[0];
|
|
55
|
+
const moduleName = parts[1];
|
|
56
|
+
if (!address || !moduleName) {
|
|
57
|
+
throw import_core.Errors.invalidArgument(
|
|
58
|
+
"moduleAddress",
|
|
59
|
+
"Expected format: 0xADDRESS::module_name"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const client = this.aptosClient;
|
|
64
|
+
const modules = await client.getAccountModules({
|
|
65
|
+
accountAddress: address
|
|
66
|
+
});
|
|
67
|
+
const targetModule = modules.find((m) => {
|
|
68
|
+
const abi2 = m.abi;
|
|
69
|
+
return abi2?.name === moduleName;
|
|
70
|
+
});
|
|
71
|
+
if (!targetModule || !targetModule.abi) {
|
|
72
|
+
throw import_core.Errors.abiFetchFailed(moduleAddress, "Module not found");
|
|
73
|
+
}
|
|
74
|
+
const abi = targetModule.abi;
|
|
75
|
+
const exposedFunctions = abi.exposed_functions.map((fn) => ({
|
|
76
|
+
name: fn.name,
|
|
77
|
+
visibility: fn.visibility,
|
|
78
|
+
isEntry: fn.is_entry,
|
|
79
|
+
isView: fn.is_view,
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
+
genericTypeParams: fn.generic_type_params.map((p) => ({
|
|
82
|
+
constraints: p.constraints
|
|
83
|
+
})),
|
|
84
|
+
params: fn.params,
|
|
85
|
+
return: fn.return
|
|
86
|
+
}));
|
|
87
|
+
return {
|
|
88
|
+
address,
|
|
89
|
+
name: moduleName,
|
|
90
|
+
exposedFunctions
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error instanceof Error && error.message.includes("Module not found")) {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
throw import_core.Errors.abiFetchFailed(moduleAddress, "network", error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Parses a Move type to TypeScript type
|
|
101
|
+
* @param moveType - Move type string
|
|
102
|
+
* @returns TypeScript type string
|
|
103
|
+
*/
|
|
104
|
+
parseType(moveType) {
|
|
105
|
+
const typeMap = {
|
|
106
|
+
"bool": "boolean",
|
|
107
|
+
"u8": "number",
|
|
108
|
+
"u16": "number",
|
|
109
|
+
"u32": "number",
|
|
110
|
+
"u64": "string",
|
|
111
|
+
"u128": "string",
|
|
112
|
+
"u256": "string",
|
|
113
|
+
"address": "string",
|
|
114
|
+
"&signer": "void",
|
|
115
|
+
"signer": "void",
|
|
116
|
+
"0x1::string::String": "string"
|
|
117
|
+
};
|
|
118
|
+
if (typeMap[moveType]) {
|
|
119
|
+
return typeMap[moveType];
|
|
120
|
+
}
|
|
121
|
+
if (moveType.startsWith("vector<")) {
|
|
122
|
+
const innerType = moveType.slice(7, -1);
|
|
123
|
+
return `${this.parseType(innerType)}[]`;
|
|
124
|
+
}
|
|
125
|
+
if (/^T\d+$/.test(moveType)) {
|
|
126
|
+
return moveType;
|
|
127
|
+
}
|
|
128
|
+
return "unknown";
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/generator.ts
|
|
133
|
+
var TypeGenerator = class {
|
|
134
|
+
parser;
|
|
135
|
+
constructor(parser) {
|
|
136
|
+
this.parser = parser;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generates a TypeScript class from a module ABI
|
|
140
|
+
* @param abi - Module ABI
|
|
141
|
+
* @returns TypeScript code string
|
|
142
|
+
*/
|
|
143
|
+
generateClass(abi) {
|
|
144
|
+
const className = this.toPascalCase(abi.name) + "Contract";
|
|
145
|
+
const lines = [];
|
|
146
|
+
lines.push("/**");
|
|
147
|
+
lines.push(` * Auto-generated TypeScript bindings for ${abi.address}::${abi.name}`);
|
|
148
|
+
lines.push(" * Generated by @movebridge/codegen");
|
|
149
|
+
lines.push(" */");
|
|
150
|
+
lines.push("");
|
|
151
|
+
lines.push("import type { Movement } from '@movebridge/core';");
|
|
152
|
+
lines.push("");
|
|
153
|
+
lines.push(`export class ${className} {`);
|
|
154
|
+
lines.push(` private readonly movement: Movement;`);
|
|
155
|
+
lines.push(` private readonly address = '${abi.address}';`);
|
|
156
|
+
lines.push(` private readonly module = '${abi.name}';`);
|
|
157
|
+
lines.push("");
|
|
158
|
+
lines.push(" /**");
|
|
159
|
+
lines.push(` * Creates a new ${className} instance`);
|
|
160
|
+
lines.push(" * @param movement - Movement SDK instance");
|
|
161
|
+
lines.push(" */");
|
|
162
|
+
lines.push(" constructor(movement: Movement) {");
|
|
163
|
+
lines.push(" this.movement = movement;");
|
|
164
|
+
lines.push(" }");
|
|
165
|
+
lines.push("");
|
|
166
|
+
for (const fn of abi.exposedFunctions) {
|
|
167
|
+
if (fn.visibility === "private") continue;
|
|
168
|
+
if (fn.params.length === 1 && fn.params[0] === "&signer") continue;
|
|
169
|
+
const method = this.generateMethod(fn);
|
|
170
|
+
lines.push(method);
|
|
171
|
+
lines.push("");
|
|
172
|
+
}
|
|
173
|
+
lines.push("}");
|
|
174
|
+
return lines.join("\n");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generates a method from a function ABI
|
|
178
|
+
* @param fn - Function ABI
|
|
179
|
+
* @returns Method code string
|
|
180
|
+
*/
|
|
181
|
+
generateMethod(fn) {
|
|
182
|
+
const lines = [];
|
|
183
|
+
const methodName = this.toCamelCase(fn.name);
|
|
184
|
+
const params = fn.params.filter((p) => p !== "&signer" && p !== "signer");
|
|
185
|
+
const paramNames = params.map((_, i) => `arg${i}`);
|
|
186
|
+
const paramTypes = params.map((p) => this.parser.parseType(p));
|
|
187
|
+
let genericParams = "";
|
|
188
|
+
if (fn.genericTypeParams.length > 0) {
|
|
189
|
+
const typeParams = fn.genericTypeParams.map((_, i) => `T${i} = unknown`);
|
|
190
|
+
genericParams = `<${typeParams.join(", ")}>`;
|
|
191
|
+
}
|
|
192
|
+
const paramList = paramNames.map((name, i) => `${name}: ${paramTypes[i]}`).join(", ");
|
|
193
|
+
const hasTypeArgs = fn.genericTypeParams.length > 0;
|
|
194
|
+
const typeArgsParam = hasTypeArgs ? ", typeArgs: string[]" : "";
|
|
195
|
+
let returnType;
|
|
196
|
+
if (fn.isView) {
|
|
197
|
+
if (fn.return.length === 0) {
|
|
198
|
+
returnType = "void";
|
|
199
|
+
} else if (fn.return.length === 1) {
|
|
200
|
+
returnType = this.parser.parseType(fn.return[0] ?? "unknown");
|
|
201
|
+
} else {
|
|
202
|
+
returnType = `[${fn.return.map((r) => this.parser.parseType(r ?? "unknown")).join(", ")}]`;
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
returnType = "string";
|
|
206
|
+
}
|
|
207
|
+
lines.push(" /**");
|
|
208
|
+
lines.push(` * ${fn.isView ? "View" : "Entry"} function: ${fn.name}`);
|
|
209
|
+
paramNames.forEach((name, i) => {
|
|
210
|
+
lines.push(` * @param ${name} - Parameter of type ${params[i]}`);
|
|
211
|
+
});
|
|
212
|
+
if (hasTypeArgs) {
|
|
213
|
+
lines.push(" * @param typeArgs - Type arguments");
|
|
214
|
+
}
|
|
215
|
+
lines.push(` * @returns ${fn.isView ? "Function result" : "Transaction hash"}`);
|
|
216
|
+
lines.push(" */");
|
|
217
|
+
const asyncKeyword = "async";
|
|
218
|
+
lines.push(
|
|
219
|
+
` ${asyncKeyword} ${methodName}${genericParams}(${paramList}${typeArgsParam}): Promise<${returnType}> {`
|
|
220
|
+
);
|
|
221
|
+
const argsArray = paramNames.length > 0 ? `[${paramNames.join(", ")}]` : "[]";
|
|
222
|
+
const typeArgsArray = hasTypeArgs ? "typeArgs" : "[]";
|
|
223
|
+
if (fn.isView) {
|
|
224
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
225
|
+
lines.push(` address: this.address,`);
|
|
226
|
+
lines.push(` module: this.module,`);
|
|
227
|
+
lines.push(` });`);
|
|
228
|
+
lines.push(` return contract.view<${returnType}>('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
229
|
+
} else {
|
|
230
|
+
lines.push(` const contract = this.movement.contract({`);
|
|
231
|
+
lines.push(` address: this.address,`);
|
|
232
|
+
lines.push(` module: this.module,`);
|
|
233
|
+
lines.push(` });`);
|
|
234
|
+
lines.push(` return contract.call('${fn.name}', ${argsArray}, ${typeArgsArray});`);
|
|
235
|
+
}
|
|
236
|
+
lines.push(" }");
|
|
237
|
+
return lines.join("\n");
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Converts a snake_case string to PascalCase
|
|
241
|
+
*/
|
|
242
|
+
toPascalCase(str) {
|
|
243
|
+
return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Converts a snake_case string to camelCase
|
|
247
|
+
*/
|
|
248
|
+
toCamelCase(str) {
|
|
249
|
+
const pascal = this.toPascalCase(str);
|
|
250
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
254
|
+
0 && (module.exports = {
|
|
255
|
+
ABIParser,
|
|
256
|
+
TypeGenerator
|
|
257
|
+
});
|
package/dist/index.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@movebridge/codegen",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Code generation tool for type-safe Movement contract interactions",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"movebridge-gen": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@aptos-labs/ts-sdk": "^1.26.0",
|
|
23
|
+
"commander": "^11.1.0",
|
|
24
|
+
"@movebridge/core": "0.0.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"tsup": "^8.0.1",
|
|
28
|
+
"typescript": "^5.3.2"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"movement",
|
|
32
|
+
"aptos",
|
|
33
|
+
"codegen",
|
|
34
|
+
"typescript",
|
|
35
|
+
"cli"
|
|
36
|
+
],
|
|
37
|
+
"author": "Aqila Rifti",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/AqilaRifti/MoveBridge",
|
|
42
|
+
"directory": "packages/codegen"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/AqilaRifti/MoveBridge#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/AqilaRifti/MoveBridge/issues"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup src/index.ts src/cli.ts --format cjs,esm --dts --clean",
|
|
50
|
+
"dev": "tsup src/index.ts src/cli.ts --format cjs,esm --dts --watch",
|
|
51
|
+
"clean": "rm -rf dist",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest"
|
|
55
|
+
}
|
|
56
|
+
}
|