@xelis/sdk 0.11.15 → 0.11.17
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/address/bech32.js +47 -56
- package/dist/cjs/address/index.js +20 -21
- package/dist/cjs/config.js +26 -38
- package/dist/cjs/contract/contract.js +178 -0
- package/dist/cjs/contract/typed_contract.js +259 -0
- package/dist/cjs/contract/xvm_serializer.js +170 -0
- package/dist/cjs/daemon/rpc.js +157 -168
- package/dist/cjs/daemon/types.js +4 -1
- package/dist/cjs/daemon/websocket.js +170 -181
- package/dist/cjs/data/element.js +39 -41
- package/dist/cjs/data/value.js +106 -111
- package/dist/cjs/react/daemon.js +33 -43
- package/dist/cjs/rpc/http.js +75 -132
- package/dist/cjs/rpc/parse_json/parse_json.js +4 -4
- package/dist/cjs/rpc/types.js +1 -1
- package/dist/cjs/rpc/websocket.js +131 -201
- package/dist/cjs/wallet/rpc.js +98 -117
- package/dist/cjs/wallet/types.js +1 -1
- package/dist/cjs/wallet/websocket.js +105 -126
- package/dist/cjs/xswd/relayer/app.js +57 -36
- package/dist/cjs/xswd/relayer/index.js +25 -27
- package/dist/cjs/xswd/types.js +1 -1
- package/dist/cjs/xswd/websocket.js +15 -33
- package/dist/esm/address/bech32.js +46 -55
- package/dist/esm/address/index.js +16 -17
- package/dist/esm/config.js +25 -37
- package/dist/esm/contract/contract.js +172 -0
- package/dist/esm/contract/typed_contract.js +251 -0
- package/dist/esm/contract/xvm_serializer.js +163 -0
- package/dist/esm/daemon/rpc.js +153 -165
- package/dist/esm/daemon/types.js +3 -0
- package/dist/esm/daemon/websocket.js +166 -179
- package/dist/esm/data/element.js +37 -40
- package/dist/esm/data/value.js +104 -112
- package/dist/esm/react/daemon.js +30 -40
- package/dist/esm/rpc/http.js +73 -131
- package/dist/esm/rpc/parse_json/parse_json.js +1 -1
- package/dist/esm/rpc/websocket.js +126 -197
- package/dist/esm/wallet/rpc.js +93 -113
- package/dist/esm/wallet/websocket.js +101 -124
- package/dist/esm/xswd/relayer/app.js +54 -34
- package/dist/esm/xswd/relayer/index.js +22 -24
- package/dist/esm/xswd/websocket.js +10 -29
- package/dist/types/contract/contract.d.ts +80 -0
- package/dist/types/contract/typed_contract.d.ts +94 -0
- package/dist/types/contract/xvm_serializer.d.ts +69 -0
- package/dist/types/daemon/rpc.d.ts +5 -2
- package/dist/types/daemon/types.d.ts +97 -18
- package/dist/types/daemon/websocket.d.ts +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createVMParameter, createContractInvocation } from './xvm_serializer.js';
|
|
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
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Contract class that dynamically generates methods based on ABI
|
|
34
|
+
*/
|
|
35
|
+
export class Contract {
|
|
36
|
+
constructor(address, abi) {
|
|
37
|
+
this.address = address;
|
|
38
|
+
this.abi = abi;
|
|
39
|
+
this.methods = new Map();
|
|
40
|
+
// Parse ABI and set up methods map
|
|
41
|
+
for (const entry of abi.data) {
|
|
42
|
+
if (entry.type === 'entry') {
|
|
43
|
+
this.methods.set(entry.name, entry);
|
|
44
|
+
// Dynamically add method to the instance
|
|
45
|
+
this.createDynamicMethod(entry);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a dynamic method on the contract instance
|
|
51
|
+
*/
|
|
52
|
+
createDynamicMethod(entry) {
|
|
53
|
+
const methodName = entry.name;
|
|
54
|
+
// Create a method that properly binds 'this'
|
|
55
|
+
const method = (params) => {
|
|
56
|
+
return this.invoke(methodName, params);
|
|
57
|
+
};
|
|
58
|
+
// Add the method to the instance
|
|
59
|
+
this[methodName] = method;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Invoke a contract method by name
|
|
63
|
+
* @param methodName - Name of the method from the ABI
|
|
64
|
+
* @param params - Parameters for the method call
|
|
65
|
+
*/
|
|
66
|
+
invoke(methodName, params = {}) {
|
|
67
|
+
const entry = this.methods.get(methodName);
|
|
68
|
+
if (!entry) {
|
|
69
|
+
throw new Error(`Method '${methodName}' not found in contract ABI`);
|
|
70
|
+
}
|
|
71
|
+
// Extract special parameters
|
|
72
|
+
const { maxGas, deposits, ...methodParams } = params;
|
|
73
|
+
// Build parameter list according to ABI
|
|
74
|
+
const parameters = [];
|
|
75
|
+
for (const abiParam of entry.params) {
|
|
76
|
+
const value = methodParams[abiParam.name];
|
|
77
|
+
if (value === undefined) {
|
|
78
|
+
throw new Error(`Missing required parameter '${abiParam.name}' for method '${methodName}'`);
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const normalizedType = normalizeType(abiParam.type);
|
|
82
|
+
const vmParam = createVMParameter(value, normalizedType);
|
|
83
|
+
parameters.push(vmParam);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new Error(`Invalid parameter '${abiParam.name}' for method '${methodName}': ${error}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Create the contract invocation
|
|
90
|
+
const invocationParams = {
|
|
91
|
+
contract: this.address,
|
|
92
|
+
chunkId: entry.chunk_id,
|
|
93
|
+
parameters,
|
|
94
|
+
maxGas: maxGas || 200000000
|
|
95
|
+
};
|
|
96
|
+
if (deposits && Object.keys(deposits).length > 0) {
|
|
97
|
+
invocationParams.deposits = deposits;
|
|
98
|
+
}
|
|
99
|
+
return createContractInvocation(invocationParams);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get list of available methods
|
|
103
|
+
*/
|
|
104
|
+
getMethods() {
|
|
105
|
+
return Array.from(this.methods.keys());
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get method signature information
|
|
109
|
+
*/
|
|
110
|
+
getMethodSignature(methodName) {
|
|
111
|
+
return this.methods.get(methodName);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Validate parameters for a method without creating the transaction
|
|
115
|
+
*/
|
|
116
|
+
validateParams(methodName, params) {
|
|
117
|
+
const entry = this.methods.get(methodName);
|
|
118
|
+
if (!entry) {
|
|
119
|
+
throw new Error(`Method '${methodName}' not found in contract ABI`);
|
|
120
|
+
}
|
|
121
|
+
const { deposits, maxGas, ...methodParams } = params;
|
|
122
|
+
// Check all required parameters are present
|
|
123
|
+
for (const abiParam of entry.params) {
|
|
124
|
+
if (!(abiParam.name in methodParams)) {
|
|
125
|
+
throw new Error(`Missing required parameter '${abiParam.name}'`);
|
|
126
|
+
}
|
|
127
|
+
// Validate type
|
|
128
|
+
try {
|
|
129
|
+
const normalizedType = normalizeType(abiParam.type);
|
|
130
|
+
createVMParameter(methodParams[abiParam.name], normalizedType, true);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new Error(`Invalid parameter '${abiParam.name}': ${error}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Check for extra parameters
|
|
137
|
+
const expectedParams = new Set(entry.params.map(p => p.name));
|
|
138
|
+
for (const key in methodParams) {
|
|
139
|
+
if (!expectedParams.has(key)) {
|
|
140
|
+
console.warn(`Warning: Unexpected parameter '${key}' for method '${methodName}'`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Helper function to create a typed contract instance
|
|
148
|
+
* This provides better TypeScript support when the ABI is known at compile time
|
|
149
|
+
*/
|
|
150
|
+
export function createContract(address, abi) {
|
|
151
|
+
return new Contract(address, abi);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Factory for creating multiple contracts with the same ABI
|
|
155
|
+
*/
|
|
156
|
+
export class ContractFactory {
|
|
157
|
+
constructor(abi) {
|
|
158
|
+
this.abi = abi;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a new contract instance at the specified address
|
|
162
|
+
*/
|
|
163
|
+
at(address) {
|
|
164
|
+
return createContract(address, this.abi);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the ABI
|
|
168
|
+
*/
|
|
169
|
+
getABI() {
|
|
170
|
+
return this.abi;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { createVMParameter, createContractInvocation } from './xvm_serializer.js';
|
|
2
|
+
// Convert ABI type to validator type
|
|
3
|
+
function normalizeType(abiType) {
|
|
4
|
+
const typeMap = {
|
|
5
|
+
'Hash': 'Hash',
|
|
6
|
+
'Address': 'Address',
|
|
7
|
+
'PublicKey': 'PublicKey',
|
|
8
|
+
'Blob': 'Blob',
|
|
9
|
+
'u256': 'u256',
|
|
10
|
+
'u128': 'u128',
|
|
11
|
+
'u64': 'u64',
|
|
12
|
+
'u32': 'u32',
|
|
13
|
+
'u16': 'u16',
|
|
14
|
+
'u8': 'u8',
|
|
15
|
+
'boolean': 'boolean',
|
|
16
|
+
'bool': 'boolean',
|
|
17
|
+
'string': 'string',
|
|
18
|
+
'String': 'string',
|
|
19
|
+
'Boolean': 'boolean',
|
|
20
|
+
'U256': 'u256',
|
|
21
|
+
'U128': 'u128',
|
|
22
|
+
'U64': 'u64',
|
|
23
|
+
'U32': 'u32',
|
|
24
|
+
'U16': 'u16',
|
|
25
|
+
'U8': 'u8'
|
|
26
|
+
};
|
|
27
|
+
const normalized = typeMap[abiType];
|
|
28
|
+
if (!normalized) {
|
|
29
|
+
throw new Error(`Unknown ABI type: ${abiType}`);
|
|
30
|
+
}
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Strongly typed contract class
|
|
35
|
+
*/
|
|
36
|
+
export class TypedContract {
|
|
37
|
+
constructor(address, abi) {
|
|
38
|
+
this.address = address;
|
|
39
|
+
this.abi = abi;
|
|
40
|
+
this.methods = new Map();
|
|
41
|
+
// Initialize methods
|
|
42
|
+
for (const entry of abi.data) {
|
|
43
|
+
if (entry.type === 'entry') {
|
|
44
|
+
console.log("new method", entry.name);
|
|
45
|
+
this.methods.set(entry.name, entry);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Return a Proxy to handle dynamic method calls
|
|
49
|
+
return new Proxy(this, {
|
|
50
|
+
get(target, prop, receiver) {
|
|
51
|
+
// If it's a known property/method, return it
|
|
52
|
+
if (prop in target) {
|
|
53
|
+
return Reflect.get(target, prop, receiver);
|
|
54
|
+
}
|
|
55
|
+
// If it's a string property that matches a method name, create dynamic method
|
|
56
|
+
if (typeof prop === 'string' && target.methods.has(prop)) {
|
|
57
|
+
return (params) => target.invokeUnsafe(prop, params);
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Internal method to invoke contract functions
|
|
65
|
+
*/
|
|
66
|
+
invokeUnsafe(methodName, params = {}) {
|
|
67
|
+
const entry = this.methods.get(methodName);
|
|
68
|
+
if (!entry) {
|
|
69
|
+
throw new Error(`Method '${methodName}' not found in contract ABI`);
|
|
70
|
+
}
|
|
71
|
+
// Extract special parameters
|
|
72
|
+
const { maxGas, deposits, ...methodParams } = params;
|
|
73
|
+
// Build parameter list according to ABI
|
|
74
|
+
const parameters = [];
|
|
75
|
+
for (const abiParam of entry.params) {
|
|
76
|
+
const value = methodParams[abiParam.name];
|
|
77
|
+
// Check if parameter is required
|
|
78
|
+
if (value === undefined && !abiParam.optional) {
|
|
79
|
+
if (abiParam.default !== undefined) {
|
|
80
|
+
methodParams[abiParam.name] = abiParam.default;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error(`Missing required parameter '${abiParam.name}' for method '${methodName}'`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (value !== undefined) {
|
|
87
|
+
try {
|
|
88
|
+
const normalizedType = normalizeType(abiParam.type);
|
|
89
|
+
const vmParam = createVMParameter(value, normalizedType);
|
|
90
|
+
parameters.push(vmParam);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new Error(`Invalid parameter '${abiParam.name}' for method '${methodName}': ${error}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Create the contract invocation
|
|
98
|
+
const invocationParams = {
|
|
99
|
+
contract: this.address,
|
|
100
|
+
chunkId: entry.chunk_id,
|
|
101
|
+
parameters,
|
|
102
|
+
maxGas: maxGas || 200000000
|
|
103
|
+
};
|
|
104
|
+
if (deposits && Object.keys(deposits).length > 0) {
|
|
105
|
+
invocationParams.deposits = deposits;
|
|
106
|
+
}
|
|
107
|
+
return createContractInvocation(invocationParams);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Type-safe invoke method
|
|
111
|
+
*/
|
|
112
|
+
invoke(methodName, params) {
|
|
113
|
+
return this.invokeUnsafe(methodName, params);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get list of available methods
|
|
117
|
+
*/
|
|
118
|
+
getMethods() {
|
|
119
|
+
return Array.from(this.methods.keys());
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get method signature information
|
|
123
|
+
*/
|
|
124
|
+
getMethodSignature(methodName) {
|
|
125
|
+
return this.methods.get(methodName);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Generate TypeScript interface for the contract
|
|
129
|
+
*/
|
|
130
|
+
generateInterface() {
|
|
131
|
+
const lines = [
|
|
132
|
+
`interface ${this.constructor.name}Methods {`
|
|
133
|
+
];
|
|
134
|
+
for (const [name, entry] of this.methods) {
|
|
135
|
+
const params = entry.params.map(p => {
|
|
136
|
+
const optional = p.optional ? '?' : '';
|
|
137
|
+
return ` ${p.name}${optional}: ${this.getTypeScriptType(p.type)};`;
|
|
138
|
+
}).join('\n');
|
|
139
|
+
lines.push(` ${name}(params: {`);
|
|
140
|
+
lines.push(params);
|
|
141
|
+
lines.push(' maxGas?: number;');
|
|
142
|
+
lines.push(' deposits?: Record<string, number | bigint>;');
|
|
143
|
+
lines.push(' }): Record<string, any>;');
|
|
144
|
+
lines.push('');
|
|
145
|
+
}
|
|
146
|
+
lines.push('}');
|
|
147
|
+
return lines.join('\n');
|
|
148
|
+
}
|
|
149
|
+
getTypeScriptType(abiType) {
|
|
150
|
+
const typeMap = {
|
|
151
|
+
'Hash': 'string',
|
|
152
|
+
'Address': 'string',
|
|
153
|
+
'PublicKey': 'string',
|
|
154
|
+
'Blob': 'string',
|
|
155
|
+
'String': 'string',
|
|
156
|
+
'string': 'string',
|
|
157
|
+
'Boolean': 'boolean',
|
|
158
|
+
'boolean': 'boolean',
|
|
159
|
+
'bool': 'boolean',
|
|
160
|
+
'U256': 'bigint | number',
|
|
161
|
+
'u256': 'bigint | number',
|
|
162
|
+
'U128': 'bigint | number',
|
|
163
|
+
'u128': 'bigint | number',
|
|
164
|
+
'U64': 'bigint | number',
|
|
165
|
+
'u64': 'bigint | number',
|
|
166
|
+
'U32': 'number',
|
|
167
|
+
'u32': 'number',
|
|
168
|
+
'U16': 'number',
|
|
169
|
+
'u16': 'number',
|
|
170
|
+
'U8': 'number',
|
|
171
|
+
'u8': 'number'
|
|
172
|
+
};
|
|
173
|
+
return typeMap[abiType] || 'any';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create a typed contract instance with full TypeScript support
|
|
178
|
+
*/
|
|
179
|
+
export function createTypedContract(address, abi) {
|
|
180
|
+
return new TypedContract(address, abi);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Contract factory with strong typing
|
|
184
|
+
*/
|
|
185
|
+
export class TypedContractFactory {
|
|
186
|
+
constructor(abi, contractName = 'Contract') {
|
|
187
|
+
this.abi = abi;
|
|
188
|
+
this.contractName = contractName;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create a new contract instance at the specified address
|
|
192
|
+
*/
|
|
193
|
+
at(address) {
|
|
194
|
+
return createTypedContract(address, this.abi);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get the ABI
|
|
198
|
+
*/
|
|
199
|
+
getABI() {
|
|
200
|
+
return this.abi;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Generate TypeScript definitions for this contract
|
|
204
|
+
*/
|
|
205
|
+
generateTypeDefinitions() {
|
|
206
|
+
const contract = new TypedContract('0x0', this.abi);
|
|
207
|
+
return contract.generateInterface();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Utility to validate ABI structure
|
|
212
|
+
*/
|
|
213
|
+
export function validateABI(abi) {
|
|
214
|
+
if (!Array.isArray(abi)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
for (const entry of abi) {
|
|
218
|
+
if (typeof entry !== 'object' || !entry) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (typeof entry.chunk_id !== 'number') {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
if (typeof entry.name !== 'string') {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
if (!Array.isArray(entry.params)) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (entry.type !== 'entry' && entry.type !== 'view') {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
for (const param of entry.params) {
|
|
234
|
+
if (typeof param.name !== 'string' || typeof param.type !== 'string') {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Helper to create a contract from JSON ABI
|
|
243
|
+
*/
|
|
244
|
+
export async function createContractFromJSON(address, abiPath) {
|
|
245
|
+
const response = await fetch(abiPath);
|
|
246
|
+
const abi = await response.json();
|
|
247
|
+
if (!validateABI(abi.data)) {
|
|
248
|
+
throw new Error('Invalid ABI structure');
|
|
249
|
+
}
|
|
250
|
+
return createTypedContract(address, abi);
|
|
251
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Known opaque types that need special wrapping
|
|
2
|
+
const OPAQUE_TYPES = new Set(['Hash', 'Address', 'PublicKey', 'Blob' /** TODO */]);
|
|
3
|
+
// Type validation and conversion helpers
|
|
4
|
+
const TYPE_VALIDATORS = {
|
|
5
|
+
'u256': (v) => {
|
|
6
|
+
const num = typeof v === 'bigint' ? v : BigInt(v);
|
|
7
|
+
if (num < 0n)
|
|
8
|
+
throw new Error(`Value ${v} cannot be negative for u256`);
|
|
9
|
+
return Number(num);
|
|
10
|
+
},
|
|
11
|
+
'u128': (v) => {
|
|
12
|
+
const num = typeof v === 'bigint' ? v : BigInt(v);
|
|
13
|
+
if (num < 0n)
|
|
14
|
+
throw new Error(`Value ${v} cannot be negative for u128`);
|
|
15
|
+
return Number(num);
|
|
16
|
+
},
|
|
17
|
+
'u64': (v) => {
|
|
18
|
+
const num = typeof v === 'bigint' ? v : BigInt(v);
|
|
19
|
+
if (num < 0n || num > 0xffffffffffffffffn) {
|
|
20
|
+
throw new Error(`Value ${v} is out of range for u64`);
|
|
21
|
+
}
|
|
22
|
+
return Number(num);
|
|
23
|
+
},
|
|
24
|
+
'u32': (v) => {
|
|
25
|
+
const num = Number(v);
|
|
26
|
+
if (num < 0 || num > 0xFFFFFFFF || !Number.isInteger(num)) {
|
|
27
|
+
throw new Error(`Value ${v} is not a valid u32`);
|
|
28
|
+
}
|
|
29
|
+
return num;
|
|
30
|
+
},
|
|
31
|
+
'u16': (v) => {
|
|
32
|
+
const num = Number(v);
|
|
33
|
+
if (num < 0 || num > 0xFFFF || !Number.isInteger(num)) {
|
|
34
|
+
throw new Error(`Value ${v} is not a valid u16`);
|
|
35
|
+
}
|
|
36
|
+
return num;
|
|
37
|
+
},
|
|
38
|
+
'u8': (v) => {
|
|
39
|
+
const num = Number(v);
|
|
40
|
+
if (num < 0 || num > 255 || !Number.isInteger(num)) {
|
|
41
|
+
throw new Error(`Value ${v} is not a valid u8`);
|
|
42
|
+
}
|
|
43
|
+
return num;
|
|
44
|
+
},
|
|
45
|
+
'boolean': (v) => Boolean(v),
|
|
46
|
+
'string': (v) => String(v),
|
|
47
|
+
'Hash': (v) => {
|
|
48
|
+
const str = String(v);
|
|
49
|
+
if (!/^[0-9a-fA-F]{64}$/.test(str)) {
|
|
50
|
+
throw new Error(`Value ${v} is not a valid 64-character hex hash`);
|
|
51
|
+
}
|
|
52
|
+
return str;
|
|
53
|
+
},
|
|
54
|
+
'Address': (v) => {
|
|
55
|
+
const str = String(v);
|
|
56
|
+
// TODO validate
|
|
57
|
+
return str;
|
|
58
|
+
},
|
|
59
|
+
'PublicKey': (v) => {
|
|
60
|
+
const str = String(v);
|
|
61
|
+
// TODO validate
|
|
62
|
+
return str;
|
|
63
|
+
},
|
|
64
|
+
'Blob': (v) => {
|
|
65
|
+
const str = String(v);
|
|
66
|
+
// TODO validate
|
|
67
|
+
return str;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Creates a VM-compatible parameter object
|
|
72
|
+
* @param value - The value to wrap
|
|
73
|
+
* @param type - The type string (e.g., 'u64', 'Hash', 'string')
|
|
74
|
+
* @param validate - Whether to validate and convert the value (default: true)
|
|
75
|
+
*/
|
|
76
|
+
export function createVMParameter(value, type, validate = true) {
|
|
77
|
+
let processedValue = value;
|
|
78
|
+
// Validate and convert value if requested
|
|
79
|
+
if (validate && TYPE_VALIDATORS[type]) {
|
|
80
|
+
try {
|
|
81
|
+
processedValue = TYPE_VALIDATORS[type](value);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(`Failed to create VM parameter for type ${type}: ${error}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Handle opaque types (Hash, Address, PublicKey)
|
|
88
|
+
if (OPAQUE_TYPES.has(type)) {
|
|
89
|
+
return {
|
|
90
|
+
type: "default",
|
|
91
|
+
value: {
|
|
92
|
+
type: "opaque",
|
|
93
|
+
value: {
|
|
94
|
+
type: type,
|
|
95
|
+
value: processedValue
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Handle regular types
|
|
101
|
+
return {
|
|
102
|
+
type: "default",
|
|
103
|
+
value: {
|
|
104
|
+
type: type,
|
|
105
|
+
value: processedValue
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Convenience functions for common types
|
|
111
|
+
*/
|
|
112
|
+
export const vmParam = {
|
|
113
|
+
hash: (value) => createVMParameter(value, 'Hash'),
|
|
114
|
+
address: (value) => createVMParameter(value, 'Address'),
|
|
115
|
+
publicKey: (value) => createVMParameter(value, 'PublicKey'),
|
|
116
|
+
blob: (value) => createVMParameter(value, 'Blob'),
|
|
117
|
+
u64: (value) => createVMParameter(value, 'u64'),
|
|
118
|
+
u32: (value) => createVMParameter(value, 'u32'),
|
|
119
|
+
u16: (value) => createVMParameter(value, 'u16'),
|
|
120
|
+
u8: (value) => createVMParameter(value, 'u8'),
|
|
121
|
+
string: (value) => createVMParameter(value, 'string'),
|
|
122
|
+
boolean: (value) => createVMParameter(value, 'boolean'),
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Creates a deposits object for contract calls
|
|
126
|
+
* @param deposits - Object mapping token hashes to amounts
|
|
127
|
+
*/
|
|
128
|
+
export function createDeposits(deposits) {
|
|
129
|
+
const result = {};
|
|
130
|
+
for (const [tokenHash, amount] of Object.entries(deposits)) {
|
|
131
|
+
// Validate hash format
|
|
132
|
+
if (!/^[0-9a-fA-F]{64}$/.test(tokenHash)) {
|
|
133
|
+
throw new Error(`Invalid token hash format: ${tokenHash}`);
|
|
134
|
+
}
|
|
135
|
+
result[tokenHash] = { amount };
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
export function createContractInvocation(params) {
|
|
140
|
+
const { contract, chunkId, parameters = [], deposits, maxGas = 200000000 } = params;
|
|
141
|
+
const result = {
|
|
142
|
+
invoke_contract: {
|
|
143
|
+
contract,
|
|
144
|
+
max_gas: maxGas,
|
|
145
|
+
chunk_id: chunkId,
|
|
146
|
+
parameters
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
if (deposits && Object.keys(deposits).length > 0) {
|
|
150
|
+
result.invoke_contract.deposits = createDeposits(deposits);
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
export function createContractDeployment(params) {
|
|
155
|
+
const { bytecode, hasConstructor = false, maxGas = 200000000 } = params;
|
|
156
|
+
const result = {
|
|
157
|
+
deploy_contract: {
|
|
158
|
+
module: bytecode,
|
|
159
|
+
...(hasConstructor && { invoke: { max_gas: maxGas } })
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return result;
|
|
163
|
+
}
|