@mr-zwets/bchn-api-wrapper 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/{src/interfaces/interfaces.ts → dist/src/interfaces/interfaces.d.ts} +51 -65
- package/dist/src/interfaces/interfaces.js +1 -0
- package/{src/interfaces/restInterfaces/interfaces.ts → dist/src/interfaces/restInterfaces/interfaces.d.ts} +145 -166
- package/dist/src/interfaces/restInterfaces/interfaces.js +1 -0
- package/dist/src/interfaces/rpcInterfaces/blockchain.d.ts +883 -0
- package/dist/src/interfaces/rpcInterfaces/blockchain.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/control.d.ts +60 -0
- package/dist/src/interfaces/rpcInterfaces/control.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/generating.d.ts +19 -0
- package/dist/src/interfaces/rpcInterfaces/generating.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/index.d.ts +9 -0
- package/{src/interfaces/rpcInterfaces/index.ts → dist/src/interfaces/rpcInterfaces/index.js} +1 -3
- package/dist/src/interfaces/rpcInterfaces/mining.d.ts +140 -0
- package/dist/src/interfaces/rpcInterfaces/mining.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/network.d.ts +197 -0
- package/dist/src/interfaces/rpcInterfaces/network.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/rawtransactions.d.ts +304 -0
- package/dist/src/interfaces/rpcInterfaces/rawtransactions.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/util.d.ts +49 -0
- package/dist/src/interfaces/rpcInterfaces/util.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/wallet.d.ts +674 -0
- package/dist/src/interfaces/rpcInterfaces/wallet.js +3 -0
- package/dist/src/interfaces/rpcInterfaces/zmq.d.ts +9 -0
- package/dist/src/interfaces/rpcInterfaces/zmq.js +3 -0
- package/dist/src/restClient.d.ts +29 -0
- package/dist/src/restClient.js +113 -0
- package/dist/src/rpcClient.d.ts +19 -0
- package/dist/src/rpcClient.js +92 -0
- package/dist/src/utils/errors.d.ts +3 -0
- package/dist/src/utils/errors.js +6 -0
- package/dist/src/utils/utils.d.ts +11 -0
- package/dist/src/utils/utils.js +49 -0
- package/package.json +7 -3
- package/.claude/settings.local.json +0 -8
- package/.github/workflows/ci.yaml +0 -36
- package/CLAUDE.md +0 -70
- package/pnpm-lock.yaml +0 -1279
- package/src/index.ts +0 -4
- package/src/interfaces/rpcInterfaces/blockchain.ts +0 -933
- package/src/interfaces/rpcInterfaces/control.ts +0 -68
- package/src/interfaces/rpcInterfaces/generating.ts +0 -23
- package/src/interfaces/rpcInterfaces/mining.ts +0 -151
- package/src/interfaces/rpcInterfaces/network.ts +0 -213
- package/src/interfaces/rpcInterfaces/rawtransactions.ts +0 -332
- package/src/interfaces/rpcInterfaces/util.ts +0 -56
- package/src/interfaces/rpcInterfaces/wallet.ts +0 -728
- package/src/interfaces/rpcInterfaces/zmq.ts +0 -12
- package/src/restClient.ts +0 -134
- package/src/rpcClient.ts +0 -100
- package/src/utils/errors.ts +0 -6
- package/src/utils/utils.ts +0 -55
- package/test/restClient.test.ts +0 -34
- package/test/rpcClient.test.ts +0 -119
- package/test/setupTests.ts +0 -56
- package/test/tsconfig.json +0 -4
- package/tsconfig.json +0 -13
- package/vitest.config.ts +0 -9
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { validateUrl } from "./utils/utils.js";
|
|
11
|
+
/** REST client for read-only BCHN blockchain access via the REST interface. */
|
|
12
|
+
export class BchnRestClient {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
this.baseUrl = validateUrl(config.url);
|
|
16
|
+
this.timeoutMs = (_a = config.timeoutMs) !== null && _a !== void 0 ? _a : 5000;
|
|
17
|
+
this.logger = (_b = config.logger) !== null && _b !== void 0 ? _b : console;
|
|
18
|
+
}
|
|
19
|
+
fetchFromNode(endpoint, format) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
try {
|
|
22
|
+
const response = yield fetch(`${this.baseUrl}/rest/${endpoint}`, {
|
|
23
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(`Error fetching data from ${endpoint}: ${response.statusText}`);
|
|
27
|
+
}
|
|
28
|
+
if (format === 'json') {
|
|
29
|
+
return yield response.json();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return yield response.text(); // For 'bin' and 'hex', return raw text
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
let errorMessage;
|
|
37
|
+
// Check if the error is due to timeout or other fetch-related issues
|
|
38
|
+
if (typeof error === 'string') {
|
|
39
|
+
errorMessage = error;
|
|
40
|
+
this.logger.error(error);
|
|
41
|
+
}
|
|
42
|
+
else if (error instanceof DOMException && error.name === 'TimeoutError') {
|
|
43
|
+
// If error is an instance DOMException TimeoutError
|
|
44
|
+
errorMessage = 'Request timed out';
|
|
45
|
+
this.logger.error(`Request to ${endpoint} timed out after ${this.timeoutMs} ms`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.logger.error(`Unknown error occurred during request to ${endpoint}`);
|
|
49
|
+
throw new Error(`Unknown error: ${error}`);
|
|
50
|
+
}
|
|
51
|
+
// Always rethrow the error after logging
|
|
52
|
+
throw new Error(errorMessage);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Returns transaction details by txid. */
|
|
57
|
+
getTransaction(txid_1) {
|
|
58
|
+
return __awaiter(this, arguments, void 0, function* (txid, format = 'json') {
|
|
59
|
+
return this.fetchFromNode(`tx/${txid}.${format}`, format);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// getBlock Implementation
|
|
63
|
+
getBlock(blockhash_1, includeTxDetails_1) {
|
|
64
|
+
return __awaiter(this, arguments, void 0, function* (blockhash, includeTxDetails, format = 'json') {
|
|
65
|
+
const path = includeTxDetails ? 'block' : 'block/notxdetails';
|
|
66
|
+
return this.fetchFromNode(`${path}/${blockhash}.${format}`, format);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Returns block headers starting from a specific block hash. */
|
|
70
|
+
getBlockHeaders(count_1, blockhash_1) {
|
|
71
|
+
return __awaiter(this, arguments, void 0, function* (count, blockhash, format = 'json') {
|
|
72
|
+
return this.fetchFromNode(`headers/${count}/${blockhash}.${format}`, format);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Returns current chain state (network, sync progress, best block). */
|
|
76
|
+
getChainInfo() {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
return this.fetchFromNode('chaininfo.json', 'json');
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** Queries UTXO set for specific outpoints. Outpoints format: "txid-vout". */
|
|
82
|
+
getUTXOs(checkmempool_1, outpoints_1) {
|
|
83
|
+
return __awaiter(this, arguments, void 0, function* (checkmempool, outpoints, format = 'json') {
|
|
84
|
+
const path = (checkmempool ? 'checkmempool/' : '') + outpoints.join('/');
|
|
85
|
+
const endpoint = `getutxos/${path}.${format}`;
|
|
86
|
+
return this.fetchFromNode(endpoint, format);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** Returns mempool statistics (size, bytes, fee rates). */
|
|
90
|
+
getMempoolInfo() {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
return this.fetchFromNode('mempool/info.json', 'json');
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Returns all transactions currently in the mempool. */
|
|
96
|
+
getMempoolContents() {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
return this.fetchFromNode('mempool/contents.json', 'json');
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** Returns block with bytecode pattern data (v29.0.0+). */
|
|
102
|
+
getBlockWithPatterns(blockhash) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
return this.fetchFromNode(`block/withpatterns/${blockhash}.json`, 'json');
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Returns transaction with bytecode pattern data (v29.0.0+). */
|
|
108
|
+
getTransactionWithPatterns(txid) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
return this.fetchFromNode(`tx/withpatterns/${txid}.json`, 'json');
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RpcClientConfig, RpcRequest } from "./interfaces/interfaces.js";
|
|
2
|
+
/** RPC client for full BCHN node interaction via JSON-RPC. */
|
|
3
|
+
export declare class BchnRpcClient {
|
|
4
|
+
private url;
|
|
5
|
+
private rpcUser;
|
|
6
|
+
private rpcPassword;
|
|
7
|
+
private maxRetries;
|
|
8
|
+
private retryDelayMs;
|
|
9
|
+
private logger;
|
|
10
|
+
private timeoutMs;
|
|
11
|
+
constructor(config: RpcClientConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Sends a typed RPC request to the BCHN node.
|
|
14
|
+
* @example
|
|
15
|
+
* const result = await client.request<GetBlockCount>("getblockcount");
|
|
16
|
+
* const block = await client.request<GetBlockVerbosity1>("getblock", hash, 1);
|
|
17
|
+
*/
|
|
18
|
+
request<T extends RpcRequest>(endpoint: T['method'], ...params: T['params']): Promise<T['response']>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { getRandomId, validateAndConstructUrl } from "./utils/utils.js";
|
|
11
|
+
import { RetryLimitExceededError } from "./utils/errors.js";
|
|
12
|
+
/** RPC client for full BCHN node interaction via JSON-RPC. */
|
|
13
|
+
export class BchnRpcClient {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
var _a, _b, _c, _d;
|
|
16
|
+
this.url = validateAndConstructUrl(config);
|
|
17
|
+
if (!config.rpcUser)
|
|
18
|
+
throw new Error('Need to provide rpcUser in config');
|
|
19
|
+
if (!config.rpcPassword)
|
|
20
|
+
throw new Error('Need to provide rpcPassword in config');
|
|
21
|
+
this.rpcUser = config.rpcUser;
|
|
22
|
+
this.rpcPassword = config.rpcPassword;
|
|
23
|
+
// optional config
|
|
24
|
+
this.maxRetries = (_a = config.maxRetries) !== null && _a !== void 0 ? _a : 0;
|
|
25
|
+
this.retryDelayMs = (_b = config.retryDelayMs) !== null && _b !== void 0 ? _b : 100;
|
|
26
|
+
this.logger = (_c = config.logger) !== null && _c !== void 0 ? _c : console;
|
|
27
|
+
this.timeoutMs = (_d = config.timeoutMs) !== null && _d !== void 0 ? _d : 5000;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Sends a typed RPC request to the BCHN node.
|
|
31
|
+
* @example
|
|
32
|
+
* const result = await client.request<GetBlockCount>("getblockcount");
|
|
33
|
+
* const block = await client.request<GetBlockVerbosity1>("getblock", hash, 1);
|
|
34
|
+
*/
|
|
35
|
+
request(endpoint, ...params) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
var _a;
|
|
38
|
+
const auth = Buffer.from(`${this.rpcUser}:${this.rpcPassword}`).toString('base64');
|
|
39
|
+
// Retry logic
|
|
40
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
41
|
+
try {
|
|
42
|
+
// Send the request with a timeout and retries
|
|
43
|
+
const response = yield fetch(this.url, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'Authorization': `Basic ${auth}`
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: endpoint, params, id: getRandomId() }),
|
|
50
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
51
|
+
});
|
|
52
|
+
const result = yield response.json();
|
|
53
|
+
// Handle response errors
|
|
54
|
+
if (!response.ok || result.error) {
|
|
55
|
+
throw new Error(`Error: ${((_a = result.error) === null || _a === void 0 ? void 0 : _a.message) || response.statusText}`);
|
|
56
|
+
}
|
|
57
|
+
return result.result; // Return the result if successful
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
let errorMessage;
|
|
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
|
+
// Retry if allowed
|
|
77
|
+
if (attempt < this.maxRetries) {
|
|
78
|
+
this.logger.warn(`Retrying request... (${attempt + 1}/${this.maxRetries})`);
|
|
79
|
+
yield new Promise(res => setTimeout(res, this.retryDelayMs)); // Wait before retrying
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// If no retries are left, throw the final error
|
|
83
|
+
throw new RetryLimitExceededError(`Request failed after ${this.maxRetries + 1} attempts: ${errorMessage}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// This line ensures TypeScript is satisfied that a value will always be returned, but
|
|
88
|
+
// it should never be reached if the retries fail, as the last attempt should throw an error.
|
|
89
|
+
throw new Error('Request failed unexpectedly');
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RpcClientConfig } from "../interfaces/interfaces.js";
|
|
2
|
+
export declare function getRandomId(): number;
|
|
3
|
+
export declare function validateAndConstructUrl(config: RpcClientConfig): string;
|
|
4
|
+
export declare function validateUrl(url: string): string;
|
|
5
|
+
export declare enum BchnNetworkPort {
|
|
6
|
+
Mainnet = 8332,
|
|
7
|
+
Testnet = 18332,
|
|
8
|
+
Testnet4 = 28332,
|
|
9
|
+
Scalenet = 38332,
|
|
10
|
+
Regtest = 18443
|
|
11
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function getRandomId() {
|
|
2
|
+
return Math.floor(Math.random() * 100000);
|
|
3
|
+
}
|
|
4
|
+
// A utility function to validate and construct the URL from the RpcClientConfig object
|
|
5
|
+
export function validateAndConstructUrl(config) {
|
|
6
|
+
let url;
|
|
7
|
+
if (isUrlConfig(config)) {
|
|
8
|
+
url = validateUrl(config.url);
|
|
9
|
+
}
|
|
10
|
+
else if (isHostConfig(config)) {
|
|
11
|
+
const { protocol, host, port } = config;
|
|
12
|
+
if (protocol !== 'http' && protocol !== 'https') {
|
|
13
|
+
throw new Error("Protocol should be 'http' or 'https'");
|
|
14
|
+
}
|
|
15
|
+
url = validateUrl(`${protocol}://${host}:${port}`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
throw new Error('Invalid configuration: Either provide the url or protocol/host/port');
|
|
19
|
+
}
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
// A utility function to validate a URL
|
|
23
|
+
export function validateUrl(url) {
|
|
24
|
+
if (!url)
|
|
25
|
+
throw new Error('URL is required');
|
|
26
|
+
try {
|
|
27
|
+
new URL(url);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
throw new Error('Invalid URL format');
|
|
31
|
+
}
|
|
32
|
+
return url;
|
|
33
|
+
}
|
|
34
|
+
// Type guard to check if the config is RpcClientUrlConfig
|
|
35
|
+
function isUrlConfig(config) {
|
|
36
|
+
return 'url' in config;
|
|
37
|
+
}
|
|
38
|
+
// Type guard to check if the config is RpcClientHostConfig
|
|
39
|
+
function isHostConfig(config) {
|
|
40
|
+
return 'protocol' in config && 'hostname' in config && 'port' in config;
|
|
41
|
+
}
|
|
42
|
+
export var BchnNetworkPort;
|
|
43
|
+
(function (BchnNetworkPort) {
|
|
44
|
+
BchnNetworkPort[BchnNetworkPort["Mainnet"] = 8332] = "Mainnet";
|
|
45
|
+
BchnNetworkPort[BchnNetworkPort["Testnet"] = 18332] = "Testnet";
|
|
46
|
+
BchnNetworkPort[BchnNetworkPort["Testnet4"] = 28332] = "Testnet4";
|
|
47
|
+
BchnNetworkPort[BchnNetworkPort["Scalenet"] = 38332] = "Scalenet";
|
|
48
|
+
BchnNetworkPort[BchnNetworkPort["Regtest"] = 18443] = "Regtest";
|
|
49
|
+
})(BchnNetworkPort || (BchnNetworkPort = {}));
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mr-zwets/bchn-api-wrapper",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "a Typescript wrapper for interacting with the Bitcoin Cash Node (BCHN) API ",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
6
9
|
"main": "dist/index.js",
|
|
7
10
|
"author": "mr-zwets",
|
|
8
11
|
"scripts": {
|
|
12
|
+
"prepare": "pnpm build",
|
|
9
13
|
"build": "tsc",
|
|
10
14
|
"test": "vitest"
|
|
11
15
|
},
|
|
@@ -13,8 +17,8 @@
|
|
|
13
17
|
"devDependencies": {
|
|
14
18
|
"@types/node": "^24.10.4",
|
|
15
19
|
"msw": "^2.12.4",
|
|
16
|
-
"typescript": "^
|
|
17
|
-
"vitest": "^4.
|
|
20
|
+
"typescript": "^6.0.2",
|
|
21
|
+
"vitest": "^4.1.2"
|
|
18
22
|
},
|
|
19
23
|
"directories": {
|
|
20
24
|
"test": "test"
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
check:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
|
|
16
|
-
steps:
|
|
17
|
-
- name: Checkout
|
|
18
|
-
uses: actions/checkout@v4
|
|
19
|
-
|
|
20
|
-
- name: Set up pnpm
|
|
21
|
-
uses: pnpm/action-setup@v4
|
|
22
|
-
|
|
23
|
-
- name: Set up Node.js
|
|
24
|
-
uses: actions/setup-node@v4
|
|
25
|
-
with:
|
|
26
|
-
node-version: 22
|
|
27
|
-
cache: pnpm
|
|
28
|
-
|
|
29
|
-
- name: Install dependencies
|
|
30
|
-
run: pnpm install
|
|
31
|
-
|
|
32
|
-
- name: Build
|
|
33
|
-
run: pnpm build
|
|
34
|
-
|
|
35
|
-
- name: Run tests
|
|
36
|
-
run: pnpm test run
|
package/CLAUDE.md
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Build & Test Commands
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pnpm build # Compile TypeScript to dist/
|
|
9
|
-
pnpm test # Run all tests with Vitest
|
|
10
|
-
pnpm test <file> # Run a specific test file
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Architecture
|
|
14
|
-
|
|
15
|
-
TypeScript library providing type-safe wrappers for Bitcoin Cash Node (BCHN) JSON-RPC and REST interfaces. Types compatible with **BCHN v29.0.0**. Zero runtime dependencies.
|
|
16
|
-
|
|
17
|
-
### Two Client Classes
|
|
18
|
-
|
|
19
|
-
**BchnRestClient** (`src/restClient.ts`)
|
|
20
|
-
- Wraps 11 REST endpoints for read-only blockchain access-
|
|
21
|
-
- Class with dedicated methods for each endpoint
|
|
22
|
-
- Supports format options (`json`, `hex`, `bin`) via conditional return types
|
|
23
|
-
- Uses function overloads for type-safe returns based on parameters (e.g., `getBlock` with `includeTxDetails`)
|
|
24
|
-
|
|
25
|
-
**BchnRpcClient** (`src/rpcClient.ts`)
|
|
26
|
-
- Wraps 136 RPC commands for full node interaction
|
|
27
|
-
- Single generic `request<T extends RpcRequest>()` method
|
|
28
|
-
- Type safety achieved through discriminated union interfaces
|
|
29
|
-
- Each RPC command interface defines: `method`, `params[]`, and `response` type
|
|
30
|
-
|
|
31
|
-
### Interface Organization
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
src/interfaces/
|
|
35
|
-
├── interfaces.ts # Config types, RpcRequest base, shared types
|
|
36
|
-
├── restInterfaces/ # REST response types
|
|
37
|
-
└── rpcInterfaces/ # 136 RPC command interfaces across 9 files
|
|
38
|
-
├── blockchain.ts # 33 commands
|
|
39
|
-
├── wallet.ts # 52 commands
|
|
40
|
-
├── rawtransactions.ts
|
|
41
|
-
├── network.ts # 14 commands
|
|
42
|
-
├── mining.ts
|
|
43
|
-
├── control.ts # 6 commands
|
|
44
|
-
├── util.ts
|
|
45
|
-
├── generating.ts
|
|
46
|
-
└── zmq.ts
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### RPC Type Pattern
|
|
50
|
-
|
|
51
|
-
RPC interfaces follow this pattern for full type inference:
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
export interface GetBlockVerbosity1 extends GetBlockBase {
|
|
55
|
-
params: [blockhash: string, verbosity?: 1 | true];
|
|
56
|
-
response: { hash: string; /* ... */ };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Usage - params and response are fully typed:
|
|
60
|
-
const result = await rpcClient.request<GetBlockVerbosity1>("getblock", hash, 1);
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Configuration Types
|
|
64
|
-
|
|
65
|
-
- `RpcClientConfig`: Supports URL-based (`{url, rpcUser, rpcPassword}`) or host-based (`{protocol, host, port, rpcUser, rpcPassword}`) configuration
|
|
66
|
-
- `RestClientConfig`: Requires `url`, optional `logger` and `timeoutMs`
|
|
67
|
-
|
|
68
|
-
### Testing
|
|
69
|
-
|
|
70
|
-
Tests use Vitest with MSW (Mock Service Worker) for HTTP interception. Mock server setup is in `test/setupTests.ts`.
|