@xelis/sdk 0.11.32 → 0.11.34
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/cjs/contract/contract.js +126 -84
- package/dist/cjs/contract/typed_contract.js +134 -88
- package/dist/cjs/contract/xvm_serializer.js +252 -29
- package/dist/cjs/rpc/websocket.js +10 -4
- package/dist/esm/contract/contract.js +125 -83
- package/dist/esm/contract/typed_contract.js +131 -85
- package/dist/esm/contract/xvm_serializer.js +244 -27
- package/dist/esm/rpc/websocket.js +10 -4
- package/dist/types/contract/contract.d.ts +46 -10
- package/dist/types/contract/typed_contract.d.ts +50 -15
- package/dist/types/contract/xvm_serializer.d.ts +68 -4
- package/dist/types/daemon/types.d.ts +17 -8
- package/dist/types/wallet/rpc.d.ts +1 -1
- package/dist/types/wallet/types.d.ts +34 -17
- package/dist/types/wallet/websocket.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createContractDeployment = exports.createContractInvocation = exports.createDeposits = exports.
|
|
3
|
+
exports.createContractDeployment = exports.createContractInvocation = exports.createDeposits = exports.createVMParameter = exports.typeRegistry = exports.defineStruct = exports.defineEnum = exports.VMParam = exports.serialize_optional = exports.serialize_map = exports.serialize_array = exports.createVMPrimitive = void 0;
|
|
4
4
|
// Known opaque types that need special wrapping
|
|
5
|
-
const OPAQUE_TYPES = new Set(['Hash', 'Address', 'PublicKey', 'Blob'
|
|
5
|
+
const OPAQUE_TYPES = new Set(['Hash', 'Address', 'PublicKey', 'Blob']);
|
|
6
6
|
// Type validation and conversion helpers
|
|
7
7
|
const TYPE_VALIDATORS = {
|
|
8
8
|
'u256': (v) => {
|
|
@@ -76,12 +76,12 @@ const TYPE_VALIDATORS = {
|
|
|
76
76
|
* @param type - The type string (e.g., 'u64', 'Hash', 'string')
|
|
77
77
|
* @param validate - Whether to validate and convert the value (default: true)
|
|
78
78
|
*/
|
|
79
|
-
function
|
|
80
|
-
let
|
|
79
|
+
function createVMPrimitive(value, type, validate = true) {
|
|
80
|
+
let processed_value = value;
|
|
81
81
|
// Validate and convert value if requested
|
|
82
82
|
if (validate && TYPE_VALIDATORS[type]) {
|
|
83
83
|
try {
|
|
84
|
-
|
|
84
|
+
processed_value = TYPE_VALIDATORS[type](value);
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
87
87
|
throw new Error(`Failed to create VM parameter for type ${type}: ${error}`);
|
|
@@ -90,64 +90,287 @@ function createVMParameter(value, type, validate = true) {
|
|
|
90
90
|
// Handle opaque types (Hash, Address, PublicKey)
|
|
91
91
|
if (OPAQUE_TYPES.has(type)) {
|
|
92
92
|
return {
|
|
93
|
-
type: "
|
|
93
|
+
type: "primitive",
|
|
94
94
|
value: {
|
|
95
95
|
type: "opaque",
|
|
96
96
|
value: {
|
|
97
97
|
type: type,
|
|
98
|
-
value:
|
|
98
|
+
value: processed_value
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
// Handle regular types
|
|
104
104
|
return {
|
|
105
|
-
type: "
|
|
105
|
+
type: "primitive",
|
|
106
106
|
value: {
|
|
107
107
|
type: type,
|
|
108
|
-
value:
|
|
108
|
+
value: processed_value
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
-
exports.
|
|
112
|
+
exports.createVMPrimitive = createVMPrimitive;
|
|
113
|
+
/**
|
|
114
|
+
* Serialize an array of values
|
|
115
|
+
*/
|
|
116
|
+
function serialize_array(items, item_type) {
|
|
117
|
+
const serialized_items = items.map(item => createVMParameter(item, item_type));
|
|
118
|
+
return {
|
|
119
|
+
type: "array",
|
|
120
|
+
value: serialized_items
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
exports.serialize_array = serialize_array;
|
|
124
|
+
/**
|
|
125
|
+
* Serialize a map
|
|
126
|
+
*/
|
|
127
|
+
function serialize_map(map, keyType, valueType) {
|
|
128
|
+
const entries = [];
|
|
129
|
+
for (const [key, value] of Object.entries(map)) {
|
|
130
|
+
const serialized_key = createVMParameter(key, keyType);
|
|
131
|
+
const serialized_value = createVMParameter(value, valueType);
|
|
132
|
+
entries.push([serialized_key, serialized_value]);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
type: "map",
|
|
136
|
+
value: entries
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
exports.serialize_map = serialize_map;
|
|
140
|
+
/**
|
|
141
|
+
* Serialize an optional value
|
|
142
|
+
*/
|
|
143
|
+
function serialize_optional(value, inner_type) {
|
|
144
|
+
if (value === null || value === undefined) {
|
|
145
|
+
return {
|
|
146
|
+
type: "option",
|
|
147
|
+
value: null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
type: "option",
|
|
152
|
+
value: createVMParameter(value, inner_type)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
exports.serialize_optional = serialize_optional;
|
|
113
156
|
/**
|
|
114
157
|
* Convenience functions for common types
|
|
115
158
|
*/
|
|
116
|
-
exports.
|
|
117
|
-
hash: (value) =>
|
|
118
|
-
address: (value) =>
|
|
119
|
-
|
|
120
|
-
blob: (value) =>
|
|
121
|
-
u64: (value) =>
|
|
122
|
-
u32: (value) =>
|
|
123
|
-
u16: (value) =>
|
|
124
|
-
u8: (value) =>
|
|
125
|
-
string: (value) =>
|
|
126
|
-
boolean: (value) =>
|
|
159
|
+
exports.VMParam = {
|
|
160
|
+
hash: (value) => createVMPrimitive(value, 'Hash'),
|
|
161
|
+
address: (value) => createVMPrimitive(value, 'Address'),
|
|
162
|
+
public_key: (value) => createVMPrimitive(value, 'PublicKey'),
|
|
163
|
+
blob: (value) => createVMPrimitive(value, 'Blob'),
|
|
164
|
+
u64: (value) => createVMPrimitive(value, 'u64'),
|
|
165
|
+
u32: (value) => createVMPrimitive(value, 'u32'),
|
|
166
|
+
u16: (value) => createVMPrimitive(value, 'u16'),
|
|
167
|
+
u8: (value) => createVMPrimitive(value, 'u8'),
|
|
168
|
+
string: (value) => createVMPrimitive(value, 'string'),
|
|
169
|
+
boolean: (value) => createVMPrimitive(value, 'boolean'),
|
|
127
170
|
};
|
|
171
|
+
/**
|
|
172
|
+
* Define an enum type from ABI schema
|
|
173
|
+
*/
|
|
174
|
+
function defineEnum(name, variants) {
|
|
175
|
+
const variantNames = variants.map(v => v.name);
|
|
176
|
+
return {
|
|
177
|
+
name,
|
|
178
|
+
// NEW: mark kind & expose a matcher
|
|
179
|
+
// (safe to add; SerializableType is duck-typed)
|
|
180
|
+
// @ts-ignore - widen at runtime
|
|
181
|
+
kind: 'enum',
|
|
182
|
+
// @ts-ignore
|
|
183
|
+
hasVariant: (vn) => variantNames.includes(vn),
|
|
184
|
+
to_VMParameter(value) {
|
|
185
|
+
const variant_index = variants.findIndex(v => v.name === value.type);
|
|
186
|
+
if (variant_index === -1) {
|
|
187
|
+
throw new Error(`Unknown variant '${value.type}' for enum '${name}'`);
|
|
188
|
+
}
|
|
189
|
+
const variant = variants[variant_index];
|
|
190
|
+
const expected_fields = new Set(variant.fields.map(f => f.name));
|
|
191
|
+
expected_fields.add('type');
|
|
192
|
+
for (const key of Object.keys(value)) {
|
|
193
|
+
if (!expected_fields.has(key)) {
|
|
194
|
+
throw new Error(`Unknown field '${key}' for variant '${value.type}' of enum '${name}'. ` +
|
|
195
|
+
`Expected fields: ${Array.from(expected_fields).filter(f => f !== 'type').join(', ')}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const params = [
|
|
199
|
+
exports.VMParam.u8(variant_index)
|
|
200
|
+
];
|
|
201
|
+
for (const field_schema of variant.fields) {
|
|
202
|
+
const field_value = value[field_schema.name];
|
|
203
|
+
if (field_value === undefined) {
|
|
204
|
+
throw new Error(`Missing field '${field_schema.name}' for variant '${value.type}' of enum '${name}'`);
|
|
205
|
+
}
|
|
206
|
+
params.push(createVMParameter(field_value, field_schema.type));
|
|
207
|
+
}
|
|
208
|
+
return { type: "object", value: params };
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
exports.defineEnum = defineEnum;
|
|
213
|
+
/**
|
|
214
|
+
* Define a struct type from ABI schema
|
|
215
|
+
*/
|
|
216
|
+
function defineStruct(name, fields) {
|
|
217
|
+
return {
|
|
218
|
+
name,
|
|
219
|
+
// NEW: mark kind for structs
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
kind: 'struct',
|
|
222
|
+
to_VMParameter(value) {
|
|
223
|
+
const expected_fields = new Set(fields.map(f => f.name));
|
|
224
|
+
for (const key of Object.keys(value)) {
|
|
225
|
+
if (!expected_fields.has(key)) {
|
|
226
|
+
throw new Error(`Unknown field '${key}' for struct '${name}'. ` +
|
|
227
|
+
`Expected fields: ${Array.from(expected_fields).join(', ')}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const params = [];
|
|
231
|
+
for (const field_schema of fields) {
|
|
232
|
+
const field_value = value[field_schema.name];
|
|
233
|
+
if (field_value === undefined) {
|
|
234
|
+
throw new Error(`Missing field '${field_schema.name}' for struct '${name}'`);
|
|
235
|
+
}
|
|
236
|
+
params.push(createVMParameter(field_value, field_schema.type));
|
|
237
|
+
}
|
|
238
|
+
return { type: "object", value: params };
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
exports.defineStruct = defineStruct;
|
|
243
|
+
/**
|
|
244
|
+
* Type registry - simple Map for custom types
|
|
245
|
+
*/
|
|
246
|
+
class TypeRegistry {
|
|
247
|
+
constructor() {
|
|
248
|
+
this.types = new Map();
|
|
249
|
+
}
|
|
250
|
+
register(definition) {
|
|
251
|
+
this.types.set(definition.name, definition);
|
|
252
|
+
return definition;
|
|
253
|
+
}
|
|
254
|
+
get(name) {
|
|
255
|
+
return this.types.get(name);
|
|
256
|
+
}
|
|
257
|
+
has(name) {
|
|
258
|
+
return this.types.has(name);
|
|
259
|
+
}
|
|
260
|
+
clear() {
|
|
261
|
+
this.types.clear();
|
|
262
|
+
}
|
|
263
|
+
all() {
|
|
264
|
+
return this.types.values();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.typeRegistry = new TypeRegistry();
|
|
268
|
+
/**
|
|
269
|
+
* Enhanced parameter creation that handles both primitive and custom types
|
|
270
|
+
* @param value - The value to serialize
|
|
271
|
+
* @param type - The type string (primitive or custom type name)
|
|
272
|
+
* @param validate - Whether to validate primitive values (default: true)
|
|
273
|
+
*/
|
|
274
|
+
function createVMParameter(value, type, validate = true) {
|
|
275
|
+
// Pass-through already-serialized parameters
|
|
276
|
+
if (value && typeof value === 'object' && 'type' in value && 'value' in value) {
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
// Arrays
|
|
280
|
+
if (type.endsWith('[]')) {
|
|
281
|
+
const inner_type = type.slice(0, -2);
|
|
282
|
+
if (!Array.isArray(value)) {
|
|
283
|
+
throw new Error(`Expected array for type ${type}, got ${typeof value}`);
|
|
284
|
+
}
|
|
285
|
+
return serialize_array(value, inner_type);
|
|
286
|
+
}
|
|
287
|
+
// Optionals
|
|
288
|
+
if (type.startsWith('optional<') && type.endsWith('>')) {
|
|
289
|
+
const inner_type = type.slice(9, -1);
|
|
290
|
+
return serialize_optional(value, inner_type);
|
|
291
|
+
}
|
|
292
|
+
// Maps
|
|
293
|
+
const map_match = type.match(/^map<(.+),\s*(.+)>$/);
|
|
294
|
+
if (map_match) {
|
|
295
|
+
const [, keyType, valueType] = map_match;
|
|
296
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
297
|
+
throw new Error(`Expected object for type ${type}, got ${typeof value}`);
|
|
298
|
+
}
|
|
299
|
+
return serialize_map(value, keyType.trim(), valueType.trim());
|
|
300
|
+
}
|
|
301
|
+
// Generic enum/struct notations: enum<Foo>, struct<Bar>
|
|
302
|
+
const enumGeneric = type.match(/^enum<\s*([^>]+)\s*>$/i);
|
|
303
|
+
if (enumGeneric) {
|
|
304
|
+
const realType = enumGeneric[1].trim();
|
|
305
|
+
const custom = exports.typeRegistry.get(realType);
|
|
306
|
+
if (!custom)
|
|
307
|
+
throw new Error(`Unregistered enum type: ${realType}`);
|
|
308
|
+
return custom.to_VMParameter(value);
|
|
309
|
+
}
|
|
310
|
+
const structGeneric = type.match(/^struct<\s*([^>]+)\s*>$/i);
|
|
311
|
+
if (structGeneric) {
|
|
312
|
+
const realType = structGeneric[1].trim();
|
|
313
|
+
const custom = exports.typeRegistry.get(realType);
|
|
314
|
+
if (!custom)
|
|
315
|
+
throw new Error(`Unregistered struct type: ${realType}`);
|
|
316
|
+
return custom.to_VMParameter(value);
|
|
317
|
+
}
|
|
318
|
+
// Preferred: named custom type
|
|
319
|
+
const custom_type = exports.typeRegistry.get(type);
|
|
320
|
+
if (custom_type) {
|
|
321
|
+
return custom_type.to_VMParameter(value);
|
|
322
|
+
}
|
|
323
|
+
// **Fix for ABI that says literally "enum"/"struct"**
|
|
324
|
+
if (type === 'enum' && value && typeof value === 'object' && 'type' in value) {
|
|
325
|
+
const variantName = value.type;
|
|
326
|
+
for (const t of exports.typeRegistry.all()) {
|
|
327
|
+
const anyT = t;
|
|
328
|
+
if (anyT?.kind === 'enum' && typeof anyT.hasVariant === 'function' && anyT.hasVariant(variantName)) {
|
|
329
|
+
return t.to_VMParameter(value);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
throw new Error(`Cannot resolve enum type for variant '${variantName}'. Is it registered?`);
|
|
333
|
+
}
|
|
334
|
+
if (type === 'struct') {
|
|
335
|
+
// Best-effort: if your ABI truly says "struct" w/o a name, you’ll need a hint.
|
|
336
|
+
// You can add your own resolution heuristic here if you have one.
|
|
337
|
+
throw new Error(`Unknown struct subtype; ABI must specify struct<Foo> or a named type`);
|
|
338
|
+
}
|
|
339
|
+
// Primitive fallback
|
|
340
|
+
if (type in TYPE_VALIDATORS) {
|
|
341
|
+
return createVMPrimitive(value, type, validate);
|
|
342
|
+
}
|
|
343
|
+
console.error("[VMParam] unknown type", type, { value });
|
|
344
|
+
throw new Error(`Unknown type: ${type}`);
|
|
345
|
+
}
|
|
346
|
+
exports.createVMParameter = createVMParameter;
|
|
347
|
+
// ============================================================================
|
|
348
|
+
// Contract Invocation Helpers
|
|
349
|
+
// ============================================================================
|
|
128
350
|
/**
|
|
129
351
|
* Creates a deposits object for contract calls
|
|
130
352
|
* @param deposits - Object mapping token hashes to amounts
|
|
131
353
|
*/
|
|
132
354
|
function createDeposits(deposits) {
|
|
133
355
|
const result = {};
|
|
134
|
-
for (const [
|
|
356
|
+
for (const [token_hash, amount] of Object.entries(deposits)) {
|
|
135
357
|
// Validate hash format
|
|
136
|
-
if (!/^[0-9a-fA-F]{64}$/.test(
|
|
137
|
-
throw new Error(`Invalid token hash format: ${
|
|
358
|
+
if (!/^[0-9a-fA-F]{64}$/.test(token_hash)) {
|
|
359
|
+
throw new Error(`Invalid token hash format: ${token_hash}`);
|
|
138
360
|
}
|
|
139
|
-
result[
|
|
361
|
+
result[token_hash] = { amount };
|
|
140
362
|
}
|
|
141
363
|
return result;
|
|
142
364
|
}
|
|
143
365
|
exports.createDeposits = createDeposits;
|
|
144
366
|
function createContractInvocation(params) {
|
|
145
|
-
const { contract,
|
|
367
|
+
const { contract, chunk_id, parameters = [], deposits, permission, maxGas = 50000000 } = params;
|
|
146
368
|
const result = {
|
|
147
369
|
invoke_contract: {
|
|
148
370
|
contract,
|
|
149
371
|
max_gas: maxGas,
|
|
150
|
-
|
|
372
|
+
entry_id: chunk_id,
|
|
373
|
+
permission,
|
|
151
374
|
parameters
|
|
152
375
|
}
|
|
153
376
|
};
|
|
@@ -158,11 +381,11 @@ function createContractInvocation(params) {
|
|
|
158
381
|
}
|
|
159
382
|
exports.createContractInvocation = createContractInvocation;
|
|
160
383
|
function createContractDeployment(params) {
|
|
161
|
-
const { bytecode, hasConstructor = false, maxGas =
|
|
384
|
+
const { bytecode, hasConstructor = false, maxGas = 50000000 } = params;
|
|
162
385
|
const result = {
|
|
163
386
|
deploy_contract: {
|
|
164
387
|
module: bytecode,
|
|
165
|
-
...(hasConstructor && { invoke: {
|
|
388
|
+
...(hasConstructor && { invoke: { maxGas } })
|
|
166
389
|
}
|
|
167
390
|
};
|
|
168
391
|
return result;
|
|
@@ -40,8 +40,6 @@ class WSRPC {
|
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
42
|
let idRefObject = {};
|
|
43
|
-
await this.dataCall(`subscribe`, { notify: event }, idRefObject)
|
|
44
|
-
.catch(err => listener(null, err));
|
|
45
43
|
const onMessage = (msgEvent) => {
|
|
46
44
|
const eventData = this.events.get(event);
|
|
47
45
|
if (eventData && typeof msgEvent.data === `string`) {
|
|
@@ -64,8 +62,16 @@ class WSRPC {
|
|
|
64
62
|
}
|
|
65
63
|
}
|
|
66
64
|
};
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
try {
|
|
66
|
+
this.socket.addEventListener(`message`, onMessage);
|
|
67
|
+
this.events.set(event, { onMessage, listeners: [listener] });
|
|
68
|
+
await this.dataCall(`subscribe`, { notify: event }, idRefObject);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
this.socket.removeEventListener(`message`, onMessage);
|
|
72
|
+
this.events.delete(event);
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
76
|
};
|
|
71
77
|
// make sure connection is open or wait
|
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
;
|
|
3
|
-
// Convert ABI type to our validator type
|
|
4
|
-
function normalizeType(abiType) {
|
|
5
|
-
const typeMap = {
|
|
6
|
-
'Hash': 'Hash',
|
|
7
|
-
'Address': 'Address',
|
|
8
|
-
'PublicKey': 'PublicKey',
|
|
9
|
-
'Blob': 'Blob',
|
|
10
|
-
'u256': 'u256',
|
|
11
|
-
'u128': 'u128',
|
|
12
|
-
'u64': 'u64',
|
|
13
|
-
'u32': 'u32',
|
|
14
|
-
'u16': 'u16',
|
|
15
|
-
'u8': 'u8',
|
|
16
|
-
'boolean': 'boolean',
|
|
17
|
-
'bool': 'boolean',
|
|
18
|
-
'string': 'string',
|
|
19
|
-
'String': 'string',
|
|
20
|
-
'Boolean': 'boolean',
|
|
21
|
-
'U64': 'u64',
|
|
22
|
-
'U32': 'u32',
|
|
23
|
-
'U16': 'u16',
|
|
24
|
-
'U8': 'u8'
|
|
25
|
-
};
|
|
26
|
-
const normalized = typeMap[abiType];
|
|
27
|
-
if (!normalized) {
|
|
28
|
-
throw new Error(`Unknown ABI type: ${abiType}`);
|
|
29
|
-
}
|
|
30
|
-
return normalized;
|
|
31
|
-
}
|
|
1
|
+
import { createContractInvocation, typeRegistry, defineEnum, defineStruct, createVMParameter } from './xvm_serializer.js';
|
|
32
2
|
/**
|
|
33
3
|
* Contract class that dynamically generates methods based on ABI
|
|
34
4
|
*/
|
|
@@ -37,107 +7,179 @@ export class Contract {
|
|
|
37
7
|
this.address = address;
|
|
38
8
|
this.abi = abi;
|
|
39
9
|
this.methods = new Map();
|
|
40
|
-
|
|
10
|
+
this.register_internal_types();
|
|
41
11
|
for (const entry of abi.data) {
|
|
42
12
|
if (entry.type === 'entry') {
|
|
43
13
|
this.methods.set(entry.name, entry);
|
|
44
|
-
|
|
45
|
-
|
|
14
|
+
this.create_dynamic_method(entry);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Register all custom types from ABI
|
|
20
|
+
*/
|
|
21
|
+
register_internal_types() {
|
|
22
|
+
if (!this.abi.internal_types)
|
|
23
|
+
return;
|
|
24
|
+
for (const type_def of this.abi.internal_types) {
|
|
25
|
+
if (type_def.kind === 'enum' && type_def.variants) {
|
|
26
|
+
typeRegistry.register(defineEnum(type_def.name, type_def.variants));
|
|
27
|
+
}
|
|
28
|
+
else if (type_def.kind === 'struct' && type_def.fields) {
|
|
29
|
+
typeRegistry.register(defineStruct(type_def.name, type_def.fields));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Helper to create struct values with positional arguments
|
|
35
|
+
* Validates field types immediately
|
|
36
|
+
* @param type_name - Name of the struct type
|
|
37
|
+
* @param field_values - Field values in the order defined in ABI
|
|
38
|
+
*/
|
|
39
|
+
struct(type_name, ...field_values) {
|
|
40
|
+
const type_def = this.abi.internal_types?.find(t => t.name === type_name && t.kind === 'struct');
|
|
41
|
+
if (!type_def || !type_def.fields) {
|
|
42
|
+
throw new Error(`Struct type '${type_name}' not found in contract ABI`);
|
|
43
|
+
}
|
|
44
|
+
if (field_values.length !== type_def.fields.length) {
|
|
45
|
+
throw new Error(`Struct '${type_name}' expects ${type_def.fields.length} fields ` +
|
|
46
|
+
`(${type_def.fields.map(f => f.name).join(', ')}), ` +
|
|
47
|
+
`but got ${field_values.length}`);
|
|
48
|
+
}
|
|
49
|
+
const result = {};
|
|
50
|
+
for (let i = 0; i < type_def.fields.length; i++) {
|
|
51
|
+
const field = type_def.fields[i];
|
|
52
|
+
const value = field_values[i];
|
|
53
|
+
try {
|
|
54
|
+
createVMParameter(value, field.type);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new Error(`Invalid value for field '${field.name}' (position ${i}) of struct '${type_name}': ${error}`);
|
|
58
|
+
}
|
|
59
|
+
result[field.name] = value;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Helper to create enum values with positional arguments
|
|
65
|
+
* Validates field types immediately
|
|
66
|
+
* @param type_name - Name of the enum type
|
|
67
|
+
* @param variant_name - Name of the variant
|
|
68
|
+
* @param field_values - Field values in the order defined in ABI
|
|
69
|
+
*/
|
|
70
|
+
enum(type_name, variant_name, ...field_values) {
|
|
71
|
+
const type_def = this.abi.internal_types?.find(t => t.name === type_name && t.kind === 'enum');
|
|
72
|
+
if (!type_def || !type_def.variants) {
|
|
73
|
+
throw new Error(`Enum type '${type_name}' not found in contract ABI`);
|
|
74
|
+
}
|
|
75
|
+
const variant = type_def.variants.find(v => v.name === variant_name);
|
|
76
|
+
if (!variant) {
|
|
77
|
+
const available = type_def.variants.map(v => v.name).join(', ');
|
|
78
|
+
throw new Error(`Unknown variant '${variant_name}' for enum '${type_name}'. ` +
|
|
79
|
+
`Available variants: ${available}`);
|
|
80
|
+
}
|
|
81
|
+
if (field_values.length !== variant.fields.length) {
|
|
82
|
+
throw new Error(`Variant '${variant_name}' of enum '${type_name}' expects ${variant.fields.length} fields ` +
|
|
83
|
+
`(${variant.fields.map(f => f.name).join(', ')}), ` +
|
|
84
|
+
`but got ${field_values.length}`);
|
|
85
|
+
}
|
|
86
|
+
const result = { type: variant_name };
|
|
87
|
+
for (let i = 0; i < variant.fields.length; i++) {
|
|
88
|
+
const field = variant.fields[i];
|
|
89
|
+
const value = field_values[i];
|
|
90
|
+
try {
|
|
91
|
+
createVMParameter(value, field.type);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw new Error(`Invalid value for field '${field.name}' (position ${i}) of variant '${variant_name}' in enum '${type_name}': ${error}`);
|
|
46
95
|
}
|
|
96
|
+
result[field.name] = value;
|
|
47
97
|
}
|
|
98
|
+
return result;
|
|
48
99
|
}
|
|
49
100
|
/**
|
|
50
101
|
* Creates a dynamic method on the contract instance
|
|
51
102
|
*/
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
// Create a method that properly binds 'this'
|
|
103
|
+
create_dynamic_method(entry) {
|
|
104
|
+
const method_name = entry.name;
|
|
55
105
|
const method = (params) => {
|
|
56
|
-
return this.invoke(
|
|
106
|
+
return this.invoke(method_name, params);
|
|
57
107
|
};
|
|
58
|
-
|
|
59
|
-
this[methodName] = method;
|
|
108
|
+
this[method_name] = method;
|
|
60
109
|
}
|
|
61
110
|
/**
|
|
62
111
|
* Invoke a contract method by name
|
|
63
|
-
* @param
|
|
112
|
+
* @param method_name - Name of the method from the ABI
|
|
64
113
|
* @param params - Parameters for the method call
|
|
65
114
|
*/
|
|
66
|
-
invoke(
|
|
67
|
-
const entry = this.methods.get(
|
|
115
|
+
invoke(method_name, params = { permission: "all" }) {
|
|
116
|
+
const entry = this.methods.get(method_name);
|
|
68
117
|
if (!entry) {
|
|
69
|
-
throw new Error(`Method '${
|
|
118
|
+
throw new Error(`Method '${method_name}' not found in contract ABI`);
|
|
70
119
|
}
|
|
71
|
-
|
|
72
|
-
const { maxGas, deposits, ...methodParams } = params;
|
|
73
|
-
// Build parameter list according to ABI
|
|
120
|
+
const { maxGas, deposits, permission, ...method_params } = params;
|
|
74
121
|
const parameters = [];
|
|
75
|
-
for (const
|
|
76
|
-
const value =
|
|
122
|
+
for (const abi_param of entry.params) {
|
|
123
|
+
const value = method_params[abi_param.name];
|
|
77
124
|
if (value === undefined) {
|
|
78
|
-
throw new Error(`Missing required parameter '${
|
|
125
|
+
throw new Error(`Missing required parameter '${abi_param.name}' for method '${method_name}'`);
|
|
79
126
|
}
|
|
80
127
|
try {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
parameters.push(vmParam);
|
|
128
|
+
const VMParam = createVMParameter(value, abi_param.type);
|
|
129
|
+
parameters.push(VMParam);
|
|
84
130
|
}
|
|
85
131
|
catch (error) {
|
|
86
|
-
throw new Error(`Invalid parameter '${
|
|
132
|
+
throw new Error(`Invalid parameter '${abi_param.name}' for method '${method_name}': ${error}`);
|
|
87
133
|
}
|
|
88
134
|
}
|
|
89
|
-
|
|
90
|
-
const invocationParams = {
|
|
135
|
+
const invocation_params = {
|
|
91
136
|
contract: this.address,
|
|
92
|
-
|
|
137
|
+
chunk_id: entry.chunk_id,
|
|
93
138
|
parameters,
|
|
94
|
-
|
|
139
|
+
permission,
|
|
140
|
+
maxGas: maxGas || 50000000
|
|
95
141
|
};
|
|
96
142
|
if (deposits && Object.keys(deposits).length > 0) {
|
|
97
|
-
|
|
143
|
+
invocation_params.deposits = deposits;
|
|
98
144
|
}
|
|
99
|
-
return createContractInvocation(
|
|
145
|
+
return createContractInvocation(invocation_params);
|
|
100
146
|
}
|
|
101
147
|
/**
|
|
102
148
|
* Get list of available methods
|
|
103
149
|
*/
|
|
104
|
-
|
|
150
|
+
get_methods() {
|
|
105
151
|
return Array.from(this.methods.keys());
|
|
106
152
|
}
|
|
107
153
|
/**
|
|
108
154
|
* Get method signature information
|
|
109
155
|
*/
|
|
110
|
-
|
|
111
|
-
return this.methods.get(
|
|
156
|
+
get_method_signature(method_name) {
|
|
157
|
+
return this.methods.get(method_name);
|
|
112
158
|
}
|
|
113
159
|
/**
|
|
114
160
|
* Validate parameters for a method without creating the transaction
|
|
115
161
|
*/
|
|
116
|
-
|
|
117
|
-
const entry = this.methods.get(
|
|
162
|
+
validate_params(method_name, params) {
|
|
163
|
+
const entry = this.methods.get(method_name);
|
|
118
164
|
if (!entry) {
|
|
119
|
-
throw new Error(`Method '${
|
|
165
|
+
throw new Error(`Method '${method_name}' not found in contract ABI`);
|
|
120
166
|
}
|
|
121
|
-
const { deposits, maxGas, ...
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
throw new Error(`Missing required parameter '${abiParam.name}'`);
|
|
167
|
+
const { deposits, maxGas, ...method_params } = params;
|
|
168
|
+
for (const abi_param of entry.params) {
|
|
169
|
+
if (!(abi_param.name in method_params)) {
|
|
170
|
+
throw new Error(`Missing required parameter '${abi_param.name}'`);
|
|
126
171
|
}
|
|
127
|
-
// Validate type
|
|
128
172
|
try {
|
|
129
|
-
|
|
130
|
-
createVMParameter(methodParams[abiParam.name], normalizedType, true);
|
|
173
|
+
createVMParameter(method_params[abi_param.name], abi_param.type, true);
|
|
131
174
|
}
|
|
132
175
|
catch (error) {
|
|
133
|
-
throw new Error(`Invalid parameter '${
|
|
176
|
+
throw new Error(`Invalid parameter '${abi_param.name}': ${error}`);
|
|
134
177
|
}
|
|
135
178
|
}
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.warn(`Warning: Unexpected parameter '${key}' for method '${methodName}'`);
|
|
179
|
+
const expected_params = new Set(entry.params.map(p => p.name));
|
|
180
|
+
for (const key in method_params) {
|
|
181
|
+
if (!expected_params.has(key)) {
|
|
182
|
+
console.warn(`Warning: Unexpected parameter '${key}' for method '${method_name}'`);
|
|
141
183
|
}
|
|
142
184
|
}
|
|
143
185
|
return true;
|
|
@@ -147,7 +189,7 @@ export class Contract {
|
|
|
147
189
|
* Helper function to create a typed contract instance
|
|
148
190
|
* This provides better TypeScript support when the ABI is known at compile time
|
|
149
191
|
*/
|
|
150
|
-
export function
|
|
192
|
+
export function create_contract(address, abi) {
|
|
151
193
|
return new Contract(address, abi);
|
|
152
194
|
}
|
|
153
195
|
/**
|
|
@@ -161,12 +203,12 @@ export class ContractFactory {
|
|
|
161
203
|
* Create a new contract instance at the specified address
|
|
162
204
|
*/
|
|
163
205
|
at(address) {
|
|
164
|
-
return
|
|
206
|
+
return create_contract(address, this.abi);
|
|
165
207
|
}
|
|
166
208
|
/**
|
|
167
209
|
* Get the ABI
|
|
168
210
|
*/
|
|
169
|
-
|
|
211
|
+
get_abi() {
|
|
170
212
|
return this.abi;
|
|
171
213
|
}
|
|
172
214
|
}
|