@mr-zwets/bchn-api-wrapper 1.0.1 → 1.0.2
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/.claude/settings.local.json +8 -0
- package/.github/workflows/ci.yaml +36 -0
- package/CLAUDE.md +70 -0
- package/README.md +121 -129
- package/dist/interfaces/interfaces.d.ts +13 -0
- package/dist/interfaces/restInterfaces/interfaces.d.ts +124 -18
- package/dist/interfaces/rpcInterfaces/blockchain.d.ts +293 -102
- package/dist/interfaces/rpcInterfaces/control.d.ts +6 -0
- package/dist/interfaces/rpcInterfaces/generating.d.ts +2 -0
- package/dist/interfaces/rpcInterfaces/mining.d.ts +9 -0
- package/dist/interfaces/rpcInterfaces/network.d.ts +18 -0
- package/dist/interfaces/rpcInterfaces/rawtransactions.d.ts +21 -0
- package/dist/interfaces/rpcInterfaces/util.d.ts +5 -0
- package/dist/interfaces/rpcInterfaces/wallet.d.ts +54 -0
- package/dist/interfaces/rpcInterfaces/zmq.d.ts +1 -0
- package/dist/restClient.d.ts +13 -1
- package/dist/restClient.js +19 -6
- package/dist/rpcClient.d.ts +7 -0
- package/dist/rpcClient.js +7 -0
- package/package.json +7 -8
- package/pnpm-lock.yaml +1279 -0
- package/src/index.ts +3 -3
- package/src/interfaces/interfaces.ts +96 -86
- package/src/interfaces/restInterfaces/interfaces.ts +235 -116
- package/src/interfaces/rpcInterfaces/blockchain.ts +932 -758
- package/src/interfaces/rpcInterfaces/control.ts +68 -62
- package/src/interfaces/rpcInterfaces/generating.ts +23 -21
- package/src/interfaces/rpcInterfaces/index.ts +13 -13
- package/src/interfaces/rpcInterfaces/mining.ts +151 -143
- package/src/interfaces/rpcInterfaces/network.ts +213 -195
- package/src/interfaces/rpcInterfaces/rawtransactions.ts +332 -314
- package/src/interfaces/rpcInterfaces/util.ts +56 -52
- package/src/interfaces/rpcInterfaces/wallet.ts +728 -674
- package/src/interfaces/rpcInterfaces/zmq.ts +12 -11
- package/src/restClient.ts +134 -119
- package/src/rpcClient.ts +100 -93
- package/src/utils/errors.ts +6 -6
- package/src/utils/utils.ts +55 -55
- package/test/restClient.test.ts +33 -31
- package/test/rpcClient.test.ts +119 -115
- package/test/setupTests.ts +56 -54
- package/test/tsconfig.json +4 -4
- package/tsconfig.json +13 -13
- package/vitest.config.ts +8 -8
- package/CHANGELOG.md +0 -7
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
/* --- Zmq Commands --- */
|
|
2
|
-
// progress 1/1
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
1
|
+
/* --- Zmq Commands --- */
|
|
2
|
+
// progress 1/1
|
|
3
|
+
|
|
4
|
+
/** Returns active ZMQ notification endpoints. */
|
|
5
|
+
export interface GetZmqNotifications {
|
|
6
|
+
method: 'getzmqnotifications';
|
|
7
|
+
params: [];
|
|
8
|
+
response: {
|
|
9
|
+
type: string;
|
|
10
|
+
address: string;
|
|
11
|
+
}[]
|
|
12
|
+
}
|
package/src/restClient.ts
CHANGED
|
@@ -1,119 +1,134 @@
|
|
|
1
|
-
import type { RestClientConfig, formatOptions, ResponseType } from "./interfaces/interfaces.js";
|
|
2
|
-
import type {
|
|
3
|
-
BlockInfoNoTxDetails,
|
|
4
|
-
BlockInfoTxDetails,
|
|
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
|
-
async getBlock<TFormat extends formatOptions = 'json'>(
|
|
83
|
-
blockhash: string, includeTxDetails:
|
|
84
|
-
): Promise<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return this.fetchFromNode<
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
async
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return this.fetchFromNode<
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
async
|
|
117
|
-
return this.fetchFromNode<
|
|
118
|
-
}
|
|
119
|
-
|
|
1
|
+
import type { RestClientConfig, formatOptions, ResponseType } from "./interfaces/interfaces.js";
|
|
2
|
+
import type {
|
|
3
|
+
BlockInfoNoTxDetails,
|
|
4
|
+
BlockInfoTxDetails,
|
|
5
|
+
BlockInfoWithPatterns,
|
|
6
|
+
ChainInfo,
|
|
7
|
+
HeaderInfo,
|
|
8
|
+
MempoolContent,
|
|
9
|
+
MempoolInfo,
|
|
10
|
+
TxDetails,
|
|
11
|
+
TxDetailsWithPatterns,
|
|
12
|
+
UtxosInfo
|
|
13
|
+
} from "./interfaces/restInterfaces/interfaces.js";
|
|
14
|
+
import { validateUrl } from "./utils/utils.js";
|
|
15
|
+
|
|
16
|
+
/** REST client for read-only BCHN blockchain access via the REST interface. */
|
|
17
|
+
export class BchnRestClient {
|
|
18
|
+
private baseUrl: string;
|
|
19
|
+
private timeoutMs: number;
|
|
20
|
+
private logger: Console;
|
|
21
|
+
|
|
22
|
+
constructor(config: RestClientConfig) {
|
|
23
|
+
this.baseUrl = validateUrl(config.url);
|
|
24
|
+
this.timeoutMs = config.timeoutMs ?? 5000;
|
|
25
|
+
this.logger = config.logger ?? console;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async fetchFromNode<T, TFormat extends formatOptions>(
|
|
29
|
+
endpoint: string,
|
|
30
|
+
format: TFormat
|
|
31
|
+
): Promise<ResponseType<TFormat, T>> {
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(`${this.baseUrl}/rest/${endpoint}`, {
|
|
34
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`Error fetching data from ${endpoint}: ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (format === 'json') {
|
|
42
|
+
return await response.json() as ResponseType<TFormat, T>;
|
|
43
|
+
} else {
|
|
44
|
+
return await response.text() as ResponseType<TFormat, T>; // For 'bin' and 'hex', return raw text
|
|
45
|
+
}
|
|
46
|
+
} catch(error) {
|
|
47
|
+
let errorMessage: string | undefined
|
|
48
|
+
|
|
49
|
+
// Check if the error is due to timeout or other fetch-related issues
|
|
50
|
+
if (typeof error === 'string') {
|
|
51
|
+
errorMessage = error;
|
|
52
|
+
this.logger.error(error);
|
|
53
|
+
} else if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
54
|
+
// If error is an instance DOMException TimeoutError
|
|
55
|
+
errorMessage = 'Request timed out';
|
|
56
|
+
this.logger.error(`Request to ${endpoint} timed out after ${this.timeoutMs} ms`);
|
|
57
|
+
} else {
|
|
58
|
+
this.logger.error(`Unknown error occurred during request to ${endpoint}`);
|
|
59
|
+
throw new Error(`Unknown error: ${error}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Always rethrow the error after logging
|
|
63
|
+
throw new Error(errorMessage);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Returns transaction details by txid. */
|
|
68
|
+
async getTransaction<TFormat extends formatOptions = 'json'>(
|
|
69
|
+
txid: string, format:TFormat = 'json' as TFormat
|
|
70
|
+
) {
|
|
71
|
+
return this.fetchFromNode<TxDetails, TFormat>(`tx/${txid}.${format}`, format);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// getBlock overload signatures
|
|
75
|
+
// This is needed so the getBlock return type can depend on the 'includeTxDetails' boolean flag
|
|
76
|
+
|
|
77
|
+
/** Returns block data. Use includeTxDetails=true for full transaction objects. */
|
|
78
|
+
async getBlock<TFormat extends formatOptions = 'json'>(
|
|
79
|
+
blockhash: string, includeTxDetails: true, format?:TFormat
|
|
80
|
+
): Promise<TFormat extends 'json' ? BlockInfoTxDetails : string>;
|
|
81
|
+
|
|
82
|
+
async getBlock<TFormat extends formatOptions = 'json'>(
|
|
83
|
+
blockhash: string, includeTxDetails: false, format?:TFormat
|
|
84
|
+
): Promise<TFormat extends 'json' ? BlockInfoNoTxDetails : string>;
|
|
85
|
+
|
|
86
|
+
// getBlock Implementation
|
|
87
|
+
async getBlock<TFormat extends formatOptions = 'json'>(
|
|
88
|
+
blockhash: string, includeTxDetails: boolean, format:TFormat = 'json' as TFormat
|
|
89
|
+
): Promise<any> {
|
|
90
|
+
const path = includeTxDetails ? 'block' : 'block/notxdetails';
|
|
91
|
+
return this.fetchFromNode(`${path}/${blockhash}.${format}`, format);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Returns block headers starting from a specific block hash. */
|
|
95
|
+
async getBlockHeaders<TFormat extends formatOptions = 'json'>(
|
|
96
|
+
count: number, blockhash: string, format:TFormat = 'json' as TFormat
|
|
97
|
+
) {
|
|
98
|
+
return this.fetchFromNode<HeaderInfo, TFormat>(`headers/${count}/${blockhash}.${format}`, format);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Returns current chain state (network, sync progress, best block). */
|
|
102
|
+
async getChainInfo() {
|
|
103
|
+
return this.fetchFromNode<ChainInfo, 'json'>('chaininfo.json', 'json');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Queries UTXO set for specific outpoints. Outpoints format: "txid-vout". */
|
|
107
|
+
async getUTXOs<TFormat extends formatOptions = 'json'>(
|
|
108
|
+
checkmempool: boolean, outpoints: string[], format:TFormat = 'json' as TFormat
|
|
109
|
+
) {
|
|
110
|
+
const path = (checkmempool ? 'checkmempool/' : '') + outpoints.join('/');
|
|
111
|
+
const endpoint = `getutxos/${path}.${format}`;
|
|
112
|
+
return this.fetchFromNode<UtxosInfo, TFormat>(endpoint, format);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Returns mempool statistics (size, bytes, fee rates). */
|
|
116
|
+
async getMempoolInfo() {
|
|
117
|
+
return this.fetchFromNode<MempoolInfo, 'json'>('mempool/info.json', 'json');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Returns all transactions currently in the mempool. */
|
|
121
|
+
async getMempoolContents() {
|
|
122
|
+
return this.fetchFromNode<MempoolContent, 'json'>('mempool/contents.json', 'json');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Returns block with bytecode pattern data (v29.0.0+). */
|
|
126
|
+
async getBlockWithPatterns(blockhash: string) {
|
|
127
|
+
return this.fetchFromNode<BlockInfoWithPatterns, 'json'>(`block/withpatterns/${blockhash}.json`, 'json');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Returns transaction with bytecode pattern data (v29.0.0+). */
|
|
131
|
+
async getTransactionWithPatterns(txid: string) {
|
|
132
|
+
return this.fetchFromNode<TxDetailsWithPatterns, 'json'>(`tx/withpatterns/${txid}.json`, 'json');
|
|
133
|
+
}
|
|
134
|
+
}
|
package/src/rpcClient.ts
CHANGED
|
@@ -1,93 +1,100 @@
|
|
|
1
|
-
import type { RpcClientConfig, RpcRequest } from "./interfaces/interfaces.js";
|
|
2
|
-
import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
|
|
3
|
-
import { RetryLimitExceededError } from "./utils/errors.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if(!config.
|
|
19
|
-
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
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
|
-
}
|
|
1
|
+
import type { RpcClientConfig, RpcRequest } from "./interfaces/interfaces.js";
|
|
2
|
+
import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
|
|
3
|
+
import { RetryLimitExceededError } from "./utils/errors.js";
|
|
4
|
+
|
|
5
|
+
/** RPC client for full BCHN node interaction via JSON-RPC. */
|
|
6
|
+
export class BchnRpcClient {
|
|
7
|
+
private url: string
|
|
8
|
+
private rpcUser: string
|
|
9
|
+
private rpcPassword: string
|
|
10
|
+
|
|
11
|
+
private maxRetries: number // number of retries before throwing an exception
|
|
12
|
+
private retryDelayMs: number // delay between each retry
|
|
13
|
+
private logger: typeof console // logger
|
|
14
|
+
private timeoutMs: number // max timeout for each retry
|
|
15
|
+
|
|
16
|
+
constructor(config: RpcClientConfig){
|
|
17
|
+
this.url = validateAndConstructUrl(config)
|
|
18
|
+
if(!config.rpcUser) throw new Error('Need to provide rpcUser in config')
|
|
19
|
+
if(!config.rpcPassword) throw new Error('Need to provide rpcPassword in config')
|
|
20
|
+
this.rpcUser = config.rpcUser;
|
|
21
|
+
this.rpcPassword= config.rpcPassword;
|
|
22
|
+
|
|
23
|
+
// optional config
|
|
24
|
+
this.maxRetries = config.maxRetries ?? 0;
|
|
25
|
+
this.retryDelayMs= config.retryDelayMs ?? 100;
|
|
26
|
+
this.logger = config.logger ?? console;
|
|
27
|
+
this.timeoutMs = config.timeoutMs ?? 5000;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sends a typed RPC request to the BCHN node.
|
|
32
|
+
* @example
|
|
33
|
+
* const result = await client.request<GetBlockCount>("getblockcount");
|
|
34
|
+
* const block = await client.request<GetBlockVerbosity1>("getblock", hash, 1);
|
|
35
|
+
*/
|
|
36
|
+
async request<T extends RpcRequest>(
|
|
37
|
+
endpoint: T['method'],
|
|
38
|
+
...params: T['params']
|
|
39
|
+
): Promise<T['response']> {
|
|
40
|
+
const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
|
|
41
|
+
|
|
42
|
+
// Retry logic
|
|
43
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
44
|
+
try {
|
|
45
|
+
// Send the request with a timeout and retries
|
|
46
|
+
const response = await fetch(this.url, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
'Authorization': `Basic ${auth}`
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
|
|
53
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const result = await response.json();
|
|
57
|
+
|
|
58
|
+
// Handle response errors
|
|
59
|
+
if (!response.ok || result.error) {
|
|
60
|
+
throw new Error(`Error: ${result.error?.message || response.statusText}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result.result as T['response']; // Return the result if successful
|
|
64
|
+
|
|
65
|
+
} catch (error) {
|
|
66
|
+
let errorMessage: string | undefined
|
|
67
|
+
|
|
68
|
+
// Check if the error is due to timeout or other fetch-related issues
|
|
69
|
+
if(typeof error == 'string'){
|
|
70
|
+
errorMessage = error
|
|
71
|
+
this.logger.error(error)
|
|
72
|
+
}
|
|
73
|
+
else if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
74
|
+
// If error is an instance DOMException TimeoutError
|
|
75
|
+
errorMessage = error.message
|
|
76
|
+
this.logger.error(`Request timed out after ${this.timeoutMs} ms`);
|
|
77
|
+
}
|
|
78
|
+
else if (error instanceof Error) {
|
|
79
|
+
// If error is an instance of Error, you can safely access its properties
|
|
80
|
+
errorMessage = error.message
|
|
81
|
+
this.logger.error(`Request failed with error: ${error.message}`);
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Retry if allowed
|
|
86
|
+
if (attempt < this.maxRetries) {
|
|
87
|
+
this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
|
|
88
|
+
await new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
|
|
89
|
+
} else {
|
|
90
|
+
// If no retries are left, throw the final error
|
|
91
|
+
throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// This line ensures TypeScript is satisfied that a value will always be returned, but
|
|
97
|
+
// it should never be reached if the retries fail, as the last attempt should throw an error.
|
|
98
|
+
throw new Error('Request failed unexpectedly');
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/utils/errors.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export class RetryLimitExceededError extends Error {
|
|
2
|
-
constructor(message: string) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'RetryLimitExceededError';
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
export class RetryLimitExceededError extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'RetryLimitExceededError';
|
|
5
|
+
}
|
|
6
|
+
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RpcClientConfig,
|
|
3
|
-
RpcClientUrlConfig,
|
|
4
|
-
RpcClientHostConfig,
|
|
5
|
-
} from "../interfaces/interfaces.js";
|
|
6
|
-
|
|
7
|
-
export function getRandomId(): number {
|
|
8
|
-
return Math.floor(Math.random() * 100000);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// A utility function to validate and construct the URL from the RpcClientConfig object
|
|
12
|
-
export function validateAndConstructUrl(config: RpcClientConfig): string {
|
|
13
|
-
let url: string
|
|
14
|
-
if (isUrlConfig(config)) {
|
|
15
|
-
url = validateUrl(config.url)
|
|
16
|
-
} else if (isHostConfig(config)) {
|
|
17
|
-
const { protocol, host, port } = config;
|
|
18
|
-
if (protocol !== 'http' && protocol !== 'https') {
|
|
19
|
-
throw new Error("Protocol should be 'http' or 'https'");
|
|
20
|
-
}
|
|
21
|
-
url = validateUrl(`${protocol}://${host}:${port}`)
|
|
22
|
-
} else {
|
|
23
|
-
throw new Error('Invalid configuration: Either provide the url or protocol/host/port');
|
|
24
|
-
}
|
|
25
|
-
return url
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// A utility function to validate a URL
|
|
29
|
-
export function validateUrl(url: string) {
|
|
30
|
-
if(!url) throw new Error('URL is required');
|
|
31
|
-
try {
|
|
32
|
-
new URL(url);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
throw new Error('Invalid URL format');
|
|
35
|
-
}
|
|
36
|
-
return url
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Type guard to check if the config is RpcClientUrlConfig
|
|
40
|
-
function isUrlConfig(config: RpcClientConfig): config is RpcClientUrlConfig {
|
|
41
|
-
return 'url' in config;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Type guard to check if the config is RpcClientHostConfig
|
|
45
|
-
function isHostConfig(config: RpcClientConfig): config is RpcClientHostConfig {
|
|
46
|
-
return 'protocol' in config && 'hostname' in config && 'port' in config;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export enum BchnNetworkPort {
|
|
50
|
-
Mainnet = 8332,
|
|
51
|
-
Testnet = 18332,
|
|
52
|
-
Testnet4 = 28332,
|
|
53
|
-
Scalenet = 38332,
|
|
54
|
-
Regtest = 18443
|
|
55
|
-
}
|
|
1
|
+
import type {
|
|
2
|
+
RpcClientConfig,
|
|
3
|
+
RpcClientUrlConfig,
|
|
4
|
+
RpcClientHostConfig,
|
|
5
|
+
} from "../interfaces/interfaces.js";
|
|
6
|
+
|
|
7
|
+
export function getRandomId(): number {
|
|
8
|
+
return Math.floor(Math.random() * 100000);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A utility function to validate and construct the URL from the RpcClientConfig object
|
|
12
|
+
export function validateAndConstructUrl(config: RpcClientConfig): string {
|
|
13
|
+
let url: string
|
|
14
|
+
if (isUrlConfig(config)) {
|
|
15
|
+
url = validateUrl(config.url)
|
|
16
|
+
} else if (isHostConfig(config)) {
|
|
17
|
+
const { protocol, host, port } = config;
|
|
18
|
+
if (protocol !== 'http' && protocol !== 'https') {
|
|
19
|
+
throw new Error("Protocol should be 'http' or 'https'");
|
|
20
|
+
}
|
|
21
|
+
url = validateUrl(`${protocol}://${host}:${port}`)
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error('Invalid configuration: Either provide the url or protocol/host/port');
|
|
24
|
+
}
|
|
25
|
+
return url
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A utility function to validate a URL
|
|
29
|
+
export function validateUrl(url: string) {
|
|
30
|
+
if(!url) throw new Error('URL is required');
|
|
31
|
+
try {
|
|
32
|
+
new URL(url);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw new Error('Invalid URL format');
|
|
35
|
+
}
|
|
36
|
+
return url
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Type guard to check if the config is RpcClientUrlConfig
|
|
40
|
+
function isUrlConfig(config: RpcClientConfig): config is RpcClientUrlConfig {
|
|
41
|
+
return 'url' in config;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Type guard to check if the config is RpcClientHostConfig
|
|
45
|
+
function isHostConfig(config: RpcClientConfig): config is RpcClientHostConfig {
|
|
46
|
+
return 'protocol' in config && 'hostname' in config && 'port' in config;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export enum BchnNetworkPort {
|
|
50
|
+
Mainnet = 8332,
|
|
51
|
+
Testnet = 18332,
|
|
52
|
+
Testnet4 = 28332,
|
|
53
|
+
Scalenet = 38332,
|
|
54
|
+
Regtest = 18443
|
|
55
|
+
}
|