@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,161 @@
1
+ /**
2
+ * WebSocket connection lifecycle manager.
3
+ * Handles connect, heartbeat, auto-reconnect, and event forwarding.
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { WebSocketProvider } from 'ethers';
7
+ import { Logger } from '../utils/logger.js';
8
+ const WS_DEFAULTS = {
9
+ enabled: true,
10
+ reconnectDelayBaseMs: 5_000,
11
+ reconnectDelayMaxMs: 30_000,
12
+ maxReconnectAttempts: 20,
13
+ heartbeatIntervalMs: 60_000,
14
+ heartbeatTimeoutMs: 10_000,
15
+ };
16
+ export class WsManager extends EventEmitter {
17
+ logger;
18
+ wsUrl;
19
+ chainId;
20
+ config;
21
+ provider = null;
22
+ heartbeatTimer = null;
23
+ reconnectAttempts = 0;
24
+ reconnectScheduled = false;
25
+ _isConnected = false;
26
+ destroyed = false;
27
+ constructor(wsUrl, chainId, wsConfig, logLevel) {
28
+ super();
29
+ this.wsUrl = wsUrl;
30
+ this.chainId = chainId;
31
+ this.config = { ...WS_DEFAULTS, ...wsConfig };
32
+ this.logger = new Logger('WsManager', logLevel ?? 'info');
33
+ }
34
+ get isConnected() {
35
+ return this._isConnected;
36
+ }
37
+ async connect(addresses) {
38
+ if (this.destroyed)
39
+ return;
40
+ try {
41
+ this.logger.info(`Connecting to ${this.wsUrl}...`);
42
+ const WS = (await import('ws')).default;
43
+ const ws = new WS(this.wsUrl);
44
+ await new Promise((resolve, reject) => {
45
+ const timeout = setTimeout(() => {
46
+ ws.terminate();
47
+ reject(new Error(`Connection timeout: ${this.wsUrl}`));
48
+ }, 10_000);
49
+ ws.once('open', () => { clearTimeout(timeout); resolve(); });
50
+ ws.once('error', (err) => { clearTimeout(timeout); reject(err); });
51
+ });
52
+ await this.destroyConnection();
53
+ this.provider = new WebSocketProvider(ws, this.chainId);
54
+ const filter = { address: addresses };
55
+ this.provider.on(filter, (log) => {
56
+ this.emit('log', log);
57
+ });
58
+ ws.on('close', () => {
59
+ if (!this._isConnected && this.reconnectScheduled)
60
+ return;
61
+ this.logger.warn(`WS closed (${this.wsUrl})`);
62
+ this._isConnected = false;
63
+ this.stopHeartbeat();
64
+ this.destroyConnection().then(() => this.scheduleReconnect(addresses));
65
+ });
66
+ ws.on('error', (err) => {
67
+ if (!this._isConnected && this.reconnectScheduled)
68
+ return;
69
+ this.logger.error(`WS error: ${err.message}`);
70
+ this._isConnected = false;
71
+ this.stopHeartbeat();
72
+ this.destroyConnection().then(() => this.scheduleReconnect(addresses));
73
+ });
74
+ this._isConnected = true;
75
+ this.reconnectAttempts = 0;
76
+ this.reconnectScheduled = false;
77
+ this.logger.info(`WS connected to ${this.wsUrl}`);
78
+ this.emit('connected', this.wsUrl);
79
+ this.startHeartbeat(addresses);
80
+ }
81
+ catch (error) {
82
+ this.logger.error(`WS connect failed: ${error.message}`);
83
+ this.emit('error', error);
84
+ this.scheduleReconnect(addresses);
85
+ }
86
+ }
87
+ async disconnect() {
88
+ this.destroyed = true;
89
+ this.stopHeartbeat();
90
+ await this.destroyConnection();
91
+ this._isConnected = false;
92
+ }
93
+ startHeartbeat(addresses) {
94
+ this.stopHeartbeat();
95
+ this.heartbeatTimer = setInterval(async () => {
96
+ if (!this._isConnected || !this.provider) {
97
+ this.stopHeartbeat();
98
+ return;
99
+ }
100
+ try {
101
+ const blockNumber = await Promise.race([
102
+ this.provider.getBlockNumber(),
103
+ new Promise((_, reject) => setTimeout(() => reject(new Error('heartbeat timeout')), this.config.heartbeatTimeoutMs)),
104
+ ]);
105
+ this.logger.debug(`Heartbeat OK — block ${blockNumber}`);
106
+ }
107
+ catch (err) {
108
+ if (!this._isConnected || this.reconnectScheduled)
109
+ return;
110
+ this.logger.warn(`Heartbeat FAILED: ${err.message} — reconnecting`);
111
+ this._isConnected = false;
112
+ this.stopHeartbeat();
113
+ await this.destroyConnection();
114
+ this.scheduleReconnect(addresses);
115
+ }
116
+ }, this.config.heartbeatIntervalMs);
117
+ }
118
+ stopHeartbeat() {
119
+ if (this.heartbeatTimer) {
120
+ clearInterval(this.heartbeatTimer);
121
+ this.heartbeatTimer = null;
122
+ }
123
+ }
124
+ scheduleReconnect(addresses) {
125
+ if (this.destroyed || this.reconnectScheduled)
126
+ return;
127
+ this.reconnectScheduled = true;
128
+ this.reconnectAttempts++;
129
+ if (this.reconnectAttempts > this.config.maxReconnectAttempts) {
130
+ this.logger.warn(`Exceeded ${this.config.maxReconnectAttempts} reconnect attempts. Retry in 5 min.`);
131
+ this.emit('disconnected', this.wsUrl, 'max_reconnect_exceeded');
132
+ setTimeout(() => {
133
+ if (!this.destroyed) {
134
+ this.reconnectAttempts = 0;
135
+ this.reconnectScheduled = false;
136
+ this.connect(addresses).catch(() => { });
137
+ }
138
+ }, 5 * 60 * 1000);
139
+ return;
140
+ }
141
+ const delay = Math.min(this.config.reconnectDelayBaseMs * Math.pow(2, this.reconnectAttempts - 1), this.config.reconnectDelayMaxMs);
142
+ this.logger.info(`Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts})`);
143
+ this.emit('disconnected', this.wsUrl, 'reconnecting');
144
+ setTimeout(() => {
145
+ this.reconnectScheduled = false;
146
+ if (!this.destroyed)
147
+ this.connect(addresses).catch(() => { });
148
+ }, delay);
149
+ }
150
+ async destroyConnection() {
151
+ if (this.provider) {
152
+ try {
153
+ this.provider.removeAllListeners();
154
+ await this.provider.destroy();
155
+ }
156
+ catch { /* ignore */ }
157
+ this.provider = null;
158
+ }
159
+ }
160
+ }
161
+ //# sourceMappingURL=ws-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-manager.js","sourceRoot":"","sources":["../../src/rpc/ws-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI5C,MAAM,WAAW,GAAuB;IACtC,OAAO,EAAE,IAAI;IACb,oBAAoB,EAAE,KAAK;IAC3B,mBAAmB,EAAE,MAAM;IAC3B,oBAAoB,EAAE,EAAE;IACxB,mBAAmB,EAAE,MAAM;IAC3B,kBAAkB,EAAE,MAAM;CAC3B,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,YAAY;IACxB,MAAM,CAAS;IACf,KAAK,CAAS;IACd,OAAO,CAAS;IAChB,MAAM,CAAqB;IAEpC,QAAQ,GAA6B,IAAI,CAAC;IAC1C,cAAc,GAA0B,IAAI,CAAC;IAC7C,iBAAiB,GAAG,CAAC,CAAC;IACtB,kBAAkB,GAAG,KAAK,CAAC;IAC3B,YAAY,GAAG,KAAK,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAY,KAAa,EAAE,OAAe,EAAE,QAAmB,EAAE,QAAmB;QAClF,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,WAAW,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,QAAQ,IAAI,MAAM,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAmB;QAC/B,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAE3B,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;YACnD,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE9B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC9B,EAAE,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC,EAAE,MAAM,CAAC,CAAC;gBAEX,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5E,CAAC,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAE/B,IAAI,CAAC,QAAQ,GAAG,IAAI,iBAAiB,CAAC,EAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAE/D,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,GAAQ,EAAE,EAAE;gBACpC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAClB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,kBAAkB;oBAAE,OAAO;gBAC1D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;gBAC9C,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;YACzE,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,kBAAkB;oBAAE,OAAO;gBAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9C,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;YACzE,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAEjC,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IAEO,cAAc,CAAC,SAAmB;QACxC,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YAC3C,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACzC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBACrC,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;oBAC9B,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACzF;iBACF,CAAC,CAAC;gBACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,WAAW,EAAE,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,kBAAkB;oBAAE,OAAO;gBAC1D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC;gBACpE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC/B,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YACpC,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACtC,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAEO,iBAAiB,CAAC,SAAmB;QAC3C,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,kBAAkB;YAAE,OAAO;QACtD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,IAAI,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC9D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,oBAAoB,sCAAsC,CAAC,CAAC;YACrG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;YAChE,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACpB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;oBAC3B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;oBAChC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,IAAI,CAAC,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,EAC1E,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAChC,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,KAAK,GAAG,IAAI,cAAc,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QACzF,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;QAEtD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/D,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;gBACnC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * BlockScanner — Historical block range scanning.
3
+ *
4
+ * Scans a block range for all events and interactions involving a contract.
5
+ * Chunks requests into maxBlockRange-sized batches (default: 100 for XDC).
6
+ * Includes block timestamp resolution with LRU cache.
7
+ */
8
+ import type { DecodedEvent, ContractInteraction, ScanOptions, ExplorerConfig, LogLevel } from '../types/index.js';
9
+ export interface BlockScannerConfig {
10
+ /** Primary HTTP RPC URL */
11
+ rpcUrl: string;
12
+ /** Fallback RPC URLs */
13
+ fallbackRpcUrls?: string[];
14
+ /** Max blocks per request (default: 100) */
15
+ maxBlockRange?: number;
16
+ /** Parallel fetch concurrency (default: 3) */
17
+ concurrency?: number;
18
+ /** Explorer config (optional — enables txlist/txlistinternal) */
19
+ explorer?: ExplorerConfig;
20
+ /** Log level */
21
+ logLevel?: LogLevel;
22
+ }
23
+ export declare class BlockScanner {
24
+ private readonly logger;
25
+ private readonly logLevel;
26
+ private readonly rpc;
27
+ private readonly maxBlockRange;
28
+ private readonly concurrency;
29
+ private readonly explorerConfig?;
30
+ private timestampCache;
31
+ private readonly maxCacheSize;
32
+ constructor(config: BlockScannerConfig);
33
+ /**
34
+ * Scan a block range for all events from a contract.
35
+ * Returns decoded events sorted by block number and log index.
36
+ */
37
+ getEvents(options: ScanOptions): Promise<DecodedEvent[]>;
38
+ /**
39
+ * Scan a block range for all interactions (events + explorer txlist + txlistinternal).
40
+ * Returns unified ContractInteraction records.
41
+ */
42
+ getInteractions(options: ScanOptions): Promise<ContractInteraction[]>;
43
+ private resolveTimestamp;
44
+ }
45
+ //# sourceMappingURL=block-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/block-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,gBAAgB;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,YAAY;IACvB,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,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IAGjD,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;gBAEzB,MAAM,EAAE,kBAAkB;IAatC;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA8C9D;;;OAGG;IACG,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;YA4F7D,gBAAgB;CAkB/B"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * BlockScanner — Historical block range scanning.
3
+ *
4
+ * Scans a block range for all events and interactions involving a contract.
5
+ * Chunks requests into maxBlockRange-sized batches (default: 100 for XDC).
6
+ * Includes block timestamp resolution with LRU cache.
7
+ */
8
+ import { Logger } from '../utils/logger.js';
9
+ import { normalizeAddress } from '../utils/address.js';
10
+ import { RpcClient } from '../rpc/rpc-client.js';
11
+ import { LogPoller } from '../watcher/log-poller.js';
12
+ import { EventDecoder } from '../decoder/event-decoder.js';
13
+ import { AbiRegistry } from '../decoder/abi-registry.js';
14
+ import { ExplorerClient } from '../explorer/explorer-client.js';
15
+ export class BlockScanner {
16
+ logger;
17
+ logLevel;
18
+ rpc;
19
+ maxBlockRange;
20
+ concurrency;
21
+ explorerConfig;
22
+ // Block timestamp LRU cache
23
+ timestampCache = new Map();
24
+ maxCacheSize = 2000;
25
+ constructor(config) {
26
+ this.logLevel = config.logLevel ?? 'info';
27
+ this.logger = new Logger('BlockScanner', this.logLevel);
28
+ this.maxBlockRange = config.maxBlockRange ?? 100;
29
+ this.concurrency = config.concurrency ?? 3;
30
+ this.explorerConfig = config.explorer;
31
+ this.rpc = new RpcClient(config.rpcUrl, {
32
+ fallbackUrls: config.fallbackRpcUrls,
33
+ logLevel: this.logLevel,
34
+ });
35
+ }
36
+ /**
37
+ * Scan a block range for all events from a contract.
38
+ * Returns decoded events sorted by block number and log index.
39
+ */
40
+ async getEvents(options) {
41
+ const address = normalizeAddress(options.address);
42
+ const registry = new AbiRegistry(this.logLevel);
43
+ if (options.abi) {
44
+ registry.register(address, options.abi);
45
+ }
46
+ const decoder = new EventDecoder(registry, this.logLevel);
47
+ const poller = new LogPoller(this.rpc, [address], { maxBlockRange: this.maxBlockRange, concurrency: this.concurrency }, this.logLevel);
48
+ const totalBlocks = options.toBlock - options.fromBlock + 1;
49
+ this.logger.info(`Scanning ${totalBlocks} blocks (${options.fromBlock}–${options.toBlock}) for ${address}`);
50
+ const logs = await poller.fetchLogs(options.fromBlock, options.toBlock);
51
+ this.logger.info(`Found ${logs.length} raw logs`);
52
+ const events = [];
53
+ for (const log of logs) {
54
+ const decoded = decoder.decode(log);
55
+ if (!decoded)
56
+ continue;
57
+ if (options.eventFilter && decoded.name !== options.eventFilter)
58
+ continue;
59
+ decoded.timestamp = await this.resolveTimestamp(decoded.blockNumber);
60
+ events.push(decoded);
61
+ if (options.onProgress) {
62
+ const blocksProcessed = decoded.blockNumber - options.fromBlock;
63
+ options.onProgress(blocksProcessed, totalBlocks);
64
+ }
65
+ }
66
+ if (options.onProgress) {
67
+ options.onProgress(totalBlocks, totalBlocks);
68
+ }
69
+ this.logger.info(`Decoded ${events.length} events`);
70
+ return events;
71
+ }
72
+ /**
73
+ * Scan a block range for all interactions (events + explorer txlist + txlistinternal).
74
+ * Returns unified ContractInteraction records.
75
+ */
76
+ async getInteractions(options) {
77
+ const address = normalizeAddress(options.address);
78
+ const interactions = [];
79
+ // 1) Get events from RPC logs
80
+ const events = await this.getEvents(options);
81
+ const seenTxHashes = new Set();
82
+ for (const event of events) {
83
+ seenTxHashes.add(event.txHash);
84
+ interactions.push({
85
+ type: 'event',
86
+ txHash: event.txHash,
87
+ blockNumber: event.blockNumber,
88
+ timestamp: event.timestamp,
89
+ from: '',
90
+ to: event.contract,
91
+ value: '0',
92
+ event,
93
+ source: 'rpc_logs',
94
+ });
95
+ }
96
+ // 2) Get transactions from explorer (if configured)
97
+ if (this.explorerConfig) {
98
+ const explorer = new ExplorerClient(this.explorerConfig, this.logLevel);
99
+ try {
100
+ const txs = await explorer.getTransactions(address, {
101
+ startBlock: options.fromBlock,
102
+ endBlock: options.toBlock,
103
+ });
104
+ for (const tx of txs) {
105
+ if (seenTxHashes.has(tx.hash))
106
+ continue;
107
+ seenTxHashes.add(tx.hash);
108
+ interactions.push({
109
+ type: 'direct_call',
110
+ txHash: tx.hash,
111
+ blockNumber: parseInt(tx.blockNumber),
112
+ timestamp: parseInt(tx.timeStamp),
113
+ from: tx.from,
114
+ to: tx.to,
115
+ value: tx.value,
116
+ methodId: tx.methodId,
117
+ methodName: tx.functionName,
118
+ input: tx.input,
119
+ gasUsed: tx.gasUsed,
120
+ isError: tx.isError === '1',
121
+ source: 'explorer_txlist',
122
+ });
123
+ }
124
+ const internalTxs = await explorer.getInternalTransactions(address, {
125
+ startBlock: options.fromBlock,
126
+ endBlock: options.toBlock,
127
+ });
128
+ for (const tx of internalTxs) {
129
+ const callType = tx.callType?.toLowerCase();
130
+ let interactionType = 'internal_call';
131
+ if (callType === 'delegatecall')
132
+ interactionType = 'delegate_call';
133
+ else if (callType === 'staticcall')
134
+ interactionType = 'static_call';
135
+ else if (callType === 'create')
136
+ interactionType = 'create';
137
+ interactions.push({
138
+ type: interactionType,
139
+ txHash: tx.hash,
140
+ blockNumber: parseInt(tx.blockNumber),
141
+ timestamp: parseInt(tx.timeStamp),
142
+ from: tx.from,
143
+ to: tx.to,
144
+ value: tx.value,
145
+ gasUsed: tx.gasUsed,
146
+ isError: tx.isError === '1',
147
+ source: 'explorer_internal',
148
+ });
149
+ }
150
+ }
151
+ catch (err) {
152
+ this.logger.warn(`Explorer scan failed: ${err.message}`);
153
+ }
154
+ finally {
155
+ explorer.destroy();
156
+ }
157
+ }
158
+ interactions.sort((a, b) => a.blockNumber - b.blockNumber || a.timestamp - b.timestamp);
159
+ this.logger.info(`Total interactions: ${interactions.length}`);
160
+ return interactions;
161
+ }
162
+ async resolveTimestamp(blockNumber) {
163
+ const cached = this.timestampCache.get(blockNumber);
164
+ if (cached !== undefined)
165
+ return cached;
166
+ try {
167
+ const timestamp = await this.rpc.getBlockTimestamp(blockNumber);
168
+ this.timestampCache.set(blockNumber, timestamp);
169
+ if (this.timestampCache.size > this.maxCacheSize) {
170
+ const entries = Array.from(this.timestampCache.entries());
171
+ this.timestampCache = new Map(entries.slice(entries.length - (this.maxCacheSize / 2)));
172
+ }
173
+ return timestamp;
174
+ }
175
+ catch {
176
+ return 0;
177
+ }
178
+ }
179
+ }
180
+ //# sourceMappingURL=block-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scanner.js","sourceRoot":"","sources":["../../src/scanner/block-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Call Tree Parser — parses callTracer output into typed CallTreeNode.
3
+ *
4
+ * Recursively processes the nested call structure from debug_traceTransaction
5
+ * with callTracer, optionally decoding method signatures when ABI is available.
6
+ */
7
+ import type { CallTreeNode } from '../types/index.js';
8
+ import type { AbiRegistry } from '../decoder/abi-registry.js';
9
+ /**
10
+ * Parse raw callTracer output into a typed CallTreeNode.
11
+ */
12
+ export declare function parseCallTree(raw: any, depth?: number, registry?: AbiRegistry): CallTreeNode;
13
+ /**
14
+ * Flatten a call tree into a linear array of all nodes.
15
+ */
16
+ export declare function flattenCallTree(node: CallTreeNode): CallTreeNode[];
17
+ /**
18
+ * Extract all unique contract addresses from a call tree.
19
+ */
20
+ export declare function extractInvolvedContracts(node: CallTreeNode): string[];
21
+ /**
22
+ * Find all calls targeting a specific contract address.
23
+ */
24
+ export declare function findCallsTo(node: CallTreeNode, targetAddress: string): CallTreeNode[];
25
+ //# sourceMappingURL=call-tree-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-tree-parser.d.ts","sourceRoot":"","sources":["../../src/tracer/call-tree-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAG9D;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,YAAY,CAsC/F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,CAMlE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CASrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,GAAG,YAAY,EAAE,CAGrF"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Call Tree Parser — parses callTracer output into typed CallTreeNode.
3
+ *
4
+ * Recursively processes the nested call structure from debug_traceTransaction
5
+ * with callTracer, optionally decoding method signatures when ABI is available.
6
+ */
7
+ import { normalizeAddress } from '../utils/address.js';
8
+ /**
9
+ * Parse raw callTracer output into a typed CallTreeNode.
10
+ */
11
+ export function parseCallTree(raw, depth = 0, registry) {
12
+ const to = normalizeAddress(raw.to ?? '0x0000000000000000000000000000000000000000');
13
+ const from = normalizeAddress(raw.from ?? '');
14
+ let method;
15
+ if (raw.input && raw.input.length >= 10 && registry) {
16
+ const contract = registry.get(to);
17
+ if (contract) {
18
+ try {
19
+ const selector = raw.input.slice(0, 10);
20
+ const fragment = contract.iface.getFunction(selector);
21
+ if (fragment) {
22
+ method = fragment.format('sighash');
23
+ }
24
+ }
25
+ catch { /* Unknown selector */ }
26
+ }
27
+ }
28
+ const node = {
29
+ type: raw.type ?? 'CALL',
30
+ from,
31
+ to,
32
+ value: raw.value ?? '0x0',
33
+ gas: raw.gas ?? '0x0',
34
+ gasUsed: raw.gasUsed ?? '0x0',
35
+ input: raw.input ?? '0x',
36
+ output: raw.output ?? '0x',
37
+ method,
38
+ error: raw.error,
39
+ calls: [],
40
+ depth,
41
+ };
42
+ if (raw.calls && Array.isArray(raw.calls)) {
43
+ node.calls = raw.calls.map((sub) => parseCallTree(sub, depth + 1, registry));
44
+ }
45
+ return node;
46
+ }
47
+ /**
48
+ * Flatten a call tree into a linear array of all nodes.
49
+ */
50
+ export function flattenCallTree(node) {
51
+ const result = [node];
52
+ for (const child of node.calls) {
53
+ result.push(...flattenCallTree(child));
54
+ }
55
+ return result;
56
+ }
57
+ /**
58
+ * Extract all unique contract addresses from a call tree.
59
+ */
60
+ export function extractInvolvedContracts(node) {
61
+ const addresses = new Set();
62
+ function walk(n) {
63
+ if (n.from)
64
+ addresses.add(normalizeAddress(n.from));
65
+ if (n.to)
66
+ addresses.add(normalizeAddress(n.to));
67
+ for (const child of n.calls)
68
+ walk(child);
69
+ }
70
+ walk(node);
71
+ return Array.from(addresses);
72
+ }
73
+ /**
74
+ * Find all calls targeting a specific contract address.
75
+ */
76
+ export function findCallsTo(node, targetAddress) {
77
+ const target = normalizeAddress(targetAddress);
78
+ return flattenCallTree(node).filter((n) => normalizeAddress(n.to) === target);
79
+ }
80
+ //# sourceMappingURL=call-tree-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-tree-parser.js","sourceRoot":"","sources":["../../src/tracer/call-tree-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * State Diff Parser — parses prestateTracer output into typed StateDiff and BalanceChange.
3
+ */
4
+ import type { StateDiff, BalanceChange } from '../types/index.js';
5
+ /**
6
+ * Parse prestateTracer diffMode output into StateDiff entries.
7
+ */
8
+ export declare function parseStateDiffs(raw: any): StateDiff[];
9
+ /**
10
+ * Parse prestateTracer diffMode output into BalanceChange entries.
11
+ */
12
+ export declare function parseBalanceChanges(raw: any): BalanceChange[];
13
+ //# sourceMappingURL=state-diff-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-diff-parser.d.ts","sourceRoot":"","sources":["../../src/tracer/state-diff-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlE;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,CAoCrD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,EAAE,CA6B7D"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * State Diff Parser — parses prestateTracer output into typed StateDiff and BalanceChange.
3
+ */
4
+ import { normalizeAddress } from '../utils/address.js';
5
+ /**
6
+ * Parse prestateTracer diffMode output into StateDiff entries.
7
+ */
8
+ export function parseStateDiffs(raw) {
9
+ if (!raw || !raw.pre || !raw.post)
10
+ return [];
11
+ const diffs = [];
12
+ const allAddresses = new Set([
13
+ ...Object.keys(raw.pre || {}),
14
+ ...Object.keys(raw.post || {}),
15
+ ]);
16
+ for (const address of allAddresses) {
17
+ const pre = raw.pre[address];
18
+ const post = raw.post[address];
19
+ const preStorage = pre?.storage ?? {};
20
+ const postStorage = post?.storage ?? {};
21
+ const allSlots = new Set([
22
+ ...Object.keys(preStorage),
23
+ ...Object.keys(postStorage),
24
+ ]);
25
+ for (const slot of allSlots) {
26
+ const before = preStorage[slot] ?? '0x0';
27
+ const after = postStorage[slot] ?? '0x0';
28
+ if (before !== after) {
29
+ diffs.push({
30
+ contract: normalizeAddress(address),
31
+ slot,
32
+ before,
33
+ after,
34
+ });
35
+ }
36
+ }
37
+ }
38
+ return diffs;
39
+ }
40
+ /**
41
+ * Parse prestateTracer diffMode output into BalanceChange entries.
42
+ */
43
+ export function parseBalanceChanges(raw) {
44
+ if (!raw || !raw.pre || !raw.post)
45
+ return [];
46
+ const changes = [];
47
+ const allAddresses = new Set([
48
+ ...Object.keys(raw.pre || {}),
49
+ ...Object.keys(raw.post || {}),
50
+ ]);
51
+ for (const address of allAddresses) {
52
+ const pre = raw.pre[address];
53
+ const post = raw.post[address];
54
+ const beforeHex = pre?.balance ?? '0x0';
55
+ const afterHex = post?.balance ?? '0x0';
56
+ const before = BigInt(beforeHex);
57
+ const after = BigInt(afterHex);
58
+ if (before !== after) {
59
+ changes.push({
60
+ address: normalizeAddress(address),
61
+ token: 'native',
62
+ before,
63
+ after,
64
+ delta: after - before,
65
+ });
66
+ }
67
+ }
68
+ return changes;
69
+ }
70
+ //# sourceMappingURL=state-diff-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-diff-parser.js","sourceRoot":"","sources":["../../src/tracer/state-diff-parser.ts"],"names":[],"mappings":"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"}