@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.
Files changed (45) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.github/workflows/ci.yaml +36 -0
  3. package/CLAUDE.md +70 -0
  4. package/README.md +121 -129
  5. package/dist/interfaces/interfaces.d.ts +13 -0
  6. package/dist/interfaces/restInterfaces/interfaces.d.ts +124 -18
  7. package/dist/interfaces/rpcInterfaces/blockchain.d.ts +293 -102
  8. package/dist/interfaces/rpcInterfaces/control.d.ts +6 -0
  9. package/dist/interfaces/rpcInterfaces/generating.d.ts +2 -0
  10. package/dist/interfaces/rpcInterfaces/mining.d.ts +9 -0
  11. package/dist/interfaces/rpcInterfaces/network.d.ts +18 -0
  12. package/dist/interfaces/rpcInterfaces/rawtransactions.d.ts +21 -0
  13. package/dist/interfaces/rpcInterfaces/util.d.ts +5 -0
  14. package/dist/interfaces/rpcInterfaces/wallet.d.ts +54 -0
  15. package/dist/interfaces/rpcInterfaces/zmq.d.ts +1 -0
  16. package/dist/restClient.d.ts +13 -1
  17. package/dist/restClient.js +19 -6
  18. package/dist/rpcClient.d.ts +7 -0
  19. package/dist/rpcClient.js +7 -0
  20. package/package.json +7 -8
  21. package/pnpm-lock.yaml +1279 -0
  22. package/src/index.ts +3 -3
  23. package/src/interfaces/interfaces.ts +96 -86
  24. package/src/interfaces/restInterfaces/interfaces.ts +235 -116
  25. package/src/interfaces/rpcInterfaces/blockchain.ts +932 -758
  26. package/src/interfaces/rpcInterfaces/control.ts +68 -62
  27. package/src/interfaces/rpcInterfaces/generating.ts +23 -21
  28. package/src/interfaces/rpcInterfaces/index.ts +13 -13
  29. package/src/interfaces/rpcInterfaces/mining.ts +151 -143
  30. package/src/interfaces/rpcInterfaces/network.ts +213 -195
  31. package/src/interfaces/rpcInterfaces/rawtransactions.ts +332 -314
  32. package/src/interfaces/rpcInterfaces/util.ts +56 -52
  33. package/src/interfaces/rpcInterfaces/wallet.ts +728 -674
  34. package/src/interfaces/rpcInterfaces/zmq.ts +12 -11
  35. package/src/restClient.ts +134 -119
  36. package/src/rpcClient.ts +100 -93
  37. package/src/utils/errors.ts +6 -6
  38. package/src/utils/utils.ts +55 -55
  39. package/test/restClient.test.ts +33 -31
  40. package/test/rpcClient.test.ts +119 -115
  41. package/test/setupTests.ts +56 -54
  42. package/test/tsconfig.json +4 -4
  43. package/tsconfig.json +13 -13
  44. package/vitest.config.ts +8 -8
  45. package/CHANGELOG.md +0 -7
