-
Notifications
You must be signed in to change notification settings - Fork 166
/
Query.ts
707 lines (647 loc) · 19.8 KB
/
Query.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
import { DEFAULT_REQ } from "../consts";
import { Transaction, TransactionJson, Signer, SignerJson } from "../tx";
import {
ContractManifestJson,
ContractParam,
StackItemJson,
NEFJson,
} from "../sc";
import { BlockJson, Validator, BlockHeaderJson } from "../types";
import { HexString } from "../u";
import { isEqual } from "lodash";
export type JsonRpcParams = unknown[] | Record<string | number, unknown>;
export type BooleanLikeParam = 0 | 1 | boolean;
export interface QueryLike<T extends JsonRpcParams> {
method: string;
params: T;
id: number;
jsonrpc: "2.0";
}
export interface RPCResponse<T> {
jsonrpc: string;
id: number;
result: T;
error?: RPCErrorResponse;
}
export interface RPCErrorResponse {
code: number;
message: string;
}
export interface ApplicationLogJson {
txid: string;
executions: {
trigger: string;
vmstate: string;
gasconsumed: string;
stack?: StackItemJson[];
notifications: {
contract: string;
eventname: string;
state: StackItemJson;
}[];
}[];
}
/**
* Result from calling invokescript or invokefunction.
*/
export interface InvokeResult {
/** The script that is sent for execution on the blockchain as a base64 string. */
script: string;
/** State of VM on exit. HALT means a successful exit while FAULT means exit with error. */
state: "HALT" | "FAULT";
/** Amount of gas consumed up to the point of stopping in the VM. If state is FAULT, this value is not representative of the amount of gas it will consume if it somehow succeeds on the blockchain.
* This is a decimal value.
*/
gasconsumed: string;
/** A human-readable string clarifying the exception that occurred. Only available when state is "FAULT". */
exception: string | null;
stack: StackItemJson[];
/** A ready to send transaction that wraps the script.
* Only available when signers are provided and the sender's private key is open in the RPC node.
* Formatted in base64-encoding.
*/
tx?: string;
}
export interface GetContractStateResult {
id: number;
updatecounter: number;
/** 0x prefixed hexstring */
hash: string;
/** Base64 encoded string */
nef: NEFJson;
manifest: ContractManifestJson;
}
export interface NativeContractState {
id: number;
hash: string;
nef: NEFJson;
manifest: ContractManifestJson;
updatehistory: number[];
}
export interface GetNep11BalancesResult {
/* Base58-encoded string */
address: string;
balance: {
/* 0x-prefixed scripthash of the contract. */
assethash: string;
tokens: { tokenid: string; amount: string; lastupdatedblock: number }[];
}[];
}
export interface GetNep11TransfersResult {
address: string;
sent: Nep11TransferEvent[];
received: Nep11TransferEvent[];
}
export interface Nep11TransferEvent {
timestamp: number;
assethash: string;
transferaddress: string;
amount: string;
blockindex: number;
transfernotifyindex: number;
txhash: string;
tokenid: string;
}
export interface GetNep17TransfersResult {
sent: Nep17TransferEvent[];
received: Nep17TransferEvent[];
address: string;
}
export interface Nep17TransferEvent {
timestamp: number;
assethash: string;
transferaddress: string;
amount: string;
blockindex: number;
transfernotifyindex: number;
txhash: string;
}
export interface GetNep17BalancesResult {
address: string;
balance: {
assethash: string;
amount: string;
lastupdatedblock: number;
}[];
}
export interface GetPeersResult {
unconnected: NodePeer[];
bad: NodePeer[];
connected: NodePeer[];
}
export interface GetRawMemPoolResult {
height: number;
verified: string[];
unverified: string[];
}
export interface GetRawTransactionResult extends TransactionJson {
hash: string;
blockhash: string;
/** Number of blocks that has been confirmed between blocktime and now. */
confirmations: number;
/** Unix timestamp in milliseconds. */
blocktime: number;
vm_state: "HALT" | "FAULT";
}
export interface GetVersionResult {
tcpport: number;
wsport: number;
nonce: number;
useragent: string;
protocol: {
addressversion: number;
network: number;
validatorscount: number;
msperblock: number;
maxtraceableblocks: number;
maxvaliduntilblockincrement: number;
maxtransactionsperblock: number;
memorypoolmaxtransactions: number;
initialgasdistribution: number;
};
}
export interface NodePeer {
/** IPv4 Address */
address: string;
port: number;
}
export interface CliPlugin {
name: string;
version: string;
interfaces: string[];
}
export interface SendResult {
/** 0x prefixed hexstring */
hash: string;
}
export interface ValidateAddressResult {
address: string;
isvalid: boolean;
}
export interface GetUnclaimedGasResult {
unclaimed: string;
address: string;
}
function transformInputTransaction(
tx: Transaction | HexString | string
): string {
return tx instanceof Transaction
? HexString.fromHex(tx.serialize(true)).toBase64()
: tx instanceof HexString
? tx.toBase64()
: tx;
}
/**
* Type guard for narrowing down to an object.
* @returns
*/
function isJsonRpcParamRecord(
i?: JsonRpcParams
): i is Record<string | number, unknown> {
return i !== undefined && i !== null && typeof i === "object";
}
/**
* A Query object helps us to construct and record requests for the Neo node RPC. For each RPC endpoint, the equivalent static method is camelcased. Each Query object can only be used once.
*
* @example
* const q1 = Query.getBestBlockHash();
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class Query<TParams extends JsonRpcParams, TResponse> {
/**
* Query returning the network fee required for a given transaction.
*/
public static calculateNetworkFee(
tx: Transaction | HexString | string
): Query<[string], { networkfee: string }> {
const base64Tx = transformInputTransaction(tx);
return new Query({ method: "calculatenetworkfee", params: [base64Tx] });
}
/**
* This Query returns the hash of the highest block.
*/
public static getBestBlockHash(): Query<[], string> {
return new Query({
method: "getbestblockhash",
});
}
/**
* Query returning the application log.
*/
public static getApplicationLog(
hash: string
): Query<[string], ApplicationLogJson> {
return new Query({
method: "getapplicationlog",
params: [hash],
});
}
/**
* This Query returns the specified block either as a hexstring or human readable JSON.
* @param indexOrHash - height or hash of block.
* @param verbose - 0 for hexstring, 1 for JSON. Defaults to 0.
*/
public static getBlock(
indexOrHash: number | string,
verbose: 1 | true
): Query<[number | string, 1 | true], BlockJson>;
public static getBlock(
indexOrHash: number | string,
verbose?: 0 | false
): Query<[number | string, 0 | false], string>;
public static getBlock(
indexOrHash: number | string,
verbose: BooleanLikeParam = 0
): Query<[number | string, BooleanLikeParam], string | BlockJson> {
return new Query({
method: "getblock",
params: [indexOrHash, verbose],
});
}
/**
* This Query returns the current block height.
*/
public static getBlockCount(): Query<[], number> {
return new Query({
method: "getblockcount",
params: [],
});
}
/**
* This Query returns the hash of a specific block.
* @param index - height of block.
*/
public static getBlockHash(index: number): Query<[number], string> {
return new Query({
method: "getblockhash",
params: [index],
});
}
/**
* This Query Returns the corresponding block header information according to the specified script hash.
* @param indexOrHash - height or hash of block.
* @param verbose - Optional, the default value of verbose is 0. When verbose is 0, the serialized information of the block is returned, represented by a hexadecimal string. If you need to get detailed information, you will need to use the SDK for deserialization. When verbose is 1, detailed information of the corresponding block in Json format string, is returned.
*/
public static getBlockHeader(
indexOrHash: string | number,
verbose: 1 | true
): Query<[string | number, 1 | true], BlockHeaderJson>;
public static getBlockHeader(
indexOrHash: string | number,
verbose?: 0 | false
): Query<[string | number, 0 | false], string>;
public static getBlockHeader(
indexOrHash: string | number,
verbose: BooleanLikeParam = 0
): Query<[string | number, BooleanLikeParam], string | BlockHeaderJson> {
return new Query({
method: "getblockheader",
params: [indexOrHash, verbose],
});
}
/**
* This Query returns the list of public keys in the current committee.
*/
public static getCommittee(): Query<[], string[]> {
return new Query({
method: "getcommittee",
});
}
/**
* This Query returns the number of other nodes that this node is connected to.
*/
public static getConnectionCount(): Query<[], number> {
return new Query({
method: "getconnectioncount",
});
}
/**
* This Query returns information about the smart contract registered at the specific hash.
* @param scriptHash - hash of contract
*/
public static getContractState(
scriptHash: string
): Query<[string], GetContractStateResult> {
return new Query({
method: "getcontractstate",
params: [scriptHash],
});
}
/**
* This Query returns all the native contracts state.
*/
public static getNativeContracts(): Query<[], NativeContractState[]> {
return new Query({
method: "getnativecontracts",
});
}
public static getNep11Balances(
accountIdentifier: string
): Query<[string], GetNep11BalancesResult> {
return new Query({
method: "getnep11balances",
params: [accountIdentifier],
});
}
public static getNep11Properties(
contractHash: string,
tokenId: string
): Query<[string, string], Record<string, unknown>> {
return new Query({
method: "getnep11properties",
params: [contractHash, tokenId],
});
}
public static getNep11Transfers(
accountIdentifer: string,
startTime?: string,
endTime?: string
): Query<[string, string?, string?], GetNep11TransfersResult> {
const params: [string, string?, string?] = [accountIdentifer];
if (startTime) params.push(startTime);
if (endTime) params.push(endTime);
return new Query({
method: "getnep17transfers",
params,
});
}
/**
* Query returning the Nep17 transfer history of an account. Defaults of 1 week of history.
* @param accountIdentifer - address or scriptHash of account
* @param startTime - millisecond Unix timestamp
* @param endTime - millisecond Unix timestamp
*/
public static getNep17Transfers(
accountIdentifer: string,
startTime?: string,
endTime?: string
): Query<[string, string?, string?], GetNep17TransfersResult> {
const params: [string, string?, string?] = [accountIdentifer];
if (startTime) params.push(startTime);
if (endTime) params.push(endTime);
return new Query({
method: "getnep17transfers",
params,
});
}
/**
* Query returning the Nep17 balance of an account.
* @param accountIdentifer - address or scriptHash of account
*/
public static getNep17Balances(
accountIdentifer: string
): Query<[string], GetNep17BalancesResult> {
return new Query({
method: "getnep17balances",
params: [accountIdentifer],
});
}
/**
* This Query returns the list of nodes that this node is connected to.
*/
public static getPeers(): Query<[], GetPeersResult> {
return new Query({
method: "getpeers",
});
}
/**
* This Query returns the transaction hashes of the transactions confirmed or unconfirmed.
* @param shouldGetUnverified - Optional. Default is 0.
*
* shouldGetUnverified = 0, get confirmed transaction hashes
*
* shouldGetUnverified = 1, get current block height and confirmed and unconfirmed tx hash
*/
public static getRawMemPool(
shouldGetUnverified?: BooleanLikeParam
): Query<[0 | false], string[]>;
public static getRawMemPool(
shouldGetUnverified: 1 | true
): Query<[1], GetRawMemPoolResult>;
public static getRawMemPool(
shouldGetUnverified: BooleanLikeParam = 0
): Query<[BooleanLikeParam], string[] | GetRawMemPoolResult> {
return new Query({
method: "getrawmempool",
params: [shouldGetUnverified],
});
}
/**
* This Query returns information about a specific transaction in either hexstring or human readable JSON.
* @param txid - hash of the specific transaction.
* @param verbose - 0 for hexstring, 1 for JSON. Defaults to 0.
*/
public static getRawTransaction(
txid: string,
verbose?: 0 | false
): Query<[string, 0 | false], string>;
public static getRawTransaction(
txid: string,
verbose: 1 | true
): Query<[string, 1 | true], GetRawTransactionResult>;
public static getRawTransaction(
txid: string,
verbose: BooleanLikeParam = 0
): Query<[string, BooleanLikeParam], string | GetRawTransactionResult> {
return new Query({
method: "getrawtransaction",
params: [txid, verbose],
});
}
/**
* This Query returns the value stored at the specific key under a specific contract in base64 format.
* @param scriptHash - hash of contract.
* @param key - the storage key in as hex string
*/
public static getStorage(
scriptHash: string,
key: string
): Query<[string, string], string> {
return new Query({
method: "getstorage",
params: [scriptHash, HexString.fromHex(key).toBase64()],
});
}
/**
* This Query returns the block index in which the transaction is found.
* @param txid - hash of the specific transaction.
*/
public static getTransactionHeight(txid: string): Query<[string], number> {
return new Query({
method: "gettransactionheight",
params: [txid],
});
}
/**
* Gets the list of candidates available for voting.
* @returns list of validators
*/
public static getNextBlockValidators(): Query<[], Validator[]> {
return new Query({
method: "getnextblockvalidators",
});
}
/**
* Returns the node version.
*/
public static getVersion(): Query<[], GetVersionResult> {
return new Query({
method: "getversion",
});
}
/**
* Invoke the verification portion of a contract.
* @param scriptHash - hash of contract to test
* @param args - arguments to pass
* @param signers - Signers to be included in transaction
*/
public static invokeContractVerify(
scriptHash: string,
args: unknown[] = [],
signers: (Signer | SignerJson)[] = []
): Query<[string, unknown[], SignerJson[]], InvokeResult> {
return new Query({
method: "invokecontractverify",
params: [
scriptHash,
args.map((a) => (a instanceof ContractParam ? a.toJson() : a)),
signers.map((s) => (s instanceof Signer ? s.toJson() : s)),
],
});
}
/**
* This Query invokes the VM to run the specific contract with the provided operation and params. Do note that this function only suits contracts with a Main(string, args[]) entry method.
* @param scriptHash - hash of contract to test.
* @param operation - name of operation to call (first argument)
* @param params - parameters to pass (second argument). ContractParam objects will be exported to JSON format.
* @param signers - scripthashes of witnesses that should sign the transaction containing this script. Signers will be exported to JSON format.
*/
public static invokeFunction(
scriptHash: string,
operation: string,
params: unknown[] = [],
signers: (Signer | SignerJson)[] = []
): Query<[string, string, unknown[], SignerJson[]], InvokeResult> {
return new Query({
method: "invokefunction",
params: [
scriptHash,
operation,
params.map((p) => (p instanceof ContractParam ? p.toJson() : p)),
signers.map((s) => (s instanceof Signer ? s.toJson() : s)),
],
});
}
/**
* This Query runs the specific script through the VM.
* @param script - base64-encoded string.
* @param signers - scripthashes of witnesses that should sign the transaction containing this script.
*/
public static invokeScript(
script: string | HexString,
signers: (Signer | SignerJson)[] = []
): Query<[string, SignerJson[]], InvokeResult> {
return new Query({
method: "invokescript",
params: [
script instanceof HexString ? script.toBase64() : script,
signers.map((s) => (s instanceof Signer ? s.toJson() : s)),
],
});
}
/**
* This Query returns a list of plugins loaded by the node.
*/
public static listPlugins(): Query<[], CliPlugin[]> {
return new Query({
method: "listplugins",
params: [],
});
}
/**
* This Query transmits the specific transaction to the node. On success, the node will return the transaction hash.
* @param transaction - transaction as a Transaction object or base64 hexstring.
*/
public static sendRawTransaction(
transaction: Transaction | string | HexString
): Query<[string], SendResult> {
const base64Tx = transformInputTransaction(transaction);
return new Query({
method: "sendrawtransaction",
params: [base64Tx],
});
}
/**
* This Query submits a block for processing.
* @param block - a serialized block
*/
public static submitBlock(block: string): Query<[string], SendResult> {
return new Query({
method: "submitblock",
params: [block],
});
}
/**
* This Query submits an address for validation.
* @param addr - address to validate.
*/
public static validateAddress(
addr: string
): Query<[string], ValidateAddressResult> {
return new Query({
method: "validateaddress",
params: [addr],
});
}
/**
* This Query returns the available unclaimed bonus GAS for a NEO address
* @param addr - a NEO address
*/
public static getUnclaimedGas(
addr: string
): Query<[string], GetUnclaimedGasResult> {
return new Query({
method: "getunclaimedgas",
params: [addr],
});
}
public id: number;
public method: string;
public params: TParams;
public constructor(req: Partial<QueryLike<TParams>>) {
this.id = req?.id ?? DEFAULT_REQ.id;
this.method = req?.method ?? DEFAULT_REQ.method;
this.params = (req?.params ?? []) as TParams;
}
public get [Symbol.toStringTag](): string {
return "Query";
}
public export(): QueryLike<TParams> {
return {
params: this.params,
jsonrpc: "2.0",
id: this.id,
method: this.method,
};
}
public equals(other: Partial<QueryLike<JsonRpcParams>>): boolean {
if (this.id !== other.id && this.method !== other.method) {
return false;
}
if (Array.isArray(this.params) && Array.isArray(other.params)) {
const otherParams = other.params;
return (
this.params.length === otherParams.length &&
this.params.every((val, ind) => otherParams[ind] === val)
);
}
if (
isJsonRpcParamRecord(this.params) &&
isJsonRpcParamRecord(other.params)
) {
return isEqual(this.params, other.params);
}
return false;
}
}
export default Query;