koilib 5.5.2 → 5.5.4
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/koinos.js +34 -18
- package/dist/koinos.min.js +1 -1
- package/package.json +4 -3
- package/src/Contract.ts +614 -0
- package/src/Provider.ts +512 -0
- package/src/Serializer.ts +366 -0
- package/src/Signer.ts +827 -0
- package/src/Transaction.ts +241 -0
- package/src/index.ts +9 -0
- package/src/index2.ts +17 -0
- package/src/indexUtils.ts +2 -0
- package/src/interface.ts +767 -0
- package/src/jsonDescriptors/chain-proto.json +726 -0
- package/src/jsonDescriptors/pow-proto.json +68 -0
- package/src/jsonDescriptors/token-proto.json +255 -0
- package/src/jsonDescriptors/value-proto.json +161 -0
- package/src/protoModules/protocol-proto.js +8138 -0
- package/src/protoModules/value-proto.js +1736 -0
- package/src/utils.ts +533 -0
- package/src/utilsNode.ts +378 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koilib",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.4",
|
|
4
4
|
"description": "JS Koinos Library",
|
|
5
5
|
"author": "Julian Gonzalez",
|
|
6
6
|
"repository": {
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"lib",
|
|
15
|
-
"dist"
|
|
15
|
+
"dist",
|
|
16
|
+
"src"
|
|
16
17
|
],
|
|
17
18
|
"main": "./lib/index.js",
|
|
18
19
|
"types": "./lib/index.d.ts",
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@noble/hashes": "^1.1.2",
|
|
48
49
|
"@noble/secp256k1": "^1.6.3",
|
|
49
|
-
"@roamin/koinos-pb-to-proto": "^0.0.
|
|
50
|
+
"@roamin/koinos-pb-to-proto": "^0.0.3",
|
|
50
51
|
"cross-fetch": "^3.1.5",
|
|
51
52
|
"multibase": "^4.0.6",
|
|
52
53
|
"protobufjs": "^7.0.0"
|
package/src/Contract.ts
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
import { Signer, SignerInterface } from "./Signer";
|
|
3
|
+
import { Provider } from "./Provider";
|
|
4
|
+
import { Serializer } from "./Serializer";
|
|
5
|
+
import {
|
|
6
|
+
TransactionJsonWait,
|
|
7
|
+
Abi,
|
|
8
|
+
CallContractOptions,
|
|
9
|
+
DecodedOperationJson,
|
|
10
|
+
OperationJson,
|
|
11
|
+
DeployOptions,
|
|
12
|
+
TransactionReceipt,
|
|
13
|
+
EventData,
|
|
14
|
+
DecodedEventData,
|
|
15
|
+
} from "./interface";
|
|
16
|
+
import { decodeBase58, encodeBase58, encodeBase64url } from "./utils";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The contract class contains the contract ID and contract entries
|
|
20
|
+
* definition needed to encode/decode operations during the
|
|
21
|
+
* interaction with the user and the communication with the RPC node.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const { Contract, Provider, Signer, utils } = require("koilib");
|
|
27
|
+
* const rpcNodes = ["http://api.koinos.io:8080"];
|
|
28
|
+
* const privateKey = "f186a5de49797bfd52dc42505c33d75a46822ed5b60046e09d7c336242e20200";
|
|
29
|
+
* const provider = new Provider(rpcNodes);
|
|
30
|
+
* const signer = new Signer({ privateKey, provider });
|
|
31
|
+
* const koinContract = new Contract({
|
|
32
|
+
* id: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
33
|
+
* abi: utils.tokenAbi,
|
|
34
|
+
* provider,
|
|
35
|
+
* signer,
|
|
36
|
+
* });
|
|
37
|
+
* const koin = koinContract.functions;
|
|
38
|
+
*
|
|
39
|
+
* // optional: preformat argument/return
|
|
40
|
+
* koinContract.abi.methods.balanceOf.preformat_argument = (owner) =>
|
|
41
|
+
* ({ owner });
|
|
42
|
+
* koinContract.abi.methods.balanceOf.preformat_return = (res) =>
|
|
43
|
+
* utils.formatUnits(res.value, 8);
|
|
44
|
+
* koinContract.abi.methods.transfer.preformat_argument = (arg) => ({
|
|
45
|
+
* from: signer.getAddress(),
|
|
46
|
+
* to: arg.to,
|
|
47
|
+
* value: utils.parseUnits(arg.value, 8),
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* async funtion main() {
|
|
51
|
+
* // Get balance
|
|
52
|
+
* const { result } = await koin.balanceOf("12fN2CQnuJM8cMnWZ1hPtM4knjLME8E4PD");
|
|
53
|
+
* console.log(result)
|
|
54
|
+
*
|
|
55
|
+
* // Transfer
|
|
56
|
+
* const { transaction, receipt } = await koin.transfer({
|
|
57
|
+
* to: "172AB1FgCsYrRAW5cwQ8KjadgxofvgPFd6",
|
|
58
|
+
* value: "10.0001",
|
|
59
|
+
* });
|
|
60
|
+
* console.log(`Transaction id ${transaction.id} submitted. Receipt:`);
|
|
61
|
+
* console.log(receipt);
|
|
62
|
+
*
|
|
63
|
+
* if (receipt.logs) {
|
|
64
|
+
* console.log(`Transfer failed. Logs: ${receipt.logs.join(",")}`);
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* // wait to be mined
|
|
68
|
+
* const blockNumber = await transaction.wait();
|
|
69
|
+
* console.log(`Transaction mined. Block number: ${blockNumber}`);
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* main();
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export class Contract {
|
|
76
|
+
/**
|
|
77
|
+
* Contract ID
|
|
78
|
+
*/
|
|
79
|
+
id: Uint8Array;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Set of functions to interact with the smart
|
|
83
|
+
* contract. These functions are automatically generated
|
|
84
|
+
* in the constructor of the class
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const owner = "1Gvqdo9if6v6tFomEuTuMWP1D7H7U9yksb";
|
|
89
|
+
* await koinContract.functions.balanceOf({ owner });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example using options
|
|
93
|
+
* ```ts
|
|
94
|
+
* await koinContract.functions.transfer({
|
|
95
|
+
* from: "1Gvqdo9if6v6tFomEuTuMWP1D7H7U9yksb",
|
|
96
|
+
* to: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
97
|
+
* value: "1",
|
|
98
|
+
* },{
|
|
99
|
+
* chainId: "EiB-hw5ABo-EXy6fGDd1Iq3gbAenxQ4Qe60pRbEVMVrR9A==",
|
|
100
|
+
* rcLimit: "100000000",
|
|
101
|
+
* nonce: "OAI=",
|
|
102
|
+
* payer: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
103
|
+
* payee: "1Gvqdo9if6v6tFomEuTuMWP1D7H7U9yksb",
|
|
104
|
+
* signTransaction: true,
|
|
105
|
+
* sendTransaction: true,
|
|
106
|
+
* broadcast: true,
|
|
107
|
+
* sendAbis: true,
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
functions: {
|
|
112
|
+
[x: string]: <T = Record<string, unknown>>(
|
|
113
|
+
args?: unknown,
|
|
114
|
+
opts?: CallContractOptions
|
|
115
|
+
) => Promise<{
|
|
116
|
+
operation: OperationJson;
|
|
117
|
+
transaction?: TransactionJsonWait;
|
|
118
|
+
result?: T;
|
|
119
|
+
receipt?: TransactionReceipt;
|
|
120
|
+
}>;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Application Binary Interface
|
|
125
|
+
*/
|
|
126
|
+
abi?: Abi;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Signer interacting with the smart contract
|
|
130
|
+
*/
|
|
131
|
+
signer?: SignerInterface;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Provider to connect with the blockchain
|
|
135
|
+
*/
|
|
136
|
+
provider?: Provider;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Serializer to serialize/deserialize data types
|
|
140
|
+
*/
|
|
141
|
+
serializer?: Serializer;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Bytecode. Needed to deploy the smart contract.
|
|
145
|
+
*/
|
|
146
|
+
bytecode?: Uint8Array;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Options to apply when creating transactions.
|
|
150
|
+
* By default it set rc_limit to 1e8, sendTransaction true,
|
|
151
|
+
* sendAbis true, and nonce undefined (to get it from the blockchain)
|
|
152
|
+
*/
|
|
153
|
+
options: CallContractOptions;
|
|
154
|
+
|
|
155
|
+
constructor(c: {
|
|
156
|
+
id?: string;
|
|
157
|
+
abi?: Abi;
|
|
158
|
+
bytecode?: Uint8Array;
|
|
159
|
+
options?: CallContractOptions;
|
|
160
|
+
signer?: Signer;
|
|
161
|
+
provider?: Provider;
|
|
162
|
+
/**
|
|
163
|
+
* Set this option if you can not use _eval_ functions
|
|
164
|
+
* in the current environment. In such cases, the
|
|
165
|
+
* serializer must come from an environment where it
|
|
166
|
+
* is able to use those functions.
|
|
167
|
+
*/
|
|
168
|
+
serializer?: Serializer;
|
|
169
|
+
}) {
|
|
170
|
+
this.signer = c.signer;
|
|
171
|
+
if (c.id) this.id = decodeBase58(c.id);
|
|
172
|
+
else {
|
|
173
|
+
if (!this.signer)
|
|
174
|
+
throw new Error("at least signer or contract id must be defined");
|
|
175
|
+
this.id = decodeBase58(this.signer.getAddress());
|
|
176
|
+
}
|
|
177
|
+
this.provider = c.provider || c.signer?.provider;
|
|
178
|
+
this.abi = c.abi;
|
|
179
|
+
this.bytecode = c.bytecode;
|
|
180
|
+
if (c.serializer) {
|
|
181
|
+
this.serializer = c.serializer;
|
|
182
|
+
} else if (c.abi && c.abi.koilib_types) {
|
|
183
|
+
this.serializer = new Serializer(c.abi.koilib_types);
|
|
184
|
+
} else if (c.abi && c.abi.types) {
|
|
185
|
+
this.serializer = new Serializer(c.abi.types);
|
|
186
|
+
}
|
|
187
|
+
this.options = {
|
|
188
|
+
signTransaction: true,
|
|
189
|
+
sendTransaction: true,
|
|
190
|
+
broadcast: true,
|
|
191
|
+
sendAbis: true,
|
|
192
|
+
...c.options,
|
|
193
|
+
};
|
|
194
|
+
this.functions = {};
|
|
195
|
+
this.updateFunctionsFromAbi();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get contract Id
|
|
200
|
+
*/
|
|
201
|
+
getId(): string {
|
|
202
|
+
return encodeBase58(this.id);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Fetch the ABI from the contract meta store and save it in the
|
|
207
|
+
* abi of the contract. The provider must have contract_meta_store
|
|
208
|
+
* microservice enabled.
|
|
209
|
+
* @param opts - options object with 2 boolean: 1) updateFunctions to
|
|
210
|
+
* specify if the contract functions should be regenerated based on
|
|
211
|
+
* the new ABI, and 2) updateSerializer to determine if the serializer
|
|
212
|
+
* should be updated with the types in the new ABI.
|
|
213
|
+
* @returns the new ABI saved in the contract
|
|
214
|
+
*/
|
|
215
|
+
async fetchAbi(
|
|
216
|
+
opts: {
|
|
217
|
+
updateFunctions: boolean;
|
|
218
|
+
updateSerializer: boolean;
|
|
219
|
+
} = {
|
|
220
|
+
updateFunctions: true,
|
|
221
|
+
updateSerializer: true,
|
|
222
|
+
}
|
|
223
|
+
): Promise<Abi | undefined> {
|
|
224
|
+
if (!this.provider) throw new Error("provider not found");
|
|
225
|
+
const response = await this.provider.call<{ meta: { abi: string } }>(
|
|
226
|
+
"contract_meta_store.get_contract_meta",
|
|
227
|
+
{
|
|
228
|
+
contract_id: this.getId(),
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
if (!response.meta || !response.meta.abi) return undefined;
|
|
232
|
+
this.abi = JSON.parse(response.meta.abi) as Abi;
|
|
233
|
+
if (opts.updateFunctions) this.updateFunctionsFromAbi();
|
|
234
|
+
if (opts.updateSerializer) {
|
|
235
|
+
if (this.abi.koilib_types)
|
|
236
|
+
this.serializer = new Serializer(this.abi.koilib_types);
|
|
237
|
+
else if (this.abi.types) this.serializer = new Serializer(this.abi.types);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return this.abi;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Create the contract functions based on the ABI
|
|
245
|
+
*/
|
|
246
|
+
updateFunctionsFromAbi(): boolean {
|
|
247
|
+
if (!this.abi || !this.abi.methods) return false;
|
|
248
|
+
Object.keys(this.abi.methods).forEach((name) => {
|
|
249
|
+
this.functions[name] = async <T = Record<string, unknown>>(
|
|
250
|
+
argu: unknown = {},
|
|
251
|
+
options?: CallContractOptions
|
|
252
|
+
): Promise<{
|
|
253
|
+
operation: OperationJson;
|
|
254
|
+
transaction?: TransactionJsonWait;
|
|
255
|
+
result?: T;
|
|
256
|
+
receipt?: TransactionReceipt;
|
|
257
|
+
}> => {
|
|
258
|
+
if (!this.provider) throw new Error("provider not found");
|
|
259
|
+
if (!this.abi || !this.abi.methods)
|
|
260
|
+
throw new Error("Methods are not defined");
|
|
261
|
+
if (!this.abi.methods[name])
|
|
262
|
+
throw new Error(`Method ${name} not defined in the ABI`);
|
|
263
|
+
const opts: CallContractOptions = {
|
|
264
|
+
...this.options,
|
|
265
|
+
...options,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const {
|
|
269
|
+
read_only: readOnly,
|
|
270
|
+
return: output,
|
|
271
|
+
default_output: defaultOutput,
|
|
272
|
+
preformat_argument: preformatArgument,
|
|
273
|
+
preformat_return: preformatReturn,
|
|
274
|
+
} = this.abi.methods[name];
|
|
275
|
+
let args: Record<string, unknown>;
|
|
276
|
+
if (typeof preformatArgument === "function") {
|
|
277
|
+
args = preformatArgument(argu);
|
|
278
|
+
} else {
|
|
279
|
+
args = argu as Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const operation = await this.encodeOperation({ name, args });
|
|
283
|
+
|
|
284
|
+
if (opts.onlyOperation) {
|
|
285
|
+
return { operation };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (readOnly) {
|
|
289
|
+
if (!output) throw new Error(`No output defined for ${name}`);
|
|
290
|
+
// read contract
|
|
291
|
+
const { result: resultEncoded } = await this.provider.readContract(
|
|
292
|
+
operation.call_contract!
|
|
293
|
+
);
|
|
294
|
+
let result = defaultOutput as T;
|
|
295
|
+
if (resultEncoded) {
|
|
296
|
+
result = await this.serializer!.deserialize<T>(
|
|
297
|
+
resultEncoded,
|
|
298
|
+
output
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
if (typeof preformatReturn === "function") {
|
|
302
|
+
result = preformatReturn(result as Record<string, unknown>) as T;
|
|
303
|
+
}
|
|
304
|
+
return { operation, result };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// write contract (sign and send)
|
|
308
|
+
if (!this.signer) throw new Error("signer not found");
|
|
309
|
+
let tx = await this.signer.prepareTransaction({
|
|
310
|
+
header: {
|
|
311
|
+
...(opts.chainId && { chain_id: opts.chainId }),
|
|
312
|
+
...(opts.rcLimit && { rc_limit: opts.rcLimit }),
|
|
313
|
+
...(opts.nonce && { nonce: opts.nonce }),
|
|
314
|
+
...(opts.payer && { payer: opts.payer }),
|
|
315
|
+
...(opts.payee && { payee: opts.payee }),
|
|
316
|
+
},
|
|
317
|
+
operations: [
|
|
318
|
+
...(opts.previousOperations ? opts.previousOperations : []),
|
|
319
|
+
operation,
|
|
320
|
+
...(opts.nextOperations ? opts.nextOperations : []),
|
|
321
|
+
],
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (opts.sendAbis) {
|
|
325
|
+
if (!opts.abis) opts.abis = {};
|
|
326
|
+
const contractId = this.getId();
|
|
327
|
+
if (!opts.abis[contractId]) opts.abis[contractId] = this.abi;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// return result if the transaction will not be broadcasted
|
|
331
|
+
if (!opts.sendTransaction) {
|
|
332
|
+
const noWait = () => {
|
|
333
|
+
throw new Error("This transaction was not broadcasted");
|
|
334
|
+
};
|
|
335
|
+
if (opts.signTransaction)
|
|
336
|
+
tx = await this.signer.signTransaction(
|
|
337
|
+
tx,
|
|
338
|
+
opts.sendAbis ? opts.abis : undefined
|
|
339
|
+
);
|
|
340
|
+
return { operation, transaction: { ...tx, wait: noWait } };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const { transaction, receipt } = await this.signer.sendTransaction(
|
|
344
|
+
tx,
|
|
345
|
+
opts
|
|
346
|
+
);
|
|
347
|
+
return { operation, transaction, receipt };
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Function to deploy a new smart contract.
|
|
355
|
+
* The Bytecode must be defined in the constructor of the class
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* const privateKey = "f186a5de49797bfd52dc42505c33d75a46822ed5b60046e09d7c336242e20200";
|
|
359
|
+
* const provider = new Provider(["http://api.koinos.io:8080"]);
|
|
360
|
+
* const signer = new Signer({ privateKey, provider });
|
|
361
|
+
* const bytecode = new Uint8Array([1, 2, 3, 4]);
|
|
362
|
+
* const contract = new Contract({ signer, provider, bytecode });
|
|
363
|
+
* const { transaction, receipt } = await contract.deploy();
|
|
364
|
+
* console.log(receipt);
|
|
365
|
+
* // wait to be mined
|
|
366
|
+
* const blockNumber = await transaction.wait();
|
|
367
|
+
* console.log(`Contract uploaded in block number ${blockNumber}`);
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @example using options
|
|
371
|
+
* ```ts
|
|
372
|
+
* const { transaction, receipt } = await contract.deploy({
|
|
373
|
+
* // contract options
|
|
374
|
+
* abi: "CssCChRrb2lub3Mvb3B0aW9ucy5wc...",
|
|
375
|
+
* authorizesCallContract: true,
|
|
376
|
+
* authorizesTransactionApplication: true,
|
|
377
|
+
* authorizesUploadContract: true,
|
|
378
|
+
*
|
|
379
|
+
* // transaction options
|
|
380
|
+
* chainId: "EiB-hw5ABo-EXy6fGDd1Iq3gbAenxQ4Qe60pRbEVMVrR9A==",
|
|
381
|
+
* rcLimit: "100000000",
|
|
382
|
+
* nonce: "OAI=",
|
|
383
|
+
* payer: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
384
|
+
* payee: "1Gvqdo9if6v6tFomEuTuMWP1D7H7U9yksb",
|
|
385
|
+
*
|
|
386
|
+
* // sign and broadcast
|
|
387
|
+
* signTransaction: true,
|
|
388
|
+
* sendTransaction: true,
|
|
389
|
+
* broadcast: true,
|
|
390
|
+
* });
|
|
391
|
+
* console.log(receipt);
|
|
392
|
+
* // wait to be mined
|
|
393
|
+
* const blockNumber = await transaction.wait();
|
|
394
|
+
* console.log(`Contract uploaded in block number ${blockNumber}`);
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
async deploy(options?: DeployOptions): Promise<{
|
|
398
|
+
operation: OperationJson;
|
|
399
|
+
transaction?: TransactionJsonWait;
|
|
400
|
+
receipt?: TransactionReceipt;
|
|
401
|
+
}> {
|
|
402
|
+
if (!this.signer) throw new Error("signer not found");
|
|
403
|
+
if (!this.bytecode) throw new Error("bytecode not found");
|
|
404
|
+
const opts: DeployOptions = {
|
|
405
|
+
...this.options,
|
|
406
|
+
...options,
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const contractId = this.getId();
|
|
410
|
+
|
|
411
|
+
const operation = {
|
|
412
|
+
upload_contract: {
|
|
413
|
+
contract_id: contractId,
|
|
414
|
+
bytecode: encodeBase64url(this.bytecode),
|
|
415
|
+
...(opts.abi && { abi: opts.abi }),
|
|
416
|
+
...(opts.authorizesCallContract && {
|
|
417
|
+
authorizes_call_contract: opts.authorizesCallContract,
|
|
418
|
+
}),
|
|
419
|
+
...(opts.authorizesTransactionApplication && {
|
|
420
|
+
authorizes_transaction_application:
|
|
421
|
+
opts.authorizesTransactionApplication,
|
|
422
|
+
}),
|
|
423
|
+
...(opts.authorizesUploadContract && {
|
|
424
|
+
authorizes_upload_contract: opts.authorizesUploadContract,
|
|
425
|
+
}),
|
|
426
|
+
},
|
|
427
|
+
} as OperationJson;
|
|
428
|
+
|
|
429
|
+
if (opts.onlyOperation) {
|
|
430
|
+
return { operation };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let tx = await this.signer.prepareTransaction({
|
|
434
|
+
header: {
|
|
435
|
+
...(opts.chainId && { chain_id: opts.chainId }),
|
|
436
|
+
...(opts.rcLimit && { rc_limit: opts.rcLimit }),
|
|
437
|
+
...(opts.nonce && { nonce: opts.nonce }),
|
|
438
|
+
...(opts.payer && { payer: opts.payer }),
|
|
439
|
+
...(opts.payee && { payee: opts.payee }),
|
|
440
|
+
},
|
|
441
|
+
operations: [
|
|
442
|
+
...(opts.previousOperations ? opts.previousOperations : []),
|
|
443
|
+
operation,
|
|
444
|
+
...(opts.nextOperations ? opts.nextOperations : []),
|
|
445
|
+
],
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// return result if the transaction will not be broadcasted
|
|
449
|
+
if (!opts.sendTransaction) {
|
|
450
|
+
const noWait = () => {
|
|
451
|
+
throw new Error("This transaction was not broadcasted");
|
|
452
|
+
};
|
|
453
|
+
if (opts.signTransaction)
|
|
454
|
+
tx = await this.signer.signTransaction(
|
|
455
|
+
tx,
|
|
456
|
+
opts.sendAbis ? opts.abis : undefined
|
|
457
|
+
);
|
|
458
|
+
return { operation, transaction: { ...tx, wait: noWait } };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const { transaction, receipt } = await this.signer.sendTransaction(
|
|
462
|
+
tx,
|
|
463
|
+
opts
|
|
464
|
+
);
|
|
465
|
+
return { operation, transaction, receipt };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Encondes a contract operation using Koinos serialization
|
|
470
|
+
* and taking the contract entries as reference to build it
|
|
471
|
+
* @param op - Operation to encode
|
|
472
|
+
* @returns Operation encoded
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* const opEncoded = contract.encodeOperation({
|
|
476
|
+
* name: "transfer",
|
|
477
|
+
* args: {
|
|
478
|
+
* from: "12fN2CQnuJM8cMnWZ1hPtM4knjLME8E4PD",
|
|
479
|
+
* to: "172AB1FgCsYrRAW5cwQ8KjadgxofvgPFd6",
|
|
480
|
+
* value: "1000",
|
|
481
|
+
* }
|
|
482
|
+
* });
|
|
483
|
+
*
|
|
484
|
+
* console.log(opEncoded);
|
|
485
|
+
* // {
|
|
486
|
+
* // call_contract: {
|
|
487
|
+
* // contract_id: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
488
|
+
* // entry_point: 670398154,
|
|
489
|
+
* // args: "ChkAEjl6vrl55V2Oym_rzsnMxIqBoie9PHmMEhkAQgjT1UACatdFY3e5QRkyG7OAzwcCCIylGOgH",
|
|
490
|
+
* // }
|
|
491
|
+
* // }
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
async encodeOperation(op: DecodedOperationJson): Promise<OperationJson> {
|
|
495
|
+
if (!this.abi || !this.abi.methods || !this.abi.methods[op.name])
|
|
496
|
+
throw new Error(`Operation ${op.name} unknown`);
|
|
497
|
+
if (!this.serializer) throw new Error("Serializer is not defined");
|
|
498
|
+
const method = this.abi.methods[op.name];
|
|
499
|
+
|
|
500
|
+
let bufferArguments = new Uint8Array(0);
|
|
501
|
+
if (method.argument) {
|
|
502
|
+
if (!op.args)
|
|
503
|
+
throw new Error(`No arguments defined for type '${method.argument}'`);
|
|
504
|
+
bufferArguments = await this.serializer.serialize(
|
|
505
|
+
op.args,
|
|
506
|
+
method.argument
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
call_contract: {
|
|
512
|
+
contract_id: this.getId(),
|
|
513
|
+
entry_point: method.entry_point,
|
|
514
|
+
args: encodeBase64url(bufferArguments),
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Decodes a contract operation to be human readable
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* const opDecoded = contract.decodeOperation({
|
|
524
|
+
* call_contract: {
|
|
525
|
+
* contract_id: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
526
|
+
* entry_point: 0x27f576ca,
|
|
527
|
+
* args: "ChkAEjl6vrl55V2Oym_rzsnMxIqBoie9PHmMEhkAQgjT1UACatdFY3e5QRkyG7OAzwcCCIylGOgH",
|
|
528
|
+
* }
|
|
529
|
+
* });
|
|
530
|
+
* console.log(opDecoded);
|
|
531
|
+
* // {
|
|
532
|
+
* // name: "transfer",
|
|
533
|
+
* // args: {
|
|
534
|
+
* // from: "12fN2CQnuJM8cMnWZ1hPtM4knjLME8E4PD",
|
|
535
|
+
* // to: "172AB1FgCsYrRAW5cwQ8KjadgxofvgPFd6",
|
|
536
|
+
* // value: "1000",
|
|
537
|
+
* // },
|
|
538
|
+
* // }
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
async decodeOperation(op: OperationJson): Promise<DecodedOperationJson> {
|
|
542
|
+
if (!this.abi || !this.abi.methods)
|
|
543
|
+
throw new Error("Methods are not defined");
|
|
544
|
+
if (!this.serializer) throw new Error("Serializer is not defined");
|
|
545
|
+
if (!op.call_contract)
|
|
546
|
+
throw new Error("Operation is not CallContractOperation");
|
|
547
|
+
if (op.call_contract.contract_id !== this.getId())
|
|
548
|
+
throw new Error(
|
|
549
|
+
`Invalid contract id. Expected: ${this.getId()}. Received: ${
|
|
550
|
+
op.call_contract.contract_id
|
|
551
|
+
}`
|
|
552
|
+
);
|
|
553
|
+
for (let i = 0; i < Object.keys(this.abi.methods).length; i += 1) {
|
|
554
|
+
const opName = Object.keys(this.abi.methods)[i];
|
|
555
|
+
const method = this.abi.methods[opName];
|
|
556
|
+
if (op.call_contract.entry_point === method.entry_point) {
|
|
557
|
+
if (!method.argument) return { name: opName };
|
|
558
|
+
return {
|
|
559
|
+
name: opName,
|
|
560
|
+
args: await this.serializer.deserialize(
|
|
561
|
+
op.call_contract.args,
|
|
562
|
+
method.argument
|
|
563
|
+
),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
throw new Error(`Unknown method id ${op.call_contract.entry_point}`);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Decode an event received in a receipt
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* const contract = new Contract({
|
|
576
|
+
* id: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
577
|
+
* abi: utils.tokenAbi,
|
|
578
|
+
* });
|
|
579
|
+
* const event = {
|
|
580
|
+
* sequence: 1,
|
|
581
|
+
* source: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
582
|
+
* name: "koinos.contracts.token.mint_event",
|
|
583
|
+
* data: "ChkAxjdqxuwS-B50lPQ-lqhRBA3bf2b2ooAHENrw3Ek=",
|
|
584
|
+
* impacted: ["1K55BRw87nd64a7aiRarp6DLGRzYvoJo8J"],
|
|
585
|
+
* };
|
|
586
|
+
* const eventDecoded = await contract.decodeEvent(event);
|
|
587
|
+
* console.log(eventDecoded);
|
|
588
|
+
* // {
|
|
589
|
+
* // sequence: 1,
|
|
590
|
+
* // source: "19JntSm8pSNETT9aHTwAUHC5RMoaSmgZPJ",
|
|
591
|
+
* // name: "koinos.contracts.token.mint_event",
|
|
592
|
+
* // data: "ChkAxjdqxuwS-B50lPQ-lqhRBA3bf2b2ooAHENrw3Ek=",
|
|
593
|
+
* // impacted: ["1K55BRw87nd64a7aiRarp6DLGRzYvoJo8J"],
|
|
594
|
+
* // args: {
|
|
595
|
+
* // to: "1K55BRw87nd64a7aiRarp6DLGRzYvoJo8J",
|
|
596
|
+
* // value: "154613850",
|
|
597
|
+
* // },
|
|
598
|
+
* // }
|
|
599
|
+
* ```
|
|
600
|
+
*/
|
|
601
|
+
async decodeEvent(event: EventData): Promise<DecodedEventData> {
|
|
602
|
+
if (!this.serializer) throw new Error("Serializer is not defined");
|
|
603
|
+
let typeName = event.name;
|
|
604
|
+
if (this.abi && this.abi.events && this.abi.events[event.name]) {
|
|
605
|
+
typeName = this.abi.events[event.name].argument as string;
|
|
606
|
+
}
|
|
607
|
+
const args = typeName
|
|
608
|
+
? await this.serializer.deserialize(event.data, typeName)
|
|
609
|
+
: {};
|
|
610
|
+
return { ...event, args };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export default Contract;
|