@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.
Files changed (74) hide show
  1. package/README.md +767 -0
  2. package/dist/checkpoint/checkpoint.d.ts +32 -0
  3. package/dist/checkpoint/checkpoint.d.ts.map +1 -0
  4. package/dist/checkpoint/checkpoint.js +68 -0
  5. package/dist/checkpoint/checkpoint.js.map +1 -0
  6. package/dist/decoder/abi-registry.d.ts +49 -0
  7. package/dist/decoder/abi-registry.d.ts.map +1 -0
  8. package/dist/decoder/abi-registry.js +88 -0
  9. package/dist/decoder/abi-registry.js.map +1 -0
  10. package/dist/decoder/event-decoder.d.ts +31 -0
  11. package/dist/decoder/event-decoder.d.ts.map +1 -0
  12. package/dist/decoder/event-decoder.js +142 -0
  13. package/dist/decoder/event-decoder.js.map +1 -0
  14. package/dist/explorer/explorer-client.d.ts +65 -0
  15. package/dist/explorer/explorer-client.d.ts.map +1 -0
  16. package/dist/explorer/explorer-client.js +164 -0
  17. package/dist/explorer/explorer-client.js.map +1 -0
  18. package/dist/explorer/rate-limiter.d.ts +31 -0
  19. package/dist/explorer/rate-limiter.d.ts.map +1 -0
  20. package/dist/explorer/rate-limiter.js +79 -0
  21. package/dist/explorer/rate-limiter.js.map +1 -0
  22. package/dist/index.d.ts +24 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +26 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/rpc/rpc-client.d.ts +70 -0
  27. package/dist/rpc/rpc-client.d.ts.map +1 -0
  28. package/dist/rpc/rpc-client.js +136 -0
  29. package/dist/rpc/rpc-client.js.map +1 -0
  30. package/dist/rpc/ws-manager.d.ts +27 -0
  31. package/dist/rpc/ws-manager.d.ts.map +1 -0
  32. package/dist/rpc/ws-manager.js +161 -0
  33. package/dist/rpc/ws-manager.js.map +1 -0
  34. package/dist/scanner/block-scanner.d.ts +45 -0
  35. package/dist/scanner/block-scanner.d.ts.map +1 -0
  36. package/dist/scanner/block-scanner.js +180 -0
  37. package/dist/scanner/block-scanner.js.map +1 -0
  38. package/dist/tracer/call-tree-parser.d.ts +25 -0
  39. package/dist/tracer/call-tree-parser.d.ts.map +1 -0
  40. package/dist/tracer/call-tree-parser.js +80 -0
  41. package/dist/tracer/call-tree-parser.js.map +1 -0
  42. package/dist/tracer/state-diff-parser.d.ts +13 -0
  43. package/dist/tracer/state-diff-parser.d.ts.map +1 -0
  44. package/dist/tracer/state-diff-parser.js +70 -0
  45. package/dist/tracer/state-diff-parser.js.map +1 -0
  46. package/dist/tracer/transaction-tracer.d.ts +52 -0
  47. package/dist/tracer/transaction-tracer.d.ts.map +1 -0
  48. package/dist/tracer/transaction-tracer.js +107 -0
  49. package/dist/tracer/transaction-tracer.js.map +1 -0
  50. package/dist/types/index.d.ts +262 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/index.js +8 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/utils/address.d.ts +29 -0
  55. package/dist/utils/address.d.ts.map +1 -0
  56. package/dist/utils/address.js +49 -0
  57. package/dist/utils/address.js.map +1 -0
  58. package/dist/utils/format.d.ts +20 -0
  59. package/dist/utils/format.d.ts.map +1 -0
  60. package/dist/utils/format.js +53 -0
  61. package/dist/utils/format.js.map +1 -0
  62. package/dist/utils/logger.d.ts +16 -0
  63. package/dist/utils/logger.d.ts.map +1 -0
  64. package/dist/utils/logger.js +58 -0
  65. package/dist/utils/logger.js.map +1 -0
  66. package/dist/watcher/contract-watcher.d.ts +56 -0
  67. package/dist/watcher/contract-watcher.d.ts.map +1 -0
  68. package/dist/watcher/contract-watcher.js +353 -0
  69. package/dist/watcher/contract-watcher.js.map +1 -0
  70. package/dist/watcher/log-poller.d.ts +24 -0
  71. package/dist/watcher/log-poller.d.ts.map +1 -0
  72. package/dist/watcher/log-poller.js +82 -0
  73. package/dist/watcher/log-poller.js.map +1 -0
  74. package/package.json +57 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Simple structured logger — no external dependencies.
