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