@xelis/sdk 0.1.0
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/README.md +44 -0
- package/dist/README.md +44 -0
- package/dist/config/nodes.d.ts +21 -0
- package/dist/config/nodes.js +14 -0
- package/dist/daemon/rpc.d.ts +30 -0
- package/dist/daemon/rpc.js +92 -0
- package/dist/daemon/types.d.ts +132 -0
- package/dist/daemon/types.js +33 -0
- package/dist/daemon/websocket.d.ts +49 -0
- package/dist/daemon/websocket.js +237 -0
- package/dist/package.json +31 -0
- package/dist/react/context.d.ts +22 -0
- package/dist/react/context.js +57 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# XELIS-JS-SDK
|
|
2
|
+
|
|
3
|
+
Xelis software development kit for JS.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Install library with NPM.
|
|
8
|
+
|
|
9
|
+
`npm i @xelis/sdk`
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Import library and start working :).
|
|
14
|
+
|
|
15
|
+
Use http/rpc connection.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { DEV_NODE_RPC } from '@xelis/sdk/config/nodes'
|
|
19
|
+
import DaemonRPC from '@xelis/sdk/daemon/rpc'
|
|
20
|
+
|
|
21
|
+
const main = async () => {
|
|
22
|
+
const daemonRPC = new DaemonRPC(DEV_NODE_RPC)
|
|
23
|
+
const info = await daemonRPC.getInfo()
|
|
24
|
+
console.log(info)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
main()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use websocket connection.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { DEV_NODE_RPC } from '@xelis/sdk/config/nodes'
|
|
34
|
+
import DaemonWS from '@xelis/sdk/daemon/websocket'
|
|
35
|
+
|
|
36
|
+
const main = async () => {
|
|
37
|
+
const daemonWS = new DaemonWS()
|
|
38
|
+
await daemonWS.connect(DEV_NODE_RPC)
|
|
39
|
+
const info = await daemonWS.getInfo()
|
|
40
|
+
console.log(info)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
main()
|
|
44
|
+
```
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# XELIS-JS-SDK
|
|
2
|
+
|
|
3
|
+
Xelis software development kit for JS.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Install library with NPM.
|
|
8
|
+
|
|
9
|
+
`npm i @xelis/sdk`
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Import library and start working :).
|
|
14
|
+
|
|
15
|
+
Use http/rpc connection.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { DEV_NODE_RPC } from '@xelis/sdk/config/nodes'
|
|
19
|
+
import DaemonRPC from '@xelis/sdk/daemon/rpc'
|
|
20
|
+
|
|
21
|
+
const main = async () => {
|
|
22
|
+
const daemonRPC = new DaemonRPC(DEV_NODE_RPC)
|
|
23
|
+
const info = await daemonRPC.getInfo()
|
|
24
|
+
console.log(info)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
main()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use websocket connection.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { DEV_NODE_RPC } from '@xelis/sdk/config/nodes'
|
|
34
|
+
import DaemonWS from '@xelis/sdk/daemon/websocket'
|
|
35
|
+
|
|
36
|
+
const main = async () => {
|
|
37
|
+
const daemonWS = new DaemonWS()
|
|
38
|
+
await daemonWS.connect(DEV_NODE_RPC)
|
|
39
|
+
const info = await daemonWS.getInfo()
|
|
40
|
+
console.log(info)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
main()
|
|
44
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const NODE_URL = "node.xelis.io";
|
|
2
|
+
export declare const TESTNET_NODE_URL = "testnet-node.xelis.io";
|
|
3
|
+
export declare const DEV_NODE_URL = "dev-node.xelis.io";
|
|
4
|
+
export declare const NODE_RPC: string;
|
|
5
|
+
export declare const TESTNET_NODE_RPC: string;
|
|
6
|
+
export declare const DEV_NODE_RPC: string;
|
|
7
|
+
export declare const NODE_WS: string;
|
|
8
|
+
export declare const TESTNET_NODE_WS: string;
|
|
9
|
+
export declare const DEV_NODE_WS: string;
|
|
10
|
+
declare const _default: {
|
|
11
|
+
NODE_URL: string;
|
|
12
|
+
TESTNET_NODE_URL: string;
|
|
13
|
+
DEV_NODE_URL: string;
|
|
14
|
+
NODE_RPC: string;
|
|
15
|
+
TESTNET_NODE_RPC: string;
|
|
16
|
+
DEV_NODE_RPC: string;
|
|
17
|
+
NODE_WS: string;
|
|
18
|
+
TESTNET_NODE_WS: string;
|
|
19
|
+
DEV_NODE_WS: string;
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const NODE_URL = `node.xelis.io`;
|
|
2
|
+
export const TESTNET_NODE_URL = `testnet-node.xelis.io`;
|
|
3
|
+
export const DEV_NODE_URL = `dev-node.xelis.io`;
|
|
4
|
+
export const NODE_RPC = `https://${NODE_URL}/json_rpc`;
|
|
5
|
+
export const TESTNET_NODE_RPC = `https://${TESTNET_NODE_URL}/json_rpc`;
|
|
6
|
+
export const DEV_NODE_RPC = `https://${DEV_NODE_URL}/json_rpc`;
|
|
7
|
+
export const NODE_WS = `wss://${NODE_URL}/ws`;
|
|
8
|
+
export const TESTNET_NODE_WS = `wss://${TESTNET_NODE_URL}/ws`;
|
|
9
|
+
export const DEV_NODE_WS = `wss://${DEV_NODE_URL}/ws`;
|
|
10
|
+
export default {
|
|
11
|
+
NODE_URL, TESTNET_NODE_URL, DEV_NODE_URL,
|
|
12
|
+
NODE_RPC, TESTNET_NODE_RPC, DEV_NODE_RPC,
|
|
13
|
+
NODE_WS, TESTNET_NODE_WS, DEV_NODE_WS
|
|
14
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Balance, Block, TopoHeightRangeParams, GetInfoResult, HeightRangeParams, GetLastBalanceResult, P2PStatusResult, RPCMethod, RPCResponse, Transaction, BalanceParams } from './types';
|
|
2
|
+
declare class RPC {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
constructor(endpoint: string);
|
|
5
|
+
fetch<T>(method: RPCMethod, params?: any): Promise<RPCResponse<T>>;
|
|
6
|
+
getInfo(): Promise<RPCResponse<GetInfoResult>>;
|
|
7
|
+
getHeight(): Promise<RPCResponse<number>>;
|
|
8
|
+
getTopoHeight(): Promise<RPCResponse<number>>;
|
|
9
|
+
getStableHeight(): Promise<RPCResponse<number>>;
|
|
10
|
+
getBlockTemplate(address: string): Promise<RPCResponse<string>>;
|
|
11
|
+
getBlockAtTopoHeight(topoHeight: number): Promise<RPCResponse<Block>>;
|
|
12
|
+
getBlocksAtHeight(height: number): Promise<RPCResponse<Block[]>>;
|
|
13
|
+
getBlockByHash(hash: string): Promise<RPCResponse<Block>>;
|
|
14
|
+
getTopBlock(): Promise<RPCResponse<Block>>;
|
|
15
|
+
getNonce(address: string): Promise<RPCResponse<number>>;
|
|
16
|
+
getLastBalance(params: BalanceParams): Promise<RPCResponse<GetLastBalanceResult>>;
|
|
17
|
+
getBalanceAtTopoHeight(params: BalanceParams): Promise<RPCResponse<Balance>>;
|
|
18
|
+
getAssets(): Promise<RPCResponse<string[]>>;
|
|
19
|
+
countTransactions(): Promise<RPCResponse<number>>;
|
|
20
|
+
getTips(): Promise<RPCResponse<string[]>>;
|
|
21
|
+
p2pStatus(): Promise<RPCResponse<P2PStatusResult>>;
|
|
22
|
+
getDAGOrder(params: TopoHeightRangeParams): Promise<RPCResponse<string[]>>;
|
|
23
|
+
getMemPool(): Promise<RPCResponse<Transaction[]>>;
|
|
24
|
+
getTransaction(hash: string): Promise<RPCResponse<Transaction>>;
|
|
25
|
+
getTransactions(txHashes: string[]): Promise<RPCResponse<Transaction[]>>;
|
|
26
|
+
getBlocks(params: TopoHeightRangeParams): Promise<RPCResponse<Block[]>>;
|
|
27
|
+
getBlocksRangeByTopoheight(params: TopoHeightRangeParams): Promise<RPCResponse<Block[]>>;
|
|
28
|
+
getBlocksRangeByHeight(params: HeightRangeParams): Promise<RPCResponse<Block[]>>;
|
|
29
|
+
}
|
|
30
|
+
export default RPC;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { RPCMethod } from './types';
|
|
2
|
+
class RPC {
|
|
3
|
+
constructor(endpoint) {
|
|
4
|
+
this.endpoint = endpoint;
|
|
5
|
+
}
|
|
6
|
+
async fetch(method, params) {
|
|
7
|
+
try {
|
|
8
|
+
const body = JSON.stringify({ id: 1, jsonrpc: '2.0', method: method, params });
|
|
9
|
+
const res = await fetch(this.endpoint, { method: `POST`, body });
|
|
10
|
+
if (res.ok) {
|
|
11
|
+
const data = await res.json();
|
|
12
|
+
return Promise.resolve(data);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
return Promise.reject(new Error(`${res.status} - ${res.statusText}`));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return Promise.reject(err);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
getInfo() {
|
|
23
|
+
return this.fetch(RPCMethod.GetInfo);
|
|
24
|
+
}
|
|
25
|
+
getHeight() {
|
|
26
|
+
return this.fetch(RPCMethod.GetHeight);
|
|
27
|
+
}
|
|
28
|
+
getTopoHeight() {
|
|
29
|
+
return this.fetch(RPCMethod.GetTopoHeight);
|
|
30
|
+
}
|
|
31
|
+
getStableHeight() {
|
|
32
|
+
return this.fetch(RPCMethod.GetStableHeight);
|
|
33
|
+
}
|
|
34
|
+
getBlockTemplate(address) {
|
|
35
|
+
return this.fetch(RPCMethod.GetBlockTemplate, { address });
|
|
36
|
+
}
|
|
37
|
+
getBlockAtTopoHeight(topoHeight) {
|
|
38
|
+
return this.fetch(RPCMethod.GetBlockAtTopoHeight, { topoheight: topoHeight });
|
|
39
|
+
}
|
|
40
|
+
getBlocksAtHeight(height) {
|
|
41
|
+
return this.fetch(RPCMethod.GetBlocksAtHeight, { height });
|
|
42
|
+
}
|
|
43
|
+
getBlockByHash(hash) {
|
|
44
|
+
return this.fetch(RPCMethod.GetBlockByHash, { hash });
|
|
45
|
+
}
|
|
46
|
+
getTopBlock() {
|
|
47
|
+
return this.fetch(RPCMethod.GetTopBlock);
|
|
48
|
+
}
|
|
49
|
+
getNonce(address) {
|
|
50
|
+
return this.fetch(RPCMethod.GetNonce, { address });
|
|
51
|
+
}
|
|
52
|
+
getLastBalance(params) {
|
|
53
|
+
return this.fetch(RPCMethod.GetLastBalance, params);
|
|
54
|
+
}
|
|
55
|
+
getBalanceAtTopoHeight(params) {
|
|
56
|
+
return this.fetch(RPCMethod.GetBalanceAtTopoHeight, params);
|
|
57
|
+
}
|
|
58
|
+
getAssets() {
|
|
59
|
+
return this.fetch(RPCMethod.GetAssets);
|
|
60
|
+
}
|
|
61
|
+
countTransactions() {
|
|
62
|
+
return this.fetch(RPCMethod.CountTransactions);
|
|
63
|
+
}
|
|
64
|
+
getTips() {
|
|
65
|
+
return this.fetch(RPCMethod.GetTips);
|
|
66
|
+
}
|
|
67
|
+
p2pStatus() {
|
|
68
|
+
return this.fetch(RPCMethod.P2PStatus);
|
|
69
|
+
}
|
|
70
|
+
getDAGOrder(params) {
|
|
71
|
+
return this.fetch(RPCMethod.GetDAGOrder, params);
|
|
72
|
+
}
|
|
73
|
+
getMemPool() {
|
|
74
|
+
return this.fetch(RPCMethod.GetMempool);
|
|
75
|
+
}
|
|
76
|
+
getTransaction(hash) {
|
|
77
|
+
return this.fetch(RPCMethod.GetTransaction, { hash });
|
|
78
|
+
}
|
|
79
|
+
getTransactions(txHashes) {
|
|
80
|
+
return this.fetch(RPCMethod.GetTransactions, { tx_hashes: txHashes });
|
|
81
|
+
}
|
|
82
|
+
getBlocks(params) {
|
|
83
|
+
return this.fetch(RPCMethod.GetBlocks, params);
|
|
84
|
+
}
|
|
85
|
+
getBlocksRangeByTopoheight(params) {
|
|
86
|
+
return this.fetch(RPCMethod.GetBlocksRangeByTopoheight, params);
|
|
87
|
+
}
|
|
88
|
+
getBlocksRangeByHeight(params) {
|
|
89
|
+
return this.fetch(RPCMethod.GetBlocksRangeByHeight, params);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export default RPC;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export interface RPCRequest {
|
|
2
|
+
id: number;
|
|
3
|
+
jsonrpc: string;
|
|
4
|
+
method: string;
|
|
5
|
+
params: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RPCError {
|
|
8
|
+
code: number;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RPCResponse<T> {
|
|
12
|
+
id: number;
|
|
13
|
+
result: T;
|
|
14
|
+
error: RPCError;
|
|
15
|
+
}
|
|
16
|
+
export interface GetInfoResult {
|
|
17
|
+
block_time_target: number;
|
|
18
|
+
difficulty: number;
|
|
19
|
+
height: number;
|
|
20
|
+
mempool_size: number;
|
|
21
|
+
native_supply: number;
|
|
22
|
+
stableheight: number;
|
|
23
|
+
top_hash: string;
|
|
24
|
+
version: string;
|
|
25
|
+
network: string;
|
|
26
|
+
topoheight: number;
|
|
27
|
+
}
|
|
28
|
+
export interface Block {
|
|
29
|
+
block_type: string;
|
|
30
|
+
cumulative_difficulty: number;
|
|
31
|
+
supply: number;
|
|
32
|
+
difficulty: number;
|
|
33
|
+
reward: number;
|
|
34
|
+
extra_nonce: string;
|
|
35
|
+
hash: string;
|
|
36
|
+
height: number;
|
|
37
|
+
miner_tx: {
|
|
38
|
+
owner: string;
|
|
39
|
+
signature: null;
|
|
40
|
+
variant: string;
|
|
41
|
+
};
|
|
42
|
+
miner: string;
|
|
43
|
+
nonce: number;
|
|
44
|
+
tips: string[];
|
|
45
|
+
topoheight: number;
|
|
46
|
+
total_fees: number;
|
|
47
|
+
total_size_in_bytes: number;
|
|
48
|
+
txs_hashes: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface BalanceParams {
|
|
51
|
+
address: string;
|
|
52
|
+
asset: string;
|
|
53
|
+
}
|
|
54
|
+
export interface Balance {
|
|
55
|
+
balance: number;
|
|
56
|
+
previous_topoheight: number;
|
|
57
|
+
}
|
|
58
|
+
export interface GetLastBalanceResult {
|
|
59
|
+
topoheight: number;
|
|
60
|
+
balance: Balance;
|
|
61
|
+
}
|
|
62
|
+
export interface P2PStatusResult {
|
|
63
|
+
best_height: number;
|
|
64
|
+
max_peers: number;
|
|
65
|
+
our_height: number;
|
|
66
|
+
peer_count: number;
|
|
67
|
+
peer_id: number;
|
|
68
|
+
tag?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface TopoHeightRangeParams {
|
|
71
|
+
start_topoheight?: number;
|
|
72
|
+
end_topoheight?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface HeightRangeParams {
|
|
75
|
+
start_height?: number;
|
|
76
|
+
end_height?: number;
|
|
77
|
+
}
|
|
78
|
+
export interface RPCEventResult {
|
|
79
|
+
event: string;
|
|
80
|
+
}
|
|
81
|
+
export interface BlockOrdered {
|
|
82
|
+
topoheight: number;
|
|
83
|
+
block_hash: string;
|
|
84
|
+
block_type: string;
|
|
85
|
+
}
|
|
86
|
+
export interface Transaction {
|
|
87
|
+
hash: string;
|
|
88
|
+
blocks: string[];
|
|
89
|
+
data: {
|
|
90
|
+
Transfer: {
|
|
91
|
+
amount: number;
|
|
92
|
+
asset: string;
|
|
93
|
+
extra_data: any;
|
|
94
|
+
to: string;
|
|
95
|
+
}[];
|
|
96
|
+
};
|
|
97
|
+
fee: number;
|
|
98
|
+
nonce: number;
|
|
99
|
+
owner: string;
|
|
100
|
+
signature: string;
|
|
101
|
+
}
|
|
102
|
+
export declare enum RPCMethod {
|
|
103
|
+
GetInfo = "get_info",
|
|
104
|
+
GetHeight = "get_height",
|
|
105
|
+
GetTopoHeight = "get_topoheight",
|
|
106
|
+
GetStableHeight = "get_stableheight",
|
|
107
|
+
GetBlockTemplate = "get_block_template",
|
|
108
|
+
GetBlockAtTopoHeight = "get_block_at_topoheight",
|
|
109
|
+
GetBlocksAtHeight = "get_blocks_at_height",
|
|
110
|
+
GetBlockByHash = "get_block_by_hash",
|
|
111
|
+
GetTopBlock = "get_top_block",
|
|
112
|
+
GetNonce = "get_nonce",
|
|
113
|
+
GetLastBalance = "get_last_balance",
|
|
114
|
+
GetBalanceAtTopoHeight = "get_balance_at_topoheight",
|
|
115
|
+
GetAssets = "get_assets",
|
|
116
|
+
CountTransactions = "count_transactions",
|
|
117
|
+
GetTips = "get_tips",
|
|
118
|
+
P2PStatus = "p2p_status",
|
|
119
|
+
GetDAGOrder = "get_dag_order",
|
|
120
|
+
GetMempool = "get_mempool",
|
|
121
|
+
GetTransaction = "get_transaction",
|
|
122
|
+
GetTransactions = "get_transactions",
|
|
123
|
+
GetBlocks = "get_blocks",
|
|
124
|
+
GetBlocksRangeByTopoheight = "get_blocks_range_by_topoheight",
|
|
125
|
+
GetBlocksRangeByHeight = "get_blocks_range_by_height"
|
|
126
|
+
}
|
|
127
|
+
export declare enum RPCEvent {
|
|
128
|
+
NewBlock = "NewBlock",
|
|
129
|
+
TransactionAddedInMempool = "TransactionAddedInMempool",
|
|
130
|
+
TransactionExecuted = "TransactionExecuted",
|
|
131
|
+
BlockOrdered = "BlockOrdered"
|
|
132
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export var RPCMethod;
|
|
2
|
+
(function (RPCMethod) {
|
|
3
|
+
RPCMethod["GetInfo"] = "get_info";
|
|
4
|
+
RPCMethod["GetHeight"] = "get_height";
|
|
5
|
+
RPCMethod["GetTopoHeight"] = "get_topoheight";
|
|
6
|
+
RPCMethod["GetStableHeight"] = "get_stableheight";
|
|
7
|
+
RPCMethod["GetBlockTemplate"] = "get_block_template";
|
|
8
|
+
RPCMethod["GetBlockAtTopoHeight"] = "get_block_at_topoheight";
|
|
9
|
+
RPCMethod["GetBlocksAtHeight"] = "get_blocks_at_height";
|
|
10
|
+
RPCMethod["GetBlockByHash"] = "get_block_by_hash";
|
|
11
|
+
RPCMethod["GetTopBlock"] = "get_top_block";
|
|
12
|
+
RPCMethod["GetNonce"] = "get_nonce";
|
|
13
|
+
RPCMethod["GetLastBalance"] = "get_last_balance";
|
|
14
|
+
RPCMethod["GetBalanceAtTopoHeight"] = "get_balance_at_topoheight";
|
|
15
|
+
RPCMethod["GetAssets"] = "get_assets";
|
|
16
|
+
RPCMethod["CountTransactions"] = "count_transactions";
|
|
17
|
+
RPCMethod["GetTips"] = "get_tips";
|
|
18
|
+
RPCMethod["P2PStatus"] = "p2p_status";
|
|
19
|
+
RPCMethod["GetDAGOrder"] = "get_dag_order";
|
|
20
|
+
RPCMethod["GetMempool"] = "get_mempool";
|
|
21
|
+
RPCMethod["GetTransaction"] = "get_transaction";
|
|
22
|
+
RPCMethod["GetTransactions"] = "get_transactions";
|
|
23
|
+
RPCMethod["GetBlocks"] = "get_blocks";
|
|
24
|
+
RPCMethod["GetBlocksRangeByTopoheight"] = "get_blocks_range_by_topoheight";
|
|
25
|
+
RPCMethod["GetBlocksRangeByHeight"] = "get_blocks_range_by_height";
|
|
26
|
+
})(RPCMethod || (RPCMethod = {}));
|
|
27
|
+
export var RPCEvent;
|
|
28
|
+
(function (RPCEvent) {
|
|
29
|
+
RPCEvent["NewBlock"] = "NewBlock";
|
|
30
|
+
RPCEvent["TransactionAddedInMempool"] = "TransactionAddedInMempool";
|
|
31
|
+
RPCEvent["TransactionExecuted"] = "TransactionExecuted";
|
|
32
|
+
RPCEvent["BlockOrdered"] = "BlockOrdered";
|
|
33
|
+
})(RPCEvent || (RPCEvent = {}));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { MessageEvent } from 'ws';
|
|
3
|
+
import WebSocket from 'isomorphic-ws';
|
|
4
|
+
import { Block, RPCResponse, GetInfoResult, RPCEvent, RPCEventResult, Transaction, TopoHeightRangeParams, P2PStatusResult, Balance, BalanceParams, GetLastBalanceResult, HeightRangeParams, BlockOrdered } from './types';
|
|
5
|
+
declare class WS {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
socket?: WebSocket;
|
|
8
|
+
timeout: number;
|
|
9
|
+
connected: boolean;
|
|
10
|
+
private events;
|
|
11
|
+
constructor();
|
|
12
|
+
connect(endpoint: string): Promise<unknown>;
|
|
13
|
+
close(code?: number | undefined, data?: string | Buffer | undefined): void;
|
|
14
|
+
onClose(cb: (event: WebSocket.CloseEvent) => void): void;
|
|
15
|
+
onError(cb: (err: WebSocket.ErrorEvent) => void): void;
|
|
16
|
+
private clearEvent;
|
|
17
|
+
closeAllListens(event: RPCEvent): Promise<void>;
|
|
18
|
+
listenEvent<T>(event: RPCEvent, onData: (msgEvent: MessageEvent, data?: T, err?: Error) => void): Promise<() => Promise<void>>;
|
|
19
|
+
onNewBlock(onData: (msgEvent: MessageEvent, data?: Block & RPCEventResult, err?: Error) => void): Promise<() => Promise<void>>;
|
|
20
|
+
onTransactionAddedInMempool(onData: (msgEvent: MessageEvent, data?: Transaction & RPCEventResult, err?: Error) => void): Promise<() => Promise<void>>;
|
|
21
|
+
onTransactionExecuted(onData: (msgEvent: MessageEvent, data?: Transaction & RPCEventResult, err?: Error) => void): Promise<() => Promise<void>>;
|
|
22
|
+
onBlockOrdered(onData: (msgEvent: MessageEvent, data?: BlockOrdered & RPCEventResult, err?: Error) => void): Promise<() => Promise<void>>;
|
|
23
|
+
call<T>(method: string, params?: any): Promise<RPCResponse<T>>;
|
|
24
|
+
dataCall<T>(method: string, params?: any): Promise<T>;
|
|
25
|
+
getInfo(): Promise<GetInfoResult>;
|
|
26
|
+
getHeight(): Promise<number>;
|
|
27
|
+
getTopoHeight(): Promise<number>;
|
|
28
|
+
getStableHeight(): Promise<number>;
|
|
29
|
+
getBlockTemplate(address: string): Promise<string>;
|
|
30
|
+
getBlockAtTopoHeight(topoHeight: number): Promise<Block>;
|
|
31
|
+
getBlocksAtHeight(height: number): Promise<Block[]>;
|
|
32
|
+
getBlockByHash(hash: string): Promise<Block>;
|
|
33
|
+
getTopBlock(): Promise<Block>;
|
|
34
|
+
getNonce(address: string): Promise<number>;
|
|
35
|
+
getLastBalance(params: BalanceParams): Promise<GetLastBalanceResult>;
|
|
36
|
+
getBalanceAtTopoHeight(params: BalanceParams): Promise<Balance>;
|
|
37
|
+
getAssets(): Promise<string[]>;
|
|
38
|
+
countTransactions(): Promise<number>;
|
|
39
|
+
getTips(): Promise<string[]>;
|
|
40
|
+
p2pStatus(): Promise<P2PStatusResult>;
|
|
41
|
+
getDAGOrder(params: TopoHeightRangeParams): Promise<string[]>;
|
|
42
|
+
getMemPool(): Promise<Transaction[]>;
|
|
43
|
+
getTransaction(hash: string): Promise<Transaction>;
|
|
44
|
+
getTransactions(txHashes: string[]): Promise<Transaction[]>;
|
|
45
|
+
getBlocks(params: TopoHeightRangeParams): Promise<Block[]>;
|
|
46
|
+
getBlocksRangeByTopoheight(params: TopoHeightRangeParams): Promise<Block[]>;
|
|
47
|
+
getBlocksRangeByHeight(params: HeightRangeParams): Promise<Block[]>;
|
|
48
|
+
}
|
|
49
|
+
export default WS;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import WebSocket from 'isomorphic-ws';
|
|
2
|
+
import to from 'await-to-js';
|
|
3
|
+
import { RPCEvent, RPCMethod } from './types';
|
|
4
|
+
function createRequestMethod(method, params) {
|
|
5
|
+
const id = Math.floor(Date.now() * Math.random());
|
|
6
|
+
const request = { id: id, jsonrpc: `2.0`, method };
|
|
7
|
+
if (params)
|
|
8
|
+
request.params = params;
|
|
9
|
+
const data = JSON.stringify(request);
|
|
10
|
+
return { data, id };
|
|
11
|
+
}
|
|
12
|
+
class WS {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.endpoint = "";
|
|
15
|
+
this.timeout = 3000;
|
|
16
|
+
this.connected = false;
|
|
17
|
+
this.events = {};
|
|
18
|
+
}
|
|
19
|
+
connect(endpoint) {
|
|
20
|
+
if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
|
|
21
|
+
this.socket.close();
|
|
22
|
+
}
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
this.socket = new WebSocket(endpoint);
|
|
25
|
+
this.endpoint = endpoint;
|
|
26
|
+
this.socket.addEventListener(`open`, (event) => {
|
|
27
|
+
this.connected = true;
|
|
28
|
+
resolve(event);
|
|
29
|
+
});
|
|
30
|
+
this.socket.addEventListener(`close`, () => {
|
|
31
|
+
this.connected = false;
|
|
32
|
+
reject();
|
|
33
|
+
});
|
|
34
|
+
this.socket.addEventListener(`error`, (err) => {
|
|
35
|
+
this.connected = false;
|
|
36
|
+
reject(err);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
close(code, data) {
|
|
41
|
+
this.socket && this.socket.close(code, data);
|
|
42
|
+
}
|
|
43
|
+
onClose(cb) {
|
|
44
|
+
if (!this.socket)
|
|
45
|
+
return;
|
|
46
|
+
this.socket.addEventListener(`close`, (event) => {
|
|
47
|
+
cb(event);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
onError(cb) {
|
|
51
|
+
if (!this.socket)
|
|
52
|
+
return;
|
|
53
|
+
this.socket.addEventListener(`error`, (err) => {
|
|
54
|
+
cb(err);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
clearEvent(event) {
|
|
58
|
+
this.events[event].listeners.forEach(listener => {
|
|
59
|
+
this.socket && this.socket.removeEventListener(`message`, listener);
|
|
60
|
+
});
|
|
61
|
+
Reflect.deleteProperty(this.events, event);
|
|
62
|
+
}
|
|
63
|
+
async closeAllListens(event) {
|
|
64
|
+
if (this.events[event]) {
|
|
65
|
+
const [err, _] = await to(this.call(`unsubscribe`, { notify: event }));
|
|
66
|
+
if (err)
|
|
67
|
+
return Promise.reject(err);
|
|
68
|
+
this.clearEvent(event);
|
|
69
|
+
}
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
}
|
|
72
|
+
async listenEvent(event, onData) {
|
|
73
|
+
const onMessage = (msgEvent) => {
|
|
74
|
+
if (this.events[event]) {
|
|
75
|
+
const { id } = this.events[event];
|
|
76
|
+
if (typeof msgEvent.data === `string`) {
|
|
77
|
+
try {
|
|
78
|
+
const data = JSON.parse(msgEvent.data);
|
|
79
|
+
if (data.id === id) {
|
|
80
|
+
if (data.error) {
|
|
81
|
+
onData(msgEvent, undefined, new Error(data.error.message));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
onData(msgEvent, data.result, undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (_a) {
|
|
89
|
+
// can't parse json -- do nothing
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
if (this.events[event]) {
|
|
95
|
+
this.events[event].listeners.push(onMessage);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// important if multiple listenEvent are called without await atleast we store listener before getting id
|
|
99
|
+
this.events[event] = { listeners: [onMessage] };
|
|
100
|
+
const [err, res] = await to(this.call(`subscribe`, { notify: event }));
|
|
101
|
+
if (err) {
|
|
102
|
+
this.clearEvent(event);
|
|
103
|
+
return Promise.reject(err);
|
|
104
|
+
}
|
|
105
|
+
this.events[event].id = res.id;
|
|
106
|
+
}
|
|
107
|
+
this.socket && this.socket.addEventListener(`message`, onMessage);
|
|
108
|
+
const closeListen = async () => {
|
|
109
|
+
if (this.events[event] && this.events[event].listeners.length === 1) {
|
|
110
|
+
// this is the last listen callback so we unsubscribe from daemon ws
|
|
111
|
+
const [err, _] = await to(this.call(`unsubscribe`, { notify: event }));
|
|
112
|
+
if (err)
|
|
113
|
+
return Promise.reject(err);
|
|
114
|
+
Reflect.deleteProperty(this.events, event);
|
|
115
|
+
}
|
|
116
|
+
this.socket && this.socket.removeEventListener(`message`, onMessage);
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
};
|
|
119
|
+
return Promise.resolve(closeListen);
|
|
120
|
+
}
|
|
121
|
+
onNewBlock(onData) {
|
|
122
|
+
return this.listenEvent(RPCEvent.NewBlock, onData);
|
|
123
|
+
}
|
|
124
|
+
onTransactionAddedInMempool(onData) {
|
|
125
|
+
return this.listenEvent(RPCEvent.TransactionAddedInMempool, onData);
|
|
126
|
+
}
|
|
127
|
+
onTransactionExecuted(onData) {
|
|
128
|
+
return this.listenEvent(RPCEvent.TransactionExecuted, onData);
|
|
129
|
+
}
|
|
130
|
+
onBlockOrdered(onData) {
|
|
131
|
+
return this.listenEvent(RPCEvent.BlockOrdered, onData);
|
|
132
|
+
}
|
|
133
|
+
call(method, params) {
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const { data, id } = createRequestMethod(method, params);
|
|
136
|
+
let timeoutId = null;
|
|
137
|
+
const onMessage = (msgEvent) => {
|
|
138
|
+
if (typeof msgEvent.data === `string`) {
|
|
139
|
+
const data = JSON.parse(msgEvent.data);
|
|
140
|
+
if (data.id === id) {
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
this.socket && this.socket.removeEventListener(`message`, onMessage);
|
|
143
|
+
if (data.error)
|
|
144
|
+
return reject(new Error(data.error.message));
|
|
145
|
+
else
|
|
146
|
+
resolve(data);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
// make sure you listen before sending data
|
|
151
|
+
this.socket && this.socket.addEventListener(`message`, onMessage); // we don't use { once: true } option because of timeout feature
|
|
152
|
+
timeoutId = setTimeout(() => {
|
|
153
|
+
this.socket && this.socket.removeEventListener(`message`, onMessage);
|
|
154
|
+
reject(new Error(`timeout`));
|
|
155
|
+
}, this.timeout);
|
|
156
|
+
this.socket && this.socket.send(data);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
dataCall(method, params) {
|
|
160
|
+
return new Promise(async (resolve, reject) => {
|
|
161
|
+
const [err, res] = await to(this.call(method, params));
|
|
162
|
+
if (err)
|
|
163
|
+
return reject(err);
|
|
164
|
+
return resolve(res.result);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
getInfo() {
|
|
168
|
+
return this.dataCall(RPCMethod.GetInfo);
|
|
169
|
+
}
|
|
170
|
+
getHeight() {
|
|
171
|
+
return this.dataCall(RPCMethod.GetHeight);
|
|
172
|
+
}
|
|
173
|
+
getTopoHeight() {
|
|
174
|
+
return this.dataCall(RPCMethod.GetTopoHeight);
|
|
175
|
+
}
|
|
176
|
+
getStableHeight() {
|
|
177
|
+
return this.dataCall(RPCMethod.GetStableHeight);
|
|
178
|
+
}
|
|
179
|
+
getBlockTemplate(address) {
|
|
180
|
+
return this.dataCall(RPCMethod.GetBlockTemplate, { address });
|
|
181
|
+
}
|
|
182
|
+
getBlockAtTopoHeight(topoHeight) {
|
|
183
|
+
return this.dataCall(RPCMethod.GetBlockAtTopoHeight, { topoheight: topoHeight });
|
|
184
|
+
}
|
|
185
|
+
getBlocksAtHeight(height) {
|
|
186
|
+
return this.dataCall(RPCMethod.GetBlocksAtHeight, { height });
|
|
187
|
+
}
|
|
188
|
+
getBlockByHash(hash) {
|
|
189
|
+
return this.dataCall(RPCMethod.GetBlockByHash, { hash });
|
|
190
|
+
}
|
|
191
|
+
getTopBlock() {
|
|
192
|
+
return this.dataCall(RPCMethod.GetTopBlock);
|
|
193
|
+
}
|
|
194
|
+
getNonce(address) {
|
|
195
|
+
return this.dataCall(RPCMethod.GetNonce, { address });
|
|
196
|
+
}
|
|
197
|
+
getLastBalance(params) {
|
|
198
|
+
return this.dataCall(RPCMethod.GetLastBalance, params);
|
|
199
|
+
}
|
|
200
|
+
getBalanceAtTopoHeight(params) {
|
|
201
|
+
return this.dataCall(RPCMethod.GetBalanceAtTopoHeight, params);
|
|
202
|
+
}
|
|
203
|
+
getAssets() {
|
|
204
|
+
return this.dataCall(RPCMethod.GetAssets);
|
|
205
|
+
}
|
|
206
|
+
countTransactions() {
|
|
207
|
+
return this.dataCall(RPCMethod.CountTransactions);
|
|
208
|
+
}
|
|
209
|
+
getTips() {
|
|
210
|
+
return this.dataCall(RPCMethod.GetTips);
|
|
211
|
+
}
|
|
212
|
+
p2pStatus() {
|
|
213
|
+
return this.dataCall(RPCMethod.P2PStatus);
|
|
214
|
+
}
|
|
215
|
+
getDAGOrder(params) {
|
|
216
|
+
return this.dataCall(RPCMethod.GetDAGOrder, params);
|
|
217
|
+
}
|
|
218
|
+
getMemPool() {
|
|
219
|
+
return this.dataCall(RPCMethod.GetMempool);
|
|
220
|
+
}
|
|
221
|
+
getTransaction(hash) {
|
|
222
|
+
return this.dataCall(RPCMethod.GetTransaction, { hash });
|
|
223
|
+
}
|
|
224
|
+
getTransactions(txHashes) {
|
|
225
|
+
return this.dataCall(RPCMethod.GetTransactions, { tx_hashes: txHashes });
|
|
226
|
+
}
|
|
227
|
+
getBlocks(params) {
|
|
228
|
+
return this.dataCall(RPCMethod.GetBlocks, params);
|
|
229
|
+
}
|
|
230
|
+
getBlocksRangeByTopoheight(params) {
|
|
231
|
+
return this.dataCall(RPCMethod.GetBlocksRangeByTopoheight, params);
|
|
232
|
+
}
|
|
233
|
+
getBlocksRangeByHeight(params) {
|
|
234
|
+
return this.dataCall(RPCMethod.GetBlocksRangeByHeight, params);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
export default WS;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"name": "@xelis/sdk",
|
|
4
|
+
"description": "Xelis software development kit for JS",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/xelis-project/xelis-js-sdk"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/xelis-project/xelis-js-sdk#readme",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"build": "npx tsc --declaration && cp package.json ./dist && cp README.md ./dist",
|
|
16
|
+
"prepublish": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/jest": "^29.4.0",
|
|
20
|
+
"@types/react": "^18.2.23",
|
|
21
|
+
"@types/ws": "^8.5.4",
|
|
22
|
+
"ts-jest": "^29.0.5",
|
|
23
|
+
"typescript": "^4.9.5"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"await-to-js": "^3.0.0",
|
|
27
|
+
"isomorphic-ws": "^5.0.0",
|
|
28
|
+
"react": "^18.2.0",
|
|
29
|
+
"ws": "^8.12.1"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from 'react';
|
|
2
|
+
import { MessageEvent } from 'ws';
|
|
3
|
+
import DaemonWS from '../daemon/websocket';
|
|
4
|
+
import { RPCEvent } from '../daemon/types';
|
|
5
|
+
interface NodeSocket {
|
|
6
|
+
daemon: DaemonWS;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
err: any;
|
|
9
|
+
connected: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface NodeSocketProviderProps {
|
|
12
|
+
endpoint: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const NodeSocketProvider: (props: PropsWithChildren<NodeSocketProviderProps>) => React.JSX.Element;
|
|
15
|
+
interface NodeSocketSubscribeProps<T> {
|
|
16
|
+
event: RPCEvent;
|
|
17
|
+
onConnected: () => void;
|
|
18
|
+
onData: (msgEvent: MessageEvent, data?: T, err?: Error | undefined) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const useNodeSocketSubscribe: ({ event, onConnected, onData }: NodeSocketSubscribeProps<any>, dependencies: React.DependencyList) => void;
|
|
21
|
+
export declare const useNodeSocket: () => NodeSocket;
|
|
22
|
+
export default useNodeSocket;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
+
import to from 'await-to-js';
|
|
3
|
+
import DaemonWS from '../daemon/websocket';
|
|
4
|
+
const daemon = new DaemonWS();
|
|
5
|
+
const Context = createContext({
|
|
6
|
+
connected: false,
|
|
7
|
+
err: null,
|
|
8
|
+
loading: false,
|
|
9
|
+
daemon,
|
|
10
|
+
});
|
|
11
|
+
export const NodeSocketProvider = (props) => {
|
|
12
|
+
const { children, endpoint } = props;
|
|
13
|
+
const [loading, setLoading] = useState(true);
|
|
14
|
+
const [connected, setConnected] = useState(false);
|
|
15
|
+
const [err, setErr] = useState();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const load = async () => {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
const [err, res] = await to(daemon.connect(endpoint));
|
|
20
|
+
if (err)
|
|
21
|
+
setErr(err);
|
|
22
|
+
else
|
|
23
|
+
setConnected(true);
|
|
24
|
+
setLoading(false);
|
|
25
|
+
daemon.onError((err) => {
|
|
26
|
+
setErr(err);
|
|
27
|
+
setConnected(false);
|
|
28
|
+
});
|
|
29
|
+
daemon.onClose(() => {
|
|
30
|
+
setConnected(false);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
load();
|
|
34
|
+
}, [endpoint]);
|
|
35
|
+
return React.createElement(Context.Provider, { value: { daemon, loading, err, connected } }, children);
|
|
36
|
+
};
|
|
37
|
+
export const useNodeSocketSubscribe = ({ event, onConnected, onData }, dependencies) => {
|
|
38
|
+
const nodeSocket = useNodeSocket();
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!nodeSocket.connected)
|
|
41
|
+
return;
|
|
42
|
+
if (typeof onConnected === `function`)
|
|
43
|
+
onConnected();
|
|
44
|
+
let closeEvent;
|
|
45
|
+
const listen = async () => {
|
|
46
|
+
if (!nodeSocket.daemon)
|
|
47
|
+
return;
|
|
48
|
+
closeEvent = await nodeSocket.daemon.listenEvent(event, onData);
|
|
49
|
+
};
|
|
50
|
+
listen();
|
|
51
|
+
return () => {
|
|
52
|
+
closeEvent && closeEvent();
|
|
53
|
+
};
|
|
54
|
+
}, [nodeSocket, ...dependencies]);
|
|
55
|
+
};
|
|
56
|
+
export const useNodeSocket = () => useContext(Context);
|
|
57
|
+
export default useNodeSocket;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"name": "@xelis/sdk",
|
|
4
|
+
"description": "Xelis software development kit for JS",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/xelis-project/xelis-js-sdk"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/xelis-project/xelis-js-sdk#readme",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"build": "npx tsc --declaration && cp package.json ./dist && cp README.md ./dist",
|
|
16
|
+
"prepublish": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/jest": "^29.4.0",
|
|
20
|
+
"@types/react": "^18.2.23",
|
|
21
|
+
"@types/ws": "^8.5.4",
|
|
22
|
+
"ts-jest": "^29.0.5",
|
|
23
|
+
"typescript": "^4.9.5"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"await-to-js": "^3.0.0",
|
|
27
|
+
"isomorphic-ws": "^5.0.0",
|
|
28
|
+
"react": "^18.2.0",
|
|
29
|
+
"ws": "^8.12.1"
|
|
30
|
+
}
|
|
31
|
+
}
|