@xelis/sdk 0.11.14 → 0.11.16

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.
Files changed (51) hide show
  1. package/dist/cjs/address/bech32.js +47 -56
  2. package/dist/cjs/address/index.js +20 -21
  3. package/dist/cjs/config.js +26 -38
  4. package/dist/cjs/contract/contract.js +178 -0
  5. package/dist/cjs/contract/typed_contract.js +259 -0
  6. package/dist/cjs/contract/xvm_serializer.js +170 -0
  7. package/dist/cjs/daemon/rpc.js +157 -168
  8. package/dist/cjs/daemon/types.js +4 -1
  9. package/dist/cjs/daemon/websocket.js +170 -181
  10. package/dist/cjs/data/element.js +39 -41
  11. package/dist/cjs/data/value.js +106 -111
  12. package/dist/cjs/react/daemon.js +33 -43
  13. package/dist/cjs/rpc/http.js +75 -132
  14. package/dist/cjs/rpc/parse_json/parse_json.js +4 -4
  15. package/dist/cjs/rpc/types.js +1 -1
  16. package/dist/cjs/rpc/websocket.js +131 -201
  17. package/dist/cjs/wallet/rpc.js +98 -117
  18. package/dist/cjs/wallet/types.js +1 -1
  19. package/dist/cjs/wallet/websocket.js +105 -126
  20. package/dist/cjs/xswd/relayer/app.js +57 -36
  21. package/dist/cjs/xswd/relayer/index.js +25 -27
  22. package/dist/cjs/xswd/types.js +1 -1
  23. package/dist/cjs/xswd/websocket.js +15 -33
  24. package/dist/esm/address/bech32.js +46 -55
  25. package/dist/esm/address/index.js +16 -17
  26. package/dist/esm/config.js +25 -37
  27. package/dist/esm/contract/contract.js +172 -0
  28. package/dist/esm/contract/typed_contract.js +251 -0
  29. package/dist/esm/contract/xvm_serializer.js +163 -0
  30. package/dist/esm/daemon/rpc.js +153 -165
  31. package/dist/esm/daemon/types.js +3 -0
  32. package/dist/esm/daemon/websocket.js +166 -179
  33. package/dist/esm/data/element.js +37 -40
  34. package/dist/esm/data/value.js +104 -112
  35. package/dist/esm/react/daemon.js +30 -40
  36. package/dist/esm/rpc/http.js +73 -131
  37. package/dist/esm/rpc/parse_json/parse_json.js +1 -1
  38. package/dist/esm/rpc/websocket.js +126 -197
  39. package/dist/esm/wallet/rpc.js +93 -113
  40. package/dist/esm/wallet/websocket.js +101 -124
  41. package/dist/esm/xswd/relayer/app.js +54 -34
  42. package/dist/esm/xswd/relayer/index.js +22 -24
  43. package/dist/esm/xswd/websocket.js +10 -29
  44. package/dist/types/contract/contract.d.ts +80 -0
  45. package/dist/types/contract/typed_contract.d.ts +94 -0
  46. package/dist/types/contract/xvm_serializer.d.ts +69 -0
  47. package/dist/types/daemon/rpc.d.ts +5 -2
  48. package/dist/types/daemon/types.d.ts +99 -20
  49. package/dist/types/daemon/websocket.d.ts +5 -2
  50. package/dist/types/wallet/types.d.ts +1 -1
  51. 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
+ }