3
+ * Supports log levels: debug, info, warn, error, silent.
4
+ */
5
+ const LEVEL_ORDER = {
6
+ debug: 0,
7
+ info: 1,
8
+ warn: 2,
9
+ error: 3,
10
+ silent: 4,
11
+ };
12
+ const LEVEL_COLORS = {
13
+ debug: '\x1b[90m', // gray
14
+ info: '\x1b[36m', // cyan
15
+ warn: '\x1b[33m', // yellow
16
+ error: '\x1b[31m', // red
17
+ };
18
+ const RESET = '\x1b[0m';
19
+ export class Logger {
20
+ context;
21
+ level;
22
+ constructor(context, level = 'info') {
23
+ this.context = context;
24
+ this.level = LEVEL_ORDER[level];
25
+ }
26
+ debug(message, ...args) {
27
+ if (this.level <= LEVEL_ORDER.debug) {
28
+ this.log('debug', message, ...args);
29
+ }
30
+ }
31
+ info(message, ...args) {
32
+ if (this.level <= LEVEL_ORDER.info) {
33
+ this.log('info', message, ...args);
34
+ }
35
+ }
36
+ warn(message, ...args) {
37
+ if (this.level <= LEVEL_ORDER.warn) {
38
+ this.log('warn', message, ...args);
39
+ }
40
+ }
41
+ error(message, ...args) {
42
+ if (this.level <= LEVEL_ORDER.error) {
43
+ this.log('error', message, ...args);
44
+ }
45
+ }
46
+ log(level, message, ...args) {
47
+ const color = LEVEL_COLORS[level] ?? '';
48
+ const ts = new Date().toISOString().slice(11, 23); // HH:MM:SS.mmm
49
+ const prefix = `${color}[${ts}] [${level.toUpperCase()}] [${this.context}]${RESET}`;
50
+ if (args.length > 0) {
51
+ console.log(prefix, message, ...args);
52
+ }
53
+ else {
54
+ console.log(prefix, message);
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,YAAY,GAA2B;IAC3C,KAAK,EAAE,UAAU,EAAG,OAAO;IAC3B,IAAI,EAAE,UAAU,EAAI,OAAO;IAC3B,IAAI,EAAE,UAAU,EAAI,SAAS;IAC7B,KAAK,EAAE,UAAU,EAAG,MAAM;CAC3B,CAAC;AAEF,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,MAAM,OAAO,MAAM;IACA,OAAO,CAAS;IAChB,KAAK,CAAS;IAE/B,YAAY,OAAe,EAAE,QAAkB,MAAM;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,IAAI,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,IAAI,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAEO,GAAG,CAAC,KAAa,EAAE,OAAe,EAAE,GAAG,IAAW;QACxD,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe;QAClE,MAAM,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE,MAAM,KAAK,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC;QACpF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ContractWatcher — Real-time contract interaction monitoring.
3
+ *
4
+ * Combines three detection paths:
5
+ * 1. WebSocket subscription (eth_subscribe("logs")) — real-time push
6
+ * 2. HTTP polling (eth_getLogs) — reliable fallback
7
+ * 3. Explorer API (txlist + txlistinternal) — direct & internal call discovery
8
+ *
9
+ * Deduplication: events are keyed by `txHash-logIndex` to prevent double-counting
10
+ * when both WS and polling detect the same event.
11
+ *
12
+ * EventEmitter API: on('event'), on('interaction'), on('log'), on('connected'),
13
+ * on('disconnected'), on('error'), on('checkpoint').
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import type { InteractionDetectorConfig } from '../types/index.js';
17
+ export declare class ContractWatcher extends EventEmitter {
18
+ private readonly logger;
19
+ private readonly logLevel;
20
+ private readonly rpc;
21
+ private readonly addresses;
22
+ private readonly decoder;
23
+ private readonly registry;
24
+ private readonly poller;
25
+ private readonly checkpoint;
26
+ private readonly checkpointKey;
27
+ private readonly pollIntervalMs;
28
+ private readonly explorerPollIntervalMs;
29
+ private wsManager;
30
+ private explorer;
31
+ private pollTimer;
32
+ private explorerTimer;
33
+ private lastProcessedBlock;
34
+ private lastExplorerBlock;
35
+ private seenEventKeys;
36
+ private seenTxHashes;
37
+ private running;
38
+ private blockTimestampCache;
39
+ constructor(config: InteractionDetectorConfig);
40
+ /**
41
+ * Start monitoring. Begins WS subscription + polling loop + optional explorer polling.
42
+ */
43
+ start(): Promise<void>;
44
+ /**
45
+ * Stop monitoring gracefully.
46
+ */
47
+ stop(): Promise<void>;
48
+ private setupWsHandlers;
49
+ private startPolling;
50
+ private pollCycle;
51
+ private startExplorerPolling;
52
+ private explorerPollCycle;
53
+ private processLog;
54
+ private resolveTimestamp;
55
+ }
56
+ //# sourceMappingURL=contract-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/contract-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAUtC,OAAO,KAAK,EACV,yBAAyB,EAM1B,MAAM,mBAAmB,CAAC;AAO3B,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAEhD,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,OAAO,CAAS;IAGxB,OAAO,CAAC,mBAAmB,CAAkC;gBAEjD,MAAM,EAAE,yBAAyB;IAkD7C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,YAAY;YAYN,SAAS;IA6CvB,OAAO,CAAC,oBAAoB;YAgBd,iBAAiB;YA8EjB,UAAU;YA+BV,gBAAgB;CAkB/B"}
@@ -0,0 +1,353 @@
1
+ /**
2
+ * ContractWatcher — Real-time contract interaction monitoring.
3
+ *
4
+ * Combines three detection paths:
5
+ * 1. WebSocket subscription (eth_subscribe("logs")) — real-time push
6
+ * 2. HTTP polling (eth_getLogs) — reliable fallback
7
+ * 3. Explorer API (txlist + txlistinternal) — direct & internal call discovery
8
+ *
9
+ * Deduplication: events are keyed by `txHash-logIndex` to prevent double-counting
10
+ * when both WS and polling detect the same event.
11
+ *
12
+ * EventEmitter API: on('event'), on('interaction'), on('log'), on('connected'),
13
+ * on('disconnected'), on('error'), on('checkpoint').
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import { Logger } from '../utils/logger.js';
17
+ import { normalizeAddress } from '../utils/address.js';
18
+ import { RpcClient } from '../rpc/rpc-client.js';
19
+ import { WsManager } from '../rpc/ws-manager.js';
20
+ import { LogPoller } from './log-poller.js';
21
+ import { EventDecoder } from '../decoder/event-decoder.js';
22
+ import { AbiRegistry } from '../decoder/abi-registry.js';
23
+ import { ExplorerClient } from '../explorer/explorer-client.js';
24
+ import { createCheckpointBackend } from '../checkpoint/checkpoint.js';
25
+ const POLL_DEFAULTS = {
26
+ intervalMs: 30_000,
27
+ maxBlockRange: 100,
28
+ };
29
+ export class ContractWatcher extends EventEmitter {
30
+ logger;
31
+ logLevel;
32
+ rpc;
33
+ addresses;
34
+ decoder;
35
+ registry;
36
+ poller;
37
+ checkpoint;
38
+ checkpointKey;
39
+ pollIntervalMs;
40
+ explorerPollIntervalMs;
41
+ wsManager = null;
42
+ explorer = null;
43
+ pollTimer = null;
44
+ explorerTimer = null;
45
+ lastProcessedBlock = 0;
46
+ lastExplorerBlock = 0;
47
+ seenEventKeys = new Set();
48
+ seenTxHashes = new Set();
49
+ running = false;
50
+ // Timestamp cache: block number → unix seconds
51
+ blockTimestampCache = new Map();
52
+ constructor(config) {
53
+ super();
54
+ this.logLevel = config.logLevel ?? 'info';
55
+ this.logger = new Logger('ContractWatcher', this.logLevel);
56
+ // Normalize addresses
57
+ this.addresses = config.contracts.map((c) => normalizeAddress(c.address));
58
+ // RPC client
59
+ this.rpc = new RpcClient(config.rpcUrl, {
60
+ fallbackUrls: config.fallbackRpcUrls,
61
+ logLevel: this.logLevel,
62
+ });
63
+ // ABI registry + decoder
64
+ this.registry = new AbiRegistry(this.logLevel);
65
+ for (const contract of config.contracts) {
66
+ if (contract.abi) {
67
+ this.registry.register(contract.address, contract.abi, contract.name);
68
+ }
69
+ }
70
+ this.decoder = new EventDecoder(this.registry, this.logLevel);
71
+ // Log poller
72
+ this.poller = new LogPoller(this.rpc, this.addresses, config.polling, this.logLevel);
73
+ this.pollIntervalMs = config.polling?.intervalMs ?? POLL_DEFAULTS.intervalMs;
74
+ // Checkpoint
75
+ this.checkpoint = createCheckpointBackend(config.checkpoint);
76
+ this.checkpointKey = `watcher-${this.addresses.sort().join('-').slice(0, 60)}`;
77
+ // WebSocket (optional)
78
+ if (config.wsUrl && config.ws?.enabled !== false) {
79
+ this.wsManager = new WsManager(config.wsUrl, config.chainId ?? 50, config.ws, this.logLevel);
80
+ }
81
+ // Explorer (optional)
82
+ if (config.explorer) {
83
+ this.explorer = new ExplorerClient(config.explorer, this.logLevel);
84
+ this.explorerPollIntervalMs = config.explorer.pollIntervalMs ?? 60_000;
85
+ }
86
+ else {
87
+ this.explorerPollIntervalMs = 0;
88
+ }
89
+ }
90
+ /**
91
+ * Start monitoring. Begins WS subscription + polling loop + optional explorer polling.
92
+ */
93
+ async start() {
94
+ if (this.running)
95
+ return;
96
+ this.running = true;
97
+ this.logger.info(`Starting ContractWatcher for ${this.addresses.length} contract(s)`);
98
+ // Restore checkpoint
99
+ const savedBlock = await this.checkpoint.load(this.checkpointKey);
100
+ if (savedBlock) {
101
+ this.lastProcessedBlock = savedBlock;
102
+ this.lastExplorerBlock = savedBlock;
103
+ this.logger.info(`Resumed from checkpoint: block ${savedBlock}`);
104
+ }
105
+ else {
106
+ // Start from current block
107
+ this.lastProcessedBlock = (await this.rpc.getBlockNumber()) - 1;
108
+ this.lastExplorerBlock = this.lastProcessedBlock;
109
+ this.logger.info(`Starting from current block: ${this.lastProcessedBlock}`);
110
+ }
111
+ // Start WebSocket subscription
112
+ if (this.wsManager) {
113
+ this.setupWsHandlers();
114
+ this.wsManager.connect(this.addresses).catch((err) => {
115
+ this.logger.warn(`WS connect failed: ${err.message}`);
116
+ });
117
+ }
118
+ // Start polling loop
119
+ this.startPolling();
120
+ // Start explorer polling
121
+ if (this.explorer) {
122
+ this.startExplorerPolling();
123
+ }
124
+ }
125
+ /**
126
+ * Stop monitoring gracefully.
127
+ */
128
+ async stop() {
129
+ if (!this.running)
130
+ return;
131
+ this.running = false;
132
+ this.logger.info('Stopping ContractWatcher...');
133
+ if (this.pollTimer) {
134
+ clearInterval(this.pollTimer);
135
+ this.pollTimer = null;
136
+ }
137
+ if (this.explorerTimer) {
138
+ clearInterval(this.explorerTimer);
139
+ this.explorerTimer = null;
140
+ }
141
+ if (this.wsManager) {
142
+ await this.wsManager.disconnect();
143
+ }
144
+ if (this.explorer) {
145
+ this.explorer.destroy();
146
+ }
147
+ // Save final checkpoint
148
+ if (this.lastProcessedBlock > 0) {
149
+ await this.checkpoint.save(this.checkpointKey, this.lastProcessedBlock);
150
+ }
151
+ this.logger.info('ContractWatcher stopped');
152
+ }
153
+ // ─── WebSocket Handlers ──────────────────────────────────────────────
154
+ setupWsHandlers() {
155
+ if (!this.wsManager)
156
+ return;
157
+ this.wsManager.on('log', (log) => {
158
+ this.processLog(log, 'ws_logs').catch((err) => {
159
+ this.logger.error(`Error processing WS log: ${err.message}`);
160
+ });
161
+ });
162
+ this.wsManager.on('connected', (url) => {
163
+ this.logger.info(`WS connected: ${url}`);
164
+ this.emit('connected', { url, type: 'websocket' });
165
+ });
166
+ this.wsManager.on('disconnected', (url, reason) => {
167
+ this.logger.warn(`WS disconnected: ${url} (${reason})`);
168
+ this.emit('disconnected', { url, reason });
169
+ });
170
+ this.wsManager.on('error', (err) => {
171
+ this.emit('error', err);
172
+ });
173
+ }
174
+ // ─── Polling Loop ─────────────────────────────────────────────────────
175
+ startPolling() {
176
+ this.pollCycle().catch((err) => {
177
+ this.logger.error(`Initial poll failed: ${err.message}`);
178
+ });
179
+ this.pollTimer = setInterval(() => {
180
+ this.pollCycle().catch((err) => {
181
+ this.logger.error(`Poll cycle failed: ${err.message}`);
182
+ });
183
+ }, this.pollIntervalMs);
184
+ }
185
+ async pollCycle() {
186
+ if (!this.running)
187
+ return;
188
+ try {
189
+ const currentBlock = await this.rpc.getBlockNumber();
190
+ if (currentBlock <= this.lastProcessedBlock) {
191
+ this.logger.debug(`No new blocks (current: ${currentBlock}, last: ${this.lastProcessedBlock})`);
192
+ return;
193
+ }
194
+ const fromBlock = this.lastProcessedBlock + 1;
195
+ const toBlock = currentBlock;
196
+ this.logger.debug(`Polling blocks ${fromBlock}–${toBlock} (${toBlock - fromBlock + 1} blocks)`);
197
+ const logs = await this.poller.fetchLogs(fromBlock, toBlock);
198
+ for (const log of logs) {
199
+ await this.processLog(log, 'rpc_logs');
200
+ }
201
+ this.lastProcessedBlock = toBlock;
202
+ // Save checkpoint
203
+ await this.checkpoint.save(this.checkpointKey, toBlock);
204
+ this.emit('checkpoint', toBlock);
205
+ // Prune old dedup keys
206
+ if (this.seenEventKeys.size > 15000) {
207
+ const keysArray = Array.from(this.seenEventKeys);
208
+ this.seenEventKeys = new Set(keysArray.slice(keysArray.length - 10000));
209
+ }
210
+ if (this.seenTxHashes.size > 15000) {
211
+ const txArray = Array.from(this.seenTxHashes);
212
+ this.seenTxHashes = new Set(txArray.slice(txArray.length - 10000));
213
+ }
214
+ }
215
+ catch (err) {
216
+ this.logger.error(`Poll cycle error: ${err.message}`);
217
+ this.emit('error', err);
218
+ }
219
+ }
220
+ // ─── Explorer Polling ─────────────────────────────────────────────────
221
+ startExplorerPolling() {
222
+ if (!this.explorer)
223
+ return;
224
+ setTimeout(() => {
225
+ this.explorerPollCycle().catch((err) => {
226
+ this.logger.error(`Initial explorer poll failed: ${err.message}`);
227
+ });
228
+ }, 5000);
229
+ this.explorerTimer = setInterval(() => {
230
+ this.explorerPollCycle().catch((err) => {
231
+ this.logger.error(`Explorer poll cycle failed: ${err.message}`);
232
+ });
233
+ }, this.explorerPollIntervalMs);
234
+ }
235
+ async explorerPollCycle() {
236
+ if (!this.running || !this.explorer)
237
+ return;
238
+ const currentBlock = this.lastProcessedBlock;
239
+ if (currentBlock <= this.lastExplorerBlock)
240
+ return;
241
+ for (const address of this.addresses) {
242
+ try {
243
+ // Fetch direct transactions
244
+ const txs = await this.explorer.getTransactions(address, {
245
+ startBlock: this.lastExplorerBlock + 1,
246
+ endBlock: currentBlock,
247
+ });
248
+ for (const tx of txs) {
249
+ if (this.seenTxHashes.has(tx.hash))
250
+ continue;
251
+ this.seenTxHashes.add(tx.hash);
252
+ const interaction = {
253
+ type: 'direct_call',
254
+ txHash: tx.hash,
255
+ blockNumber: parseInt(tx.blockNumber),
256
+ timestamp: parseInt(tx.timeStamp),
257
+ from: tx.from,
258
+ to: tx.to,
259
+ value: tx.value,
260
+ methodId: tx.methodId,
261
+ methodName: tx.functionName,
262
+ input: tx.input,
263
+ gasUsed: tx.gasUsed,
264
+ isError: tx.isError === '1',
265
+ source: 'explorer_txlist',
266
+ };
267
+ this.emit('interaction', interaction);
268
+ }
269
+ // Fetch internal transactions
270
+ const internalTxs = await this.explorer.getInternalTransactions(address, {
271
+ startBlock: this.lastExplorerBlock + 1,
272
+ endBlock: currentBlock,
273
+ });
274
+ for (const tx of internalTxs) {
275
+ if (this.seenTxHashes.has(`${tx.hash}-internal-${tx.from}-${tx.to}`))
276
+ continue;
277
+ this.seenTxHashes.add(`${tx.hash}-internal-${tx.from}-${tx.to}`);
278
+ const callType = tx.callType?.toLowerCase();
279
+ let interactionType = 'internal_call';
280
+ if (callType === 'delegatecall')
281
+ interactionType = 'delegate_call';
282
+ else if (callType === 'staticcall')
283
+ interactionType = 'static_call';
284
+ else if (callType === 'create')
285
+ interactionType = 'create';
286
+ const interaction = {
287
+ type: interactionType,
288
+ txHash: tx.hash,
289
+ blockNumber: parseInt(tx.blockNumber),
290
+ timestamp: parseInt(tx.timeStamp),
291
+ from: tx.from,
292
+ to: tx.to,
293
+ value: tx.value,
294
+ gasUsed: tx.gasUsed,
295
+ isError: tx.isError === '1',
296
+ source: 'explorer_internal',
297
+ };
298
+ this.emit('interaction', interaction);
299
+ }
300
+ }
301
+ catch (err) {
302
+ this.logger.warn(`Explorer poll failed for ${address}: ${err.message}`);
303
+ }
304
+ }
305
+ this.lastExplorerBlock = currentBlock;
306
+ }
307
+ // ─── Log Processing ───────────────────────────────────────────────────
308
+ async processLog(log, source) {
309
+ const logIndex = typeof log.logIndex === 'string'
310
+ ? parseInt(log.logIndex, 16)
311
+ : log.logIndex;
312
+ const dedupKey = `${log.transactionHash}-${logIndex}`;
313
+ if (this.seenEventKeys.has(dedupKey))
314
+ return;
315
+ this.seenEventKeys.add(dedupKey);
316
+ this.emit('log', log);
317
+ const decoded = this.decoder.decode(log);
318
+ if (decoded) {
319
+ decoded.timestamp = await this.resolveTimestamp(decoded.blockNumber);
320
+ this.emit('event', decoded);
321
+ const interaction = {
322
+ type: 'event',
323
+ txHash: decoded.txHash,
324
+ blockNumber: decoded.blockNumber,
325
+ timestamp: decoded.timestamp,
326
+ from: '',
327
+ to: decoded.contract,
328
+ value: '0',
329
+ event: decoded,
330
+ source,
331
+ };
332
+ this.emit('interaction', interaction);
333
+ }
334
+ }
335
+ async resolveTimestamp(blockNumber) {
336
+ const cached = this.blockTimestampCache.get(blockNumber);
337
+ if (cached !== undefined)
338
+ return cached;
339
+ try {
340
+ const timestamp = await this.rpc.getBlockTimestamp(blockNumber);
341
+ this.blockTimestampCache.set(blockNumber, timestamp);
342
+ if (this.blockTimestampCache.size > 600) {
343
+ const entries = Array.from(this.blockTimestampCache.entries());
344
+ this.blockTimestampCache = new Map(entries.slice(entries.length - 500));
345
+ }
346
+ return timestamp;
347
+ }
348
+ catch {
349
+ return Math.floor(Date.now() / 1000);
350
+ }
351
+ }
352
+ }
353
+ //# sourceMappingURL=contract-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-watcher.js","sourceRoot":"","sources":["../../src/watcher/contract-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Log Poller — fetches logs via eth_getLogs in chunked block ranges.
3
+ *
4
+ * Handles XDC's 100-block-per-request limit with automatic chunking.
5
+ * Used by ContractWatcher as the reliable fallback to WebSocket.
6
+ */
7
+ import { RpcClient } from '../rpc/rpc-client.js';
8
+ import type { RawLog, PollingConfig, LogLevel } from '../types/index.js';
9
+ export declare class LogPoller {
10
+ private readonly logger;
11
+ private readonly rpc;
12
+ private readonly addresses;
13
+ private readonly maxBlockRange;
14
+ private readonly concurrency;
15
+ constructor(rpc: RpcClient, addresses: string[], config?: PollingConfig, logLevel?: LogLevel);
16
+ /**
17
+ * Fetch all logs from `fromBlock` to `toBlock` for the monitored addresses.
18
+ * Automatically chunks into maxBlockRange-sized requests.
19
+ */
20
+ fetchLogs(fromBlock: number, toBlock: number): Promise<RawLog[]>;
21
+ private fetchChunk;
22
+ private toNumber;
23
+ }
24
+ //# sourceMappingURL=log-poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-poller.d.ts","sourceRoot":"","sources":["../../src/watcher/log-poller.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEzE,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAGnC,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,CAAC,EAAE,aAAa,EACtB,QAAQ,CAAC,EAAE,QAAQ;IASrB;;;OAGG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAyCxD,UAAU;IAexB,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Log Poller — fetches logs via eth_getLogs in chunked block ranges.
3
+ *
4
+ * Handles XDC's 100-block-per-request limit with automatic chunking.
5
+ * Used by ContractWatcher as the reliable fallback to WebSocket.
6
+ */
7
+ import { Logger } from '../utils/logger.js';
8
+ export class LogPoller {
9
+ logger;
10
+ rpc;
11
+ addresses;
12
+ maxBlockRange;
13
+ concurrency;
14
+ constructor(rpc, addresses, config, logLevel) {
15
+ this.rpc = rpc;
16
+ this.addresses = addresses;
17
+ this.maxBlockRange = config?.maxBlockRange ?? 100;
18
+ this.concurrency = config?.concurrency ?? 3;
19
+ this.logger = new Logger('LogPoller', logLevel ?? 'info');
20
+ }
21
+ /**
22
+ * Fetch all logs from `fromBlock` to `toBlock` for the monitored addresses.
23
+ * Automatically chunks into maxBlockRange-sized requests.
24
+ */
25
+ async fetchLogs(fromBlock, toBlock) {
26
+ if (fromBlock > toBlock)
27
+ return [];
28
+ const totalBlocks = toBlock - fromBlock + 1;
29
+ if (totalBlocks <= this.maxBlockRange) {
30
+ return this.fetchChunk(fromBlock, toBlock);
31
+ }
32
+ // Split into chunks
33
+ const chunks = [];
34
+ let current = fromBlock;
35
+ while (current <= toBlock) {
36
+ const chunkEnd = Math.min(current + this.maxBlockRange - 1, toBlock);
37
+ chunks.push({ from: current, to: chunkEnd });
38
+ current = chunkEnd + 1;
39
+ }
40
+ this.logger.debug(`Fetching logs: ${chunks.length} chunks (blocks ${fromBlock}–${toBlock})`);
41
+ // Execute chunks with concurrency control
42
+ const allLogs = [];
43
+ for (let i = 0; i < chunks.length; i += this.concurrency) {
44
+ const batch = chunks.slice(i, i + this.concurrency);
45
+ const results = await Promise.all(batch.map((chunk) => this.fetchChunk(chunk.from, chunk.to)));
46
+ for (const logs of results) {
47
+ allLogs.push(...logs);
48
+ }
49
+ }
50
+ // Sort by block number, then log index
51
+ allLogs.sort((a, b) => {
52
+ const blockDiff = this.toNumber(a.blockNumber) - this.toNumber(b.blockNumber);
53
+ if (blockDiff !== 0)
54
+ return blockDiff;
55
+ return this.toNumber(a.logIndex) - this.toNumber(b.logIndex);
56
+ });
57
+ return allLogs;
58
+ }
59
+ async fetchChunk(fromBlock, toBlock) {
60
+ try {
61
+ const logs = await this.rpc.getLogs({
62
+ address: this.addresses,
63
+ fromBlock,
64
+ toBlock,
65
+ });
66
+ this.logger.debug(`Fetched ${logs.length} logs from blocks ${fromBlock}–${toBlock}`);
67
+ return logs;
68
+ }
69
+ catch (err) {
70
+ this.logger.error(`Failed to fetch logs ${fromBlock}–${toBlock}: ${err.message}`);
71
+ return [];
72
+ }
73
+ }
74
+ toNumber(value) {
75
+ if (typeof value === 'number')
76
+ return value;
77
+ if (value.startsWith('0x') || value.startsWith('0X'))
78
+ return parseInt(value, 16);
79
+ return parseInt(value, 10);
80
+ }
81
+ }
82
+ //# sourceMappingURL=log-poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-poller.js","sourceRoot":"","sources":["../../src/watcher/log-poller.ts"],"names":[],"mappings":"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"}