@@ -1,11 +1,12 @@
1
- /* --- Zmq Commands --- */
2
- // progress 1/1
3
-
4
- export interface GetZmqNotifications {
5
- method: 'getzmqnotifications';
6
- params: [];
7
- response: {
8
- type: string;
9
- address: string;
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
- ChainInfo,
6
- HeaderInfo,
7
- MempoolContent,
8
- MempoolInfo,
9
- TxDetails,
10
- UtxosInfo
11
- } from "./interfaces/restInterfaces/interfaces.js";
12
- import { validateUrl } from "./utils/utils.js";
13
-
14
- export class BchnRestClient {
15
- private baseUrl: string;
16
- private timeoutMs: number;
17
- private logger: Console;
18
-
19
- constructor(config: RestClientConfig) {
20
- this.baseUrl = validateUrl(config.url);
21
- this.timeoutMs = config.timeoutMs ?? 5000;
22
- this.logger = config.logger ?? console;
23
- }
24
-
25
- private async fetchFromNode<T, TFormat extends formatOptions>(
26
- endpoint: string,
27
- format: TFormat
28
- ): Promise<ResponseType<TFormat, T>> {
29
- try {
30
- const response = await fetch(`${this.baseUrl}/rest/${endpoint}`, {
31
- signal: AbortSignal.timeout(this.timeoutMs),
32
- });
33
-
34
- if (!response.ok) {
35
- throw new Error(`Error fetching data from ${endpoint}: ${response.statusText}`);
36
- }
37
-
38
- if (format === 'json') {
39
- return await response.json() as ResponseType<TFormat, T>;
40
- } else {
41
- return await response.text() as ResponseType<TFormat, T>; // For 'bin' and 'hex', return raw text
42
- }
43
- } catch(error) {
44
- let errorMessage: string | undefined
45
-
46
- // Check if the error is due to timeout or other fetch-related issues
47
- if (typeof error === 'string') {
48
- errorMessage = error;
49
- this.logger.error(error);
50
- } else if (error instanceof DOMException && error.name === 'TimeoutError') {
51
- // If error is an instance DOMException TimeoutError
52
- errorMessage = 'Request timed out';
53
- this.logger.error(`Request to ${endpoint} timed out after ${this.timeoutMs} ms`);
54
- } else {
55
- this.logger.error(`Unknown error occurred during request to ${endpoint}`);
56
- throw new Error(`Unknown error: ${error}`);
57
- }
58
-
59
- // Always rethrow the error after logging
60
- throw new Error(errorMessage);
61
- }
62
- }
63
-
64
- // Get transaction details by transaction hash
65
- async getTransaction<TFormat extends formatOptions = 'json'>(
66
- txid: string, format:TFormat = 'json' as TFormat
67
- ) {
68
- return this.fetchFromNode<TxDetails, TFormat>(`tx/${txid}.${format}`, format);
69
- }
70
-
71
- // getBlock overload signatures
72
- // This is needed so the getBlock return type can depend on the 'includeTxDetails' boolean flag
73
- async getBlock<TFormat extends formatOptions = 'json'>(
74
- blockhash: string, includeTxDetails: true, format?:TFormat
75
- ): Promise<TFormat extends 'json' ? BlockInfoTxDetails : string>;
76
-
77
- async getBlock<TFormat extends formatOptions = 'json'>(
78
- blockhash: string, includeTxDetails: false, format?:TFormat
79
- ): Promise<TFormat extends 'json' ? BlockInfoNoTxDetails : string>;
80
-
81
- // getBlock Implementation
82
- async getBlock<TFormat extends formatOptions = 'json'>(
83
- blockhash: string, includeTxDetails: boolean, format:TFormat = 'json' as TFormat
84
- ): Promise<any> {
85
- const path = includeTxDetails ? 'block' : 'block/notxdetails';
86
- return this.fetchFromNode(`${path}/${blockhash}.${format}`, format);
87
- }
88
-
89
- // Get block headers starting from a specific block hash
90
- async getBlockHeaders<TFormat extends formatOptions = 'json'>(
91
- count: number, blockhash: string, format:TFormat = 'json' as TFormat
92
- ) {
93
- return this.fetchFromNode<HeaderInfo, TFormat>(`headers/${count}/${blockhash}.${format}`, format);
94
- }
95
-
96
- // Get chain info (chain state details)
97
- async getChainInfo() {
98
- return this.fetchFromNode<ChainInfo, 'json'>('chaininfo.json', 'json');
99
- }
100
-
101
- // Query UTXO set based on specific outpoints (txid and vout)
102
- async getUTXOs<TFormat extends formatOptions = 'json'>(
103
- checkmempool: boolean, outpoints: string[], format:TFormat = 'json' as TFormat
104
- ) {
105
- const path = (checkmempool ? 'checkmempool/' : '') + outpoints.join('/');
106
- const endpoint = `getutxos/${path}.${format}`;
107
- return this.fetchFromNode<UtxosInfo, TFormat>(endpoint, format);
108
- }
109
-
110
- // Get mempool information (basic)
111
- async getMempoolInfo() {
112
- return this.fetchFromNode<MempoolInfo, 'json'>('mempool/info.json', 'json');
113
- }
114
-
115
- // Get mempool contents (transactions currently in the mempool)
116
- async getMempoolContents() {
117
- return this.fetchFromNode<MempoolContent, 'json'>('mempool/contents.json', 'json');
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
- export class BchnRpcClient {
6
- private url: string
7
- private rpcUser: string
8
- private rpcPassword: string
9
-
10
- private maxRetries: number // number of retries before throwing an exception
11
- private retryDelayMs: number // delay between each retry
12
- private logger: typeof console // logger
13
- private timeoutMs: number // max timeout for each retry
14
-
15
- constructor(config: RpcClientConfig){
16
- this.url = validateAndConstructUrl(config)
17
- if(!config.rpcUser) throw new Error('Need to provide rpcUser in config')
18
- if(!config.rpcPassword) throw new Error('Need to provide rpcPassword in config')
19
- this.rpcUser = config.rpcUser;
20
- this.rpcPassword= config.rpcPassword;
21
-
22
- // optional config
23
- this.maxRetries = config.maxRetries ?? 0;
24
- this.retryDelayMs= config.retryDelayMs ?? 100;
25
- this.logger = config.logger ?? console;
26
- this.timeoutMs = config.timeoutMs ?? 5000;
27
- }
28
-
29
- async request<T extends RpcRequest>(
30
- endpoint: T['method'],
31
- ...params: T['params']
32
- ): Promise<T['response']> {
33
- const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
34
-
35
- // Retry logic
36
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
37
- try {
38
- // Send the request with a timeout and retries
39
- const response = await fetch(this.url, {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/json',
43
- 'Authorization': `Basic ${auth}`
44
- },
45
- body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
46
- signal: AbortSignal.timeout(this.timeoutMs),
47
- });
48
-
49
- const result = await response.json();
50
-
51
- // Handle response errors
52
- if (!response.ok || result.error) {
53
- throw new Error(`Error: ${result.error?.message || response.statusText}`);
54
- }
55
-
56
- return result.result as T['response']; // Return the result if successful
57
-
58
- } catch (error) {
59
- let errorMessage: string | undefined
60
-
61
- // Check if the error is due to timeout or other fetch-related issues
62
- if(typeof error == 'string'){
63
- errorMessage = error
64
- this.logger.error(error)
65
- }
66
- else if (error instanceof DOMException && error.name === 'TimeoutError') {
67
- // If error is an instance DOMException TimeoutError
68
- errorMessage = error.message
69
- this.logger.error(`Request timed out after ${this.timeoutMs} ms`);
70
- }
71
- else if (error instanceof Error) {
72
- // If error is an instance of Error, you can safely access its properties
73
- errorMessage = error.message
74
- this.logger.error(`Request failed with error: ${error.message}`);
75
-
76
- }
77
-
78
- // Retry if allowed
79
- if (attempt < this.maxRetries) {
80
- this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
81
- await new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
82
- } else {
83
- // If no retries are left, throw the final error
84
- throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
85
- }
86
- }
87
- }
88
-
89
- // This line ensures TypeScript is satisfied that a value will always be returned, but
90
- // it should never be reached if the retries fail, as the last attempt should throw an error.
91
- throw new Error('Request failed unexpectedly');
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
+ }
@@ -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
+ }
@@ -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
+ }