@xdc.org/interaction-detector 1.0.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 +767 -0
- package/dist/checkpoint/checkpoint.d.ts +32 -0
- package/dist/checkpoint/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint/checkpoint.js +68 -0
- package/dist/checkpoint/checkpoint.js.map +1 -0
- package/dist/decoder/abi-registry.d.ts +49 -0
- package/dist/decoder/abi-registry.d.ts.map +1 -0
- package/dist/decoder/abi-registry.js +88 -0
- package/dist/decoder/abi-registry.js.map +1 -0
- package/dist/decoder/event-decoder.d.ts +31 -0
- package/dist/decoder/event-decoder.d.ts.map +1 -0
- package/dist/decoder/event-decoder.js +142 -0
- package/dist/decoder/event-decoder.js.map +1 -0
- package/dist/explorer/explorer-client.d.ts +65 -0
- package/dist/explorer/explorer-client.d.ts.map +1 -0
- package/dist/explorer/explorer-client.js +164 -0
- package/dist/explorer/explorer-client.js.map +1 -0
- package/dist/explorer/rate-limiter.d.ts +31 -0
- package/dist/explorer/rate-limiter.d.ts.map +1 -0
- package/dist/explorer/rate-limiter.js +79 -0
- package/dist/explorer/rate-limiter.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/rpc/rpc-client.d.ts +70 -0
- package/dist/rpc/rpc-client.d.ts.map +1 -0
- package/dist/rpc/rpc-client.js +136 -0
- package/dist/rpc/rpc-client.js.map +1 -0
- package/dist/rpc/ws-manager.d.ts +27 -0
- package/dist/rpc/ws-manager.d.ts.map +1 -0
- package/dist/rpc/ws-manager.js +161 -0
- package/dist/rpc/ws-manager.js.map +1 -0
- package/dist/scanner/block-scanner.d.ts +45 -0
- package/dist/scanner/block-scanner.d.ts.map +1 -0
- package/dist/scanner/block-scanner.js +180 -0
- package/dist/scanner/block-scanner.js.map +1 -0
- package/dist/tracer/call-tree-parser.d.ts +25 -0
- package/dist/tracer/call-tree-parser.d.ts.map +1 -0
- package/dist/tracer/call-tree-parser.js +80 -0
- package/dist/tracer/call-tree-parser.js.map +1 -0
- package/dist/tracer/state-diff-parser.d.ts +13 -0
- package/dist/tracer/state-diff-parser.d.ts.map +1 -0
- package/dist/tracer/state-diff-parser.js +70 -0
- package/dist/tracer/state-diff-parser.js.map +1 -0
- package/dist/tracer/transaction-tracer.d.ts +52 -0
- package/dist/tracer/transaction-tracer.d.ts.map +1 -0
- package/dist/tracer/transaction-tracer.js +107 -0
- package/dist/tracer/transaction-tracer.js.map +1 -0
- package/dist/types/index.d.ts +262 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/address.d.ts +29 -0
- package/dist/utils/address.d.ts.map +1 -0
- package/dist/utils/address.js +49 -0
- package/dist/utils/address.js.map +1 -0
- package/dist/utils/format.d.ts +20 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +53 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +58 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/watcher/contract-watcher.d.ts +56 -0
- package/dist/watcher/contract-watcher.d.ts.map +1 -0
- package/dist/watcher/contract-watcher.js +353 -0
- package/dist/watcher/contract-watcher.js.map +1 -0
- package/dist/watcher/log-poller.d.ts +24 -0
- package/dist/watcher/log-poller.d.ts.map +1 -0
- package/dist/watcher/log-poller.js +82 -0
- package/dist/watcher/log-poller.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Etherscan-compatible block explorer API client.
|
|
3
|
+
*
|
|
4
|
+
* Works with: XDCScan, Etherscan v2, BSCScan, PolygonScan, and any
|
|
5
|
+
* Etherscan-compatible API by changing the base URL.
|
|
6
|
+
*/
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import { Logger } from '../utils/logger.js';
|
|
9
|
+
import { RateLimiter } from './rate-limiter.js';
|
|
10
|
+
export class ExplorerClient {
|
|
11
|
+
logger;
|
|
12
|
+
baseUrl;
|
|
13
|
+
apiKey;
|
|
14
|
+
chainId;
|
|
15
|
+
rateLimiter;
|
|
16
|
+
constructor(config, logLevel) {
|
|
17
|
+
this.baseUrl = config.apiUrl.replace(/\/+$/, '');
|
|
18
|
+
this.apiKey = config.apiKey;
|
|
19
|
+
this.chainId = config.chainId;
|
|
20
|
+
this.rateLimiter = new RateLimiter(config.rateLimitPerSec ?? 5);
|
|
21
|
+
this.logger = new Logger('ExplorerClient', logLevel ?? 'info');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get external transactions for an address (txlist).
|
|
25
|
+
*/
|
|
26
|
+
async getTransactions(address, options) {
|
|
27
|
+
const params = {
|
|
28
|
+
module: 'account',
|
|
29
|
+
action: 'txlist',
|
|
30
|
+
address,
|
|
31
|
+
startblock: String(options?.startBlock ?? 0),
|
|
32
|
+
endblock: String(options?.endBlock ?? 99999999),
|
|
33
|
+
sort: options?.sort ?? 'asc',
|
|
34
|
+
};
|
|
35
|
+
const results = await this.request(params);
|
|
36
|
+
return (results ?? []).map((tx) => ({ ...tx, txType: 'external' }));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get internal transactions for an address (txlistinternal).
|
|
40
|
+
*/
|
|
41
|
+
async getInternalTransactions(address, options) {
|
|
42
|
+
const params = {
|
|
43
|
+
module: 'account',
|
|
44
|
+
action: 'txlistinternal',
|
|
45
|
+
address,
|
|
46
|
+
startblock: String(options?.startBlock ?? 0),
|
|
47
|
+
endblock: String(options?.endBlock ?? 99999999),
|
|
48
|
+
sort: options?.sort ?? 'asc',
|
|
49
|
+
};
|
|
50
|
+
const results = await this.request(params);
|
|
51
|
+
return (results ?? []).map((tx) => ({ ...tx, txType: 'internal' }));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get internal transactions within a specific transaction (txlistinternal by txhash).
|
|
55
|
+
*/
|
|
56
|
+
async getInternalTransactionsByTx(txHash) {
|
|
57
|
+
const params = {
|
|
58
|
+
module: 'account',
|
|
59
|
+
action: 'txlistinternal',
|
|
60
|
+
txhash: txHash,
|
|
61
|
+
};
|
|
62
|
+
const results = await this.request(params);
|
|
63
|
+
return (results ?? []).map((tx) => ({ ...tx, txType: 'internal' }));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get event logs for an address (getLogs).
|
|
67
|
+
*/
|
|
68
|
+
async getLogs(address, options) {
|
|
69
|
+
const params = {
|
|
70
|
+
module: 'logs',
|
|
71
|
+
action: 'getLogs',
|
|
72
|
+
address,
|
|
73
|
+
fromBlock: String(options?.fromBlock ?? 0),
|
|
74
|
+
toBlock: String(options?.toBlock ?? 99999999),
|
|
75
|
+
};
|
|
76
|
+
if (options?.topic0) {
|
|
77
|
+
params.topic0 = options.topic0;
|
|
78
|
+
}
|
|
79
|
+
return (await this.request(params)) ?? [];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get verified contract ABI from explorer.
|
|
83
|
+
*/
|
|
84
|
+
async getContractABI(address) {
|
|
85
|
+
const params = {
|
|
86
|
+
module: 'contract',
|
|
87
|
+
action: 'getabi',
|
|
88
|
+
address,
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const result = await this.request(params);
|
|
92
|
+
if (result && typeof result === 'string') {
|
|
93
|
+
return JSON.parse(result);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
this.logger.debug(`Failed to fetch ABI for ${address}: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get ERC-20 / XRC-20 token transfers for an address.
|
|
103
|
+
*/
|
|
104
|
+
async getTokenTransfers(address, options) {
|
|
105
|
+
const params = {
|
|
106
|
+
module: 'account',
|
|
107
|
+
action: 'tokentx',
|
|
108
|
+
address,
|
|
109
|
+
startblock: String(options?.startBlock ?? 0),
|
|
110
|
+
endblock: String(options?.endBlock ?? 99999999),
|
|
111
|
+
sort: options?.sort ?? 'asc',
|
|
112
|
+
};
|
|
113
|
+
return (await this.request(params)) ?? [];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get native balance for an address.
|
|
117
|
+
*/
|
|
118
|
+
async getBalance(address) {
|
|
119
|
+
const params = {
|
|
120
|
+
module: 'account',
|
|
121
|
+
action: 'balance',
|
|
122
|
+
address,
|
|
123
|
+
};
|
|
124
|
+
return (await this.request(params)) ?? '0';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Destroy the client and clean up rate limiter.
|
|
128
|
+
*/
|
|
129
|
+
destroy() {
|
|
130
|
+
this.rateLimiter.destroy();
|
|
131
|
+
}
|
|
132
|
+
// ─── Internal ─────────────────────────────────────────────────────────
|
|
133
|
+
async request(params) {
|
|
134
|
+
await this.rateLimiter.acquire();
|
|
135
|
+
if (this.apiKey) {
|
|
136
|
+
params.apikey = this.apiKey;
|
|
137
|
+
}
|
|
138
|
+
// Add chainId for Etherscan v2 unified endpoint
|
|
139
|
+
if (this.chainId && this.baseUrl.includes('etherscan.io')) {
|
|
140
|
+
params.chainid = String(this.chainId);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const response = await axios.get(this.baseUrl, {
|
|
144
|
+
params,
|
|
145
|
+
timeout: 15000,
|
|
146
|
+
});
|
|
147
|
+
const data = response.data;
|
|
148
|
+
if (data.status === '0' && data.message === 'No transactions found') {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
if (data.status === '0' && data.message !== 'No transactions found') {
|
|
152
|
+
const msg = data.result || data.message || 'Unknown error';
|
|
153
|
+
this.logger.warn(`Explorer API error: ${msg}`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return data.result;
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
this.logger.error(`Explorer request failed: ${err.message}`);
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=explorer-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer-client.js","sourceRoot":"","sources":["../../src/explorer/explorer-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-bucket rate limiter for API calls.
|
|
3
|
+
* Ensures we don't exceed N requests per second.
|
|
4
|
+
*/
|
|
5
|
+
export declare class RateLimiter {
|
|
6
|
+
private readonly maxTokens;
|
|
7
|
+
private readonly refillRateMs;
|
|
8
|
+
private tokens;
|
|
9
|
+
private lastRefill;
|
|
10
|
+
private queue;
|
|
11
|
+
private drainTimer;
|
|
12
|
+
/**
|
|
13
|
+
* @param maxPerSecond Maximum requests per second (default: 5)
|
|
14
|
+
*/
|
|
15
|
+
constructor(maxPerSecond?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Acquire a token. Resolves when a request slot is available.
|
|
18
|
+
*/
|
|
19
|
+
acquire(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Get the number of queued requests waiting.
|
|
22
|
+
*/
|
|
23
|
+
get pending(): number;
|
|
24
|
+
/**
|
|
25
|
+
* Destroy the rate limiter and clear timers.
|
|
26
|
+
*/
|
|
27
|
+
destroy(): void;
|
|
28
|
+
private refill;
|
|
29
|
+
private startDrain;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/explorer/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,UAAU,CAA+B;IAEjD;;OAEG;gBACS,YAAY,GAAE,MAAU;IAOpC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,UAAU;CAkBnB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-bucket rate limiter for API calls.
|
|
3
|
+
* Ensures we don't exceed N requests per second.
|
|
4
|
+
*/
|
|
5
|
+
export class RateLimiter {
|
|
6
|
+
maxTokens;
|
|
7
|
+
refillRateMs;
|
|
8
|
+
tokens;
|
|
9
|
+
lastRefill;
|
|
10
|
+
queue = [];
|
|
11
|
+
drainTimer = null;
|
|
12
|
+
/**
|
|
13
|
+
* @param maxPerSecond Maximum requests per second (default: 5)
|
|
14
|
+
*/
|
|
15
|
+
constructor(maxPerSecond = 5) {
|
|
16
|
+
this.maxTokens = maxPerSecond;
|
|
17
|
+
this.refillRateMs = 1000 / maxPerSecond;
|
|
18
|
+
this.tokens = maxPerSecond;
|
|
19
|
+
this.lastRefill = Date.now();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Acquire a token. Resolves when a request slot is available.
|
|
23
|
+
*/
|
|
24
|
+
async acquire() {
|
|
25
|
+
this.refill();
|
|
26
|
+
if (this.tokens >= 1) {
|
|
27
|
+
this.tokens -= 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Queue the request — it will be resolved when tokens are available
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
this.queue.push({ resolve });
|
|
33
|
+
this.startDrain();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the number of queued requests waiting.
|
|
38
|
+
*/
|
|
39
|
+
get pending() {
|
|
40
|
+
return this.queue.length;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Destroy the rate limiter and clear timers.
|
|
44
|
+
*/
|
|
45
|
+
destroy() {
|
|
46
|
+
if (this.drainTimer) {
|
|
47
|
+
clearInterval(this.drainTimer);
|
|
48
|
+
this.drainTimer = null;
|
|
49
|
+
}
|
|
50
|
+
for (const item of this.queue) {
|
|
51
|
+
item.resolve();
|
|
52
|
+
}
|
|
53
|
+
this.queue = [];
|
|
54
|
+
}
|
|
55
|
+
refill() {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const elapsed = now - this.lastRefill;
|
|
58
|
+
const newTokens = elapsed / this.refillRateMs;
|
|
59
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
60
|
+
this.lastRefill = now;
|
|
61
|
+
}
|
|
62
|
+
startDrain() {
|
|
63
|
+
if (this.drainTimer)
|
|
64
|
+
return;
|
|
65
|
+
this.drainTimer = setInterval(() => {
|
|
66
|
+
this.refill();
|
|
67
|
+
while (this.queue.length > 0 && this.tokens >= 1) {
|
|
68
|
+
this.tokens -= 1;
|
|
69
|
+
const item = this.queue.shift();
|
|
70
|
+
item.resolve();
|
|
71
|
+
}
|
|
72
|
+
if (this.queue.length === 0) {
|
|
73
|
+
clearInterval(this.drainTimer);
|
|
74
|
+
this.drainTimer = null;
|
|
75
|
+
}
|
|
76
|
+
}, this.refillRateMs);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/explorer/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,WAAW;IACL,SAAS,CAAS;IAClB,YAAY,CAAS;IAC9B,MAAM,CAAS;IACf,UAAU,CAAS;IACnB,KAAK,GAAmC,EAAE,CAAC;IAC3C,UAAU,GAA0B,IAAI,CAAC;IAEjD;;OAEG;IACH,YAAY,eAAuB,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC;QAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,YAAY,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,MAAM,EAAE,CAAC;QAEd,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QAED,oEAAoE;QACpE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAC7B,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAEO,MAAM;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QACtC,MAAM,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACxB,CAAC;IAEO,UAAU;QAChB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAE5B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YAEd,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;gBACjB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,CAAC;gBACjC,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5B,aAAa,CAAC,IAAI,CAAC,UAAW,CAAC,CAAC;gBAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACzB,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACxB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XDC Interaction Detector — Public API
|
|
3
|
+
*/
|
|
4
|
+
export { ContractWatcher } from './watcher/contract-watcher.js';
|
|
5
|
+
export { BlockScanner } from './scanner/block-scanner.js';
|
|
6
|
+
export type { BlockScannerConfig } from './scanner/block-scanner.js';
|
|
7
|
+
export { TransactionTracer } from './tracer/transaction-tracer.js';
|
|
8
|
+
export type { TransactionTracerConfig } from './tracer/transaction-tracer.js';
|
|
9
|
+
export { ExplorerClient } from './explorer/explorer-client.js';
|
|
10
|
+
export { RateLimiter } from './explorer/rate-limiter.js';
|
|
11
|
+
export { RpcClient } from './rpc/rpc-client.js';
|
|
12
|
+
export type { RpcClientOptions } from './rpc/rpc-client.js';
|
|
13
|
+
export { WsManager } from './rpc/ws-manager.js';
|
|
14
|
+
export { EventDecoder } from './decoder/event-decoder.js';
|
|
15
|
+
export { AbiRegistry } from './decoder/abi-registry.js';
|
|
16
|
+
export type { RegisteredContract } from './decoder/abi-registry.js';
|
|
17
|
+
export { MemoryCheckpoint, FileCheckpoint, createCheckpointBackend } from './checkpoint/checkpoint.js';
|
|
18
|
+
export { parseCallTree, flattenCallTree, extractInvolvedContracts, findCallsTo } from './tracer/call-tree-parser.js';
|
|
19
|
+
export { parseStateDiffs, parseBalanceChanges } from './tracer/state-diff-parser.js';
|
|
20
|
+
export { normalizeAddress, toXdcAddress, toEthAddress, isAddress, addressEqual } from './utils/address.js';
|
|
21
|
+
export { formatXDC, formatWei, parseHexOrDecimal, toHex, shortAddress, sleep } from './utils/format.js';
|
|
22
|
+
export { Logger } from './utils/logger.js';
|
|
23
|
+
export type { InteractionDetectorConfig, ContractConfig, ExplorerConfig, PollingConfig, WsConfig, CheckpointConfig, CheckpointBackend, RawLog, DecodedEvent, InteractionType, ContractInteraction, TracerResult, CallTreeNode, StateDiff, BalanceChange, ExplorerTransaction, ScanOptions, LogLevel, } from './types/index.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAGvG,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACrH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAGrF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACxG,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,YAAY,EACV,yBAAyB,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAClF,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAC1E,mBAAmB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EACzE,mBAAmB,EAAE,WAAW,EAAE,QAAQ,GAC3C,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XDC Interaction Detector — Public API
|
|
3
|
+
*/
|
|
4
|
+
// Core classes
|
|
5
|
+
export { ContractWatcher } from './watcher/contract-watcher.js';
|
|
6
|
+
export { BlockScanner } from './scanner/block-scanner.js';
|
|
7
|
+
export { TransactionTracer } from './tracer/transaction-tracer.js';
|
|
8
|
+
// Explorer
|
|
9
|
+
export { ExplorerClient } from './explorer/explorer-client.js';
|
|
10
|
+
export { RateLimiter } from './explorer/rate-limiter.js';
|
|
11
|
+
// RPC
|
|
12
|
+
export { RpcClient } from './rpc/rpc-client.js';
|
|
13
|
+
export { WsManager } from './rpc/ws-manager.js';
|
|
14
|
+
// Decoder
|
|
15
|
+
export { EventDecoder } from './decoder/event-decoder.js';
|
|
16
|
+
export { AbiRegistry } from './decoder/abi-registry.js';
|
|
17
|
+
// Checkpoint
|
|
18
|
+
export { MemoryCheckpoint, FileCheckpoint, createCheckpointBackend } from './checkpoint/checkpoint.js';
|
|
19
|
+
// Tracer utilities
|
|
20
|
+
export { parseCallTree, flattenCallTree, extractInvolvedContracts, findCallsTo } from './tracer/call-tree-parser.js';
|
|
21
|
+
export { parseStateDiffs, parseBalanceChanges } from './tracer/state-diff-parser.js';
|
|
22
|
+
// Utilities
|
|
23
|
+
export { normalizeAddress, toXdcAddress, toEthAddress, isAddress, addressEqual } from './utils/address.js';
|
|
24
|
+
export { formatXDC, formatWei, parseHexOrDecimal, toHex, shortAddress, sleep } from './utils/format.js';
|
|
25
|
+
export { Logger } from './utils/logger.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAe;AACf,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGnE,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,MAAM;AACN,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,aAAa;AACb,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAEvG,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACrH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAErF,YAAY;AACZ,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACxG,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone JSON-RPC client with retry and fallback.
|
|
3
|
+
*/
|
|
4
|
+
import type { RawLog, LogLevel } from '../types/index.js';
|
|
5
|
+
export interface RpcClientOptions {
|
|
6
|
+
/** Maximum retry attempts (default: 3) */
|
|
7
|
+
maxRetries?: number;
|
|
8
|
+
/** Base delay between retries in ms (default: 1000) */
|
|
9
|
+
retryDelayMs?: number;
|
|
10
|
+
/** Request timeout in ms (default: 30000) */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
/** Fallback RPC URLs */
|
|
13
|
+
fallbackUrls?: string[];
|
|
14
|
+
/** Log level */
|
|
15
|
+
logLevel?: LogLevel;
|
|
16
|
+
}
|
|
17
|
+
export declare class RpcClient {
|
|
18
|
+
private readonly logger;
|
|
19
|
+
private readonly primaryUrl;
|
|
20
|
+
private readonly fallbackUrls;
|
|
21
|
+
private readonly maxRetries;
|
|
22
|
+
private readonly retryDelayMs;
|
|
23
|
+
private readonly timeoutMs;
|
|
24
|
+
private requestId;
|
|
25
|
+
constructor(primaryUrl: string, options?: RpcClientOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Call a JSON-RPC method with retry and fallback logic.
|
|
28
|
+
*/
|
|
29
|
+
call<T = any>(method: string, params?: any[]): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Get the current block number.
|
|
32
|
+
*/
|
|
33
|
+
getBlockNumber(): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Get logs matching a filter.
|
|
36
|
+
*/
|
|
37
|
+
getLogs(filter: {
|
|
38
|
+
address?: string | string[];
|
|
39
|
+
topics?: (string | string[] | null)[];
|
|
40
|
+
fromBlock: string | number;
|
|
41
|
+
toBlock: string | number;
|
|
42
|
+
}): Promise<RawLog[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Get a block by number (with or without full tx objects).
|
|
45
|
+
*/
|
|
46
|
+
getBlockByNumber(blockNumber: number, includeTransactions?: boolean): Promise<any>;
|
|
47
|
+
/**
|
|
48
|
+
* Get block timestamp (cached internally by callers for efficiency).
|
|
49
|
+
*/
|
|
50
|
+
getBlockTimestamp(blockNumber: number): Promise<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Get transaction receipt.
|
|
53
|
+
*/
|
|
54
|
+
getTransactionReceipt(txHash: string): Promise<any>;
|
|
55
|
+
/**
|
|
56
|
+
* Get transaction by hash.
|
|
57
|
+
*/
|
|
58
|
+
getTransaction(txHash: string): Promise<any>;
|
|
59
|
+
/**
|
|
60
|
+
* Call debug_traceTransaction (requires debug namespace enabled).
|
|
61
|
+
*/
|
|
62
|
+
traceTransaction(txHash: string, tracerConfig: any): Promise<any>;
|
|
63
|
+
/**
|
|
64
|
+
* Get storage at a specific slot.
|
|
65
|
+
*/
|
|
66
|
+
getStorageAt(address: string, slot: string, blockTag?: string): Promise<string>;
|
|
67
|
+
private callWithRetry;
|
|
68
|
+
private execute;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=rpc-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/rpc/rpc-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAgBD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAK;gBAEV,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB;IAS1D;;OAEG;IACG,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAkBnE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAKvC;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE;QACpB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC5B,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACtC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;QAC3B,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAUrB;;OAEG;IACG,gBAAgB,CACpB,WAAW,EAAE,MAAM,EACnB,mBAAmB,UAAQ,GAC1B,OAAO,CAAC,GAAG,CAAC;IAIf;;OAEG;IACG,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM7D;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIzD;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIlD;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAIvE;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,OAAO,CAAC,MAAM,CAAC;YAMzE,aAAa;YAmBb,OAAO;CAqBtB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone JSON-RPC client with retry and fallback.
|
|
3
|
+
*/
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import { Logger } from '../utils/logger.js';
|
|
6
|
+
import { sleep } from '../utils/format.js';
|
|
7
|
+
import { toHex } from '../utils/format.js';
|
|
8
|
+
export class RpcClient {
|
|
9
|
+
logger;
|
|
10
|
+
primaryUrl;
|
|
11
|
+
fallbackUrls;
|
|
12
|
+
maxRetries;
|
|
13
|
+
retryDelayMs;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
requestId = 1;
|
|
16
|
+
constructor(primaryUrl, options) {
|
|
17
|
+
this.primaryUrl = primaryUrl;
|
|
18
|
+
this.fallbackUrls = options?.fallbackUrls ?? [];
|
|
19
|
+
this.maxRetries = options?.maxRetries ?? 3;
|
|
20
|
+
this.retryDelayMs = options?.retryDelayMs ?? 1000;
|
|
21
|
+
this.timeoutMs = options?.timeoutMs ?? 30000;
|
|
22
|
+
this.logger = new Logger('RpcClient', options?.logLevel ?? 'warn');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Call a JSON-RPC method with retry and fallback logic.
|
|
26
|
+
*/
|
|
27
|
+
async call(method, params = []) {
|
|
28
|
+
const urls = [this.primaryUrl, ...this.fallbackUrls];
|
|
29
|
+
let lastError = null;
|
|
30
|
+
for (const url of urls) {
|
|
31
|
+
try {
|
|
32
|
+
return await this.callWithRetry(url, method, params);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
lastError = error;
|
|
36
|
+
this.logger.warn(`RPC ${method} failed on ${url}: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw lastError || new Error(`RPC ${method} failed on all endpoints`);
|
|
40
|
+
}
|
|
41
|
+
// ─── Convenience Methods ──────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Get the current block number.
|
|
44
|
+
*/
|
|
45
|
+
async getBlockNumber() {
|
|
46
|
+
const hex = await this.call('eth_blockNumber');
|
|
47
|
+
return parseInt(hex, 16);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get logs matching a filter.
|
|
51
|
+
*/
|
|
52
|
+
async getLogs(filter) {
|
|
53
|
+
const params = {
|
|
54
|
+
address: filter.address,
|
|
55
|
+
topics: filter.topics,
|
|
56
|
+
fromBlock: typeof filter.fromBlock === 'number' ? toHex(filter.fromBlock) : filter.fromBlock,
|
|
57
|
+
toBlock: typeof filter.toBlock === 'number' ? toHex(filter.toBlock) : filter.toBlock,
|
|
58
|
+
};
|
|
59
|
+
return this.call('eth_getLogs', [params]);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a block by number (with or without full tx objects).
|
|
63
|
+
*/
|
|
64
|
+
async getBlockByNumber(blockNumber, includeTransactions = false) {
|
|
65
|
+
return this.call('eth_getBlockByNumber', [toHex(blockNumber), includeTransactions]);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get block timestamp (cached internally by callers for efficiency).
|
|
69
|
+
*/
|
|
70
|
+
async getBlockTimestamp(blockNumber) {
|
|
71
|
+
const block = await this.getBlockByNumber(blockNumber, false);
|
|
72
|
+
if (!block || !block.timestamp)
|
|
73
|
+
return 0;
|
|
74
|
+
return parseInt(block.timestamp, 16);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get transaction receipt.
|
|
78
|
+
*/
|
|
79
|
+
async getTransactionReceipt(txHash) {
|
|
80
|
+
return this.call('eth_getTransactionReceipt', [txHash]);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get transaction by hash.
|
|
84
|
+
*/
|
|
85
|
+
async getTransaction(txHash) {
|
|
86
|
+
return this.call('eth_getTransactionByHash', [txHash]);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Call debug_traceTransaction (requires debug namespace enabled).
|
|
90
|
+
*/
|
|
91
|
+
async traceTransaction(txHash, tracerConfig) {
|
|
92
|
+
return this.call('debug_traceTransaction', [txHash, tracerConfig]);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get storage at a specific slot.
|
|
96
|
+
*/
|
|
97
|
+
async getStorageAt(address, slot, blockTag = 'latest') {
|
|
98
|
+
return this.call('eth_getStorageAt', [address, slot, blockTag]);
|
|
99
|
+
}
|
|
100
|
+
// ─── Internal ─────────────────────────────────────────────────────────
|
|
101
|
+
async callWithRetry(url, method, params) {
|
|
102
|
+
let lastError = null;
|
|
103
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
104
|
+
try {
|
|
105
|
+
return await this.execute(url, method, params);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
lastError = error;
|
|
109
|
+
if (attempt < this.maxRetries - 1) {
|
|
110
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
111
|
+
this.logger.debug(`Retry ${attempt + 1}/${this.maxRetries} for ${method} after ${delay}ms`);
|
|
112
|
+
await sleep(delay);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw lastError || new Error(`RPC ${method} failed after ${this.maxRetries} attempts`);
|
|
117
|
+
}
|
|
118
|
+
async execute(url, method, params) {
|
|
119
|
+
const request = {
|
|
120
|
+
jsonrpc: '2.0',
|
|
121
|
+
method,
|
|
122
|
+
params,
|
|
123
|
+
id: this.requestId++,
|
|
124
|
+
};
|
|
125
|
+
const config = {
|
|
126
|
+
timeout: this.timeoutMs,
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
};
|
|
129
|
+
const response = await axios.post(url, request, config);
|
|
130
|
+
if (response.data.error) {
|
|
131
|
+
throw new Error(`RPC error: ${response.data.error.message} (code: ${response.data.error.code})`);
|
|
132
|
+
}
|
|
133
|
+
return response.data.result;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=rpc-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/rpc/rpc-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket connection lifecycle manager.
|
|
3
|
+
* Handles connect, heartbeat, auto-reconnect, and event forwarding.
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import type { WsConfig, LogLevel } from '../types/index.js';
|
|
7
|
+
export declare class WsManager extends EventEmitter {
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly wsUrl;
|
|
10
|
+
private readonly chainId;
|
|
11
|
+
private readonly config;
|
|
12
|
+
private provider;
|
|
13
|
+
private heartbeatTimer;
|
|
14
|
+
private reconnectAttempts;
|
|
15
|
+
private reconnectScheduled;
|
|
16
|
+
private _isConnected;
|
|
17
|
+
private destroyed;
|
|
18
|
+
constructor(wsUrl: string, chainId: number, wsConfig?: WsConfig, logLevel?: LogLevel);
|
|
19
|
+
get isConnected(): boolean;
|
|
20
|
+
connect(addresses: string[]): Promise<void>;
|
|
21
|
+
disconnect(): Promise<void>;
|
|
22
|
+
private startHeartbeat;
|
|
23
|
+
private stopHeartbeat;
|
|
24
|
+
private scheduleReconnect;
|
|
25
|
+
private destroyConnection;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ws-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-manager.d.ts","sourceRoot":"","sources":["../../src/rpc/ws-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAW5D,qBAAa,SAAU,SAAQ,YAAY;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;gBAEd,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ;IAQpF,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyD3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,iBAAiB;YA+BX,iBAAiB;CAShC"}
|