@xdc.org/interaction-detector 1.0.1 → 1.0.6
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 +178 -30
- package/dist/checkpoint/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint/checkpoint.js +6 -2
- package/dist/checkpoint/checkpoint.js.map +1 -1
- package/dist/explorer/explorer-client.d.ts.map +1 -1
- package/dist/explorer/explorer-client.js +22 -13
- package/dist/explorer/explorer-client.js.map +1 -1
- package/dist/explorer/rate-limiter.d.ts +15 -3
- package/dist/explorer/rate-limiter.d.ts.map +1 -1
- package/dist/explorer/rate-limiter.js +15 -3
- package/dist/explorer/rate-limiter.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/rpc/rpc-client.d.ts.map +1 -1
- package/dist/rpc/rpc-client.js +13 -9
- package/dist/rpc/rpc-client.js.map +1 -1
- package/dist/rpc/ws-manager.d.ts +23 -0
- package/dist/rpc/ws-manager.d.ts.map +1 -1
- package/dist/rpc/ws-manager.js +40 -4
- package/dist/rpc/ws-manager.js.map +1 -1
- package/dist/scanner/block-scanner.d.ts +14 -4
- package/dist/scanner/block-scanner.d.ts.map +1 -1
- package/dist/scanner/block-scanner.js +33 -12
- package/dist/scanner/block-scanner.js.map +1 -1
- package/dist/tracer/call-tree-parser.d.ts +2 -1
- package/dist/tracer/call-tree-parser.d.ts.map +1 -1
- package/dist/tracer/call-tree-parser.js +9 -5
- package/dist/tracer/call-tree-parser.js.map +1 -1
- package/dist/utils/address.js +1 -1
- package/dist/utils/address.js.map +1 -1
- package/dist/utils/format.d.ts +4 -2
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +12 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/watcher/contract-watcher.d.ts +1 -0
- package/dist/watcher/contract-watcher.d.ts.map +1 -1
- package/dist/watcher/contract-watcher.js +20 -7
- package/dist/watcher/contract-watcher.js.map +1 -1
- package/dist/watcher/log-poller.d.ts +16 -0
- package/dist/watcher/log-poller.d.ts.map +1 -1
- package/dist/watcher/log-poller.js +20 -13
- package/dist/watcher/log-poller.js.map +1 -1
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -17,9 +17,12 @@ Combines: **events** + **direct calls** + **internal calls** + **transaction tra
|
|
|
17
17
|
- [1. ContractWatcher — Real-Time Monitoring](#1-contractwatcher--real-time-monitoring)
|
|
18
18
|
- [2. BlockScanner — Historical Queries](#2-blockscanner--historical-queries)
|
|
19
19
|
- [3. TransactionTracer — Deep Transaction Analysis](#3-transactiontracer--deep-transaction-analysis)
|
|
20
|
+
- [LogPoller — Standalone Log Fetching](#logpoller--standalone-log-fetching)
|
|
20
21
|
- [Explorer API Client](#explorer-api-client)
|
|
21
22
|
- [Event Decoder \& ABI Registry](#event-decoder--abi-registry)
|
|
22
23
|
- [Checkpoint Persistence](#checkpoint-persistence)
|
|
24
|
+
- [Built-in Backends](#built-in-backends)
|
|
25
|
+
- [Custom Backends](#custom-backends)
|
|
23
26
|
- [Utility Functions](#utility-functions)
|
|
24
27
|
- [Configuration Reference](#configuration-reference)
|
|
25
28
|
- [InteractionDetectorConfig (ContractWatcher)](#interactiondetectorconfig-contractwatcher)
|
|
@@ -54,7 +57,7 @@ Combines: **events** + **direct calls** + **internal calls** + **transaction tra
|
|
|
54
57
|
npm install @xdc.org/interaction-detector
|
|
55
58
|
```
|
|
56
59
|
|
|
57
|
-
**Dependencies:** `ethers` v6, `
|
|
60
|
+
**Dependencies:** `ethers` v6, `ws` — all installed automatically. HTTP calls use Node.js native `fetch` (requires Node ≥ 18).
|
|
58
61
|
|
|
59
62
|
---
|
|
60
63
|
|
|
@@ -415,6 +418,54 @@ const addresses = extractInvolvedContracts(result.callTree);
|
|
|
415
418
|
|
|
416
419
|
---
|
|
417
420
|
|
|
421
|
+
## LogPoller — Standalone Log Fetching
|
|
422
|
+
|
|
423
|
+
The `LogPoller` is used internally by `ContractWatcher` and `BlockScanner`, but is also exported for standalone use when you need direct control over `eth_getLogs` fetching with automatic chunking and concurrency.
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { LogPoller, RpcClient } from '@xdc.org/interaction-detector';
|
|
427
|
+
import type { FetchLogsResult } from '@xdc.org/interaction-detector';
|
|
428
|
+
|
|
429
|
+
const rpc = new RpcClient('https://rpc.xinfin.network');
|
|
430
|
+
const poller = new LogPoller(
|
|
431
|
+
rpc,
|
|
432
|
+
['0x0000000000000000000000000000000000000088'], // addresses to monitor
|
|
433
|
+
{ maxBlockRange: 100, concurrency: 3 }, // optional PollingConfig
|
|
434
|
+
'info', // optional log level
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// ── Basic usage — returns RawLog[] ──────────────────────────
|
|
438
|
+
const logs = await poller.fetchLogs(75_000_000, 75_001_000);
|
|
439
|
+
console.log(`Fetched ${logs.length} logs`);
|
|
440
|
+
|
|
441
|
+
// ── With failure tracking — returns FetchLogsResult ─────────
|
|
442
|
+
// Tracks which block ranges failed, so you can retry or alert
|
|
443
|
+
const result: FetchLogsResult = await poller.fetchLogs(75_000_000, 75_001_000, { trackFailures: true });
|
|
444
|
+
|
|
445
|
+
console.log(`Fetched ${result.logs.length} logs`);
|
|
446
|
+
if (result.failedRanges.length > 0) {
|
|
447
|
+
console.warn('Some ranges failed:');
|
|
448
|
+
for (const range of result.failedRanges) {
|
|
449
|
+
console.warn(` blocks ${range.from}–${range.to}: ${range.error}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**`FetchLogsResult` type:**
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
interface FetchLogsResult {
|
|
458
|
+
/** Successfully fetched logs */
|
|
459
|
+
logs: RawLog[];
|
|
460
|
+
/** Block ranges that failed to fetch (data may be missing for these ranges) */
|
|
461
|
+
failedRanges: Array<{ from: number; to: number; error: string }>;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
> **Note:** The default `fetchLogs(from, to)` call (without `{ trackFailures: true }`) returns `RawLog[]` directly for backward compatibility. Failed chunks return empty arrays silently — use the `trackFailures` option when you need visibility into partial failures.
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
418
469
|
## Explorer API Client
|
|
419
470
|
|
|
420
471
|
Standalone Etherscan-compatible API client. Works with XDCScan, Etherscan v2, BSCScan, PolygonScan, and any Etherscan-compatible explorer.
|
|
@@ -482,13 +533,13 @@ explorer.destroy(); // Cleans up rate limiter timers
|
|
|
482
533
|
|
|
483
534
|
**Explorer compatibility:**
|
|
484
535
|
|
|
485
|
-
| Explorer
|
|
486
|
-
|
|
|
487
|
-
| **Etherscan v2** ⭐
|
|
488
|
-
| **XDCScan**
|
|
489
|
-
| **BSCScan**
|
|
490
|
-
| **PolygonScan**
|
|
491
|
-
| **Custom**
|
|
536
|
+
| Explorer | Base URL | Chain | Free Rate Limit |
|
|
537
|
+
| ------------------- | --------------------------------- | -------------------------------- | --------------- |
|
|
538
|
+
| **Etherscan v2** ⭐ | `https://api.etherscan.io/v2/api` | XDC (50) + 80 chains via chainId | 5 req/s |
|
|
539
|
+
| **XDCScan** | `https://xdc.blocksscan.io/api` | XDC Mainnet (50) | 5 req/s |
|
|
540
|
+
| **BSCScan** | `https://api.bscscan.com/api` | BSC (56) | 5 req/s |
|
|
541
|
+
| **PolygonScan** | `https://api.polygonscan.com/api` | Polygon (137) | 5 req/s |
|
|
542
|
+
| **Custom** | Any Etherscan-compatible URL | Any EVM chain | Configurable |
|
|
492
543
|
|
|
493
544
|
---
|
|
494
545
|
|
|
@@ -515,8 +566,12 @@ registry.register(
|
|
|
515
566
|
// Register from a JSON ABI array
|
|
516
567
|
registry.register('0xAnotherContract', require('./MyContract.json').abi, 'MyContract');
|
|
517
568
|
|
|
518
|
-
// Auto-fetch from block explorer (verified contracts only)
|
|
519
|
-
const explorer = new ExplorerClient({
|
|
569
|
+
// Auto-fetch from block explorer (verified contracts only, requires API key)
|
|
570
|
+
const explorer = new ExplorerClient({
|
|
571
|
+
apiUrl: 'https://api.etherscan.io/v2/api',
|
|
572
|
+
apiKey: 'YOUR_ETHERSCAN_API_KEY',
|
|
573
|
+
chainId: 50,
|
|
574
|
+
});
|
|
520
575
|
const success = await registry.registerFromExplorer('0xVerifiedContract', explorer, 'VerifiedToken');
|
|
521
576
|
console.log(success ? 'ABI fetched!' : 'Contract not verified');
|
|
522
577
|
|
|
@@ -544,36 +599,112 @@ if (decoded) {
|
|
|
544
599
|
|
|
545
600
|
Checkpoints let the watcher resume from where it left off after a restart.
|
|
546
601
|
|
|
602
|
+
| Backend | Storage | Survives Restart? | Best For |
|
|
603
|
+
| -------- | -------------------------------- | ----------------- | ------------------------------ |
|
|
604
|
+
| `memory` | JavaScript `Map` in RAM | ❌ No | Development, testing |
|
|
605
|
+
| `file` | `checkpoints.json` on disk | ✅ Yes | Simple production deployments |
|
|
606
|
+
| `custom` | Your own (Redis, SQL, InfluxDB…) | ✅ Yes | Distributed / production-grade |
|
|
607
|
+
|
|
608
|
+
### Built-in Backends
|
|
609
|
+
|
|
547
610
|
```typescript
|
|
548
611
|
import { MemoryCheckpoint, FileCheckpoint, createCheckpointBackend } from '@xdc.org/interaction-detector';
|
|
549
612
|
|
|
550
613
|
// ── Memory (development / testing) ───────────────────────────
|
|
614
|
+
// Stores in RAM only — lost when process stops
|
|
551
615
|
const memCp = new MemoryCheckpoint();
|
|
552
616
|
await memCp.save('watcher-key', 75_000_000);
|
|
553
617
|
await memCp.load('watcher-key'); // 75000000
|
|
554
618
|
|
|
555
619
|
// ── File (simple production) ─────────────────────────────────
|
|
620
|
+
// Auto-creates directory and file on first save
|
|
556
621
|
const fileCp = new FileCheckpoint('./checkpoints');
|
|
557
622
|
await fileCp.save('watcher-key', 75_000_000);
|
|
558
|
-
// Persists to ./checkpoints/checkpoints.json
|
|
623
|
+
// Persists to ./checkpoints/checkpoints.json (relative to process.cwd())
|
|
559
624
|
// Survives process restarts
|
|
560
625
|
|
|
561
|
-
// ──
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
626
|
+
// ── Factory function ─────────────────────────────────────────
|
|
627
|
+
const cp = createCheckpointBackend({ backend: 'file', path: './data' });
|
|
628
|
+
const cp2 = createCheckpointBackend({ backend: 'memory' });
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Custom Backends
|
|
632
|
+
|
|
633
|
+
Implement the `CheckpointBackend` interface — just two methods:
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
interface CheckpointBackend {
|
|
637
|
+
save(key: string, blockNumber: number): Promise<void>;
|
|
638
|
+
load(key: string): Promise<number | null>;
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
**Redis:**
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
checkpoint: {
|
|
646
|
+
backend: 'custom',
|
|
647
|
+
custom: {
|
|
648
|
+
async save(key: string, blockNumber: number) {
|
|
649
|
+
await redis.set(`checkpoint:${key}`, blockNumber);
|
|
650
|
+
},
|
|
651
|
+
async load(key: string) {
|
|
652
|
+
const val = await redis.get(`checkpoint:${key}`);
|
|
653
|
+
return val ? parseInt(val) : null;
|
|
654
|
+
},
|
|
565
655
|
},
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
656
|
+
},
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
**PostgreSQL / MySQL:**
|
|
660
|
+
|
|
661
|
+
```typescript
|
|
662
|
+
checkpoint: {
|
|
663
|
+
backend: 'custom',
|
|
664
|
+
custom: {
|
|
665
|
+
async save(key: string, blockNumber: number) {
|
|
666
|
+
await pool.query(
|
|
667
|
+
`INSERT INTO checkpoints (key, block_number) VALUES ($1, $2)
|
|
668
|
+
ON CONFLICT (key) DO UPDATE SET block_number = $2`,
|
|
669
|
+
[key, blockNumber],
|
|
670
|
+
);
|
|
671
|
+
},
|
|
672
|
+
async load(key: string) {
|
|
673
|
+
const result = await pool.query(
|
|
674
|
+
'SELECT block_number FROM checkpoints WHERE key = $1',
|
|
675
|
+
[key],
|
|
676
|
+
);
|
|
677
|
+
return result.rows[0]?.block_number ?? null;
|
|
678
|
+
},
|
|
569
679
|
},
|
|
570
|
-
}
|
|
680
|
+
},
|
|
681
|
+
```
|
|
571
682
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
683
|
+
**InfluxDB:**
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
checkpoint: {
|
|
687
|
+
backend: 'custom',
|
|
688
|
+
custom: {
|
|
689
|
+
async save(key: string, blockNumber: number) {
|
|
690
|
+
await influx.writePoints([{
|
|
691
|
+
measurement: 'checkpoints',
|
|
692
|
+
tags: { key },
|
|
693
|
+
fields: { block_number: blockNumber },
|
|
694
|
+
}]);
|
|
695
|
+
},
|
|
696
|
+
async load(key: string) {
|
|
697
|
+
const result = await influx.query(
|
|
698
|
+
`SELECT LAST(block_number) FROM checkpoints WHERE key = '${key}'`,
|
|
699
|
+
);
|
|
700
|
+
return result[0]?.block_number ?? null;
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
},
|
|
575
704
|
```
|
|
576
705
|
|
|
706
|
+
> **Recommendation:** Use **Redis** or **SQL** for checkpoint storage. InfluxDB is better suited for storing the detected events/interactions as time-series data rather than simple key-value checkpoint state.
|
|
707
|
+
|
|
577
708
|
---
|
|
578
709
|
|
|
579
710
|
## Utility Functions
|
|
@@ -587,9 +718,9 @@ import {
|
|
|
587
718
|
isAddress, // validate hex address (20 bytes)
|
|
588
719
|
addressEqual, // compare addresses (case-insensitive, prefix-aware)
|
|
589
720
|
|
|
590
|
-
// Formatting
|
|
721
|
+
// Formatting (precision-safe for large BigInt values)
|
|
591
722
|
formatXDC, // bigint wei → '1.23M XDC' / '456.78K XDC'
|
|
592
|
-
formatWei, // bigint wei → '1.23M' (generic,
|
|
723
|
+
formatWei, // bigint wei → '1.23M' (generic, configurable decimals)
|
|
593
724
|
parseHexOrDecimal, // '0x100' → 256, '256' → 256
|
|
594
725
|
toHex, // 256 → '0x100'
|
|
595
726
|
shortAddress, // '0x1234...abcd'
|
|
@@ -600,6 +731,18 @@ import {
|
|
|
600
731
|
} from '@xdc.org/interaction-detector';
|
|
601
732
|
```
|
|
602
733
|
|
|
734
|
+
**Address normalization behavior:**
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
normalizeAddress('0xAbCdEf...'); // → '0xabcdef...' (lowercased)
|
|
738
|
+
normalizeAddress('xdcAbCdEf...'); // → '0xabcdef...' (xdc → 0x)
|
|
739
|
+
normalizeAddress(''); // → '0x0000000000000000000000000000000000000000' (zero address)
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
> **Note:** `normalizeAddress` returns the zero address for empty or falsy inputs, consistent with EVM conventions. This prevents empty strings from propagating as ghost entries in maps and sets.
|
|
743
|
+
|
|
744
|
+
**Formatting precision:** Both `formatXDC` and `formatWei` use bigint arithmetic internally, so large amounts (beyond JavaScript's `Number.MAX_SAFE_INTEGER`) are displayed correctly without loss of precision.
|
|
745
|
+
|
|
603
746
|
---
|
|
604
747
|
|
|
605
748
|
## Configuration Reference
|
|
@@ -672,30 +815,33 @@ import {
|
|
|
672
815
|
|
|
673
816
|
- **Block range limit:** XDC RPC limits `eth_getLogs` to 100 blocks per request. The `LogPoller` and `BlockScanner` automatically chunk requests. Don't set `maxBlockRange` above 100 for XDC.
|
|
674
817
|
|
|
675
|
-
- **Address prefix:** XDC uses the `xdc` prefix instead of `0x`. All library functions accept both formats. Internally, everything normalizes to lowercase `0x`.
|
|
818
|
+
- **Address prefix:** XDC uses the `xdc` prefix instead of `0x`. All library functions accept both formats. Internally, everything normalizes to lowercase `0x`. Empty/falsy inputs normalize to the zero address (`0x000...000`).
|
|
676
819
|
|
|
677
820
|
- **Tracing:** `debug_traceTransaction` requires an archive node for historical transactions. For monitoring current blocks in real-time, a standard full node works fine.
|
|
678
821
|
|
|
822
|
+
- **Atomic checkpoints:** The `FileCheckpoint` backend uses atomic write-then-rename to prevent data corruption on process crash. Checkpoint data is always consistent.
|
|
823
|
+
|
|
679
824
|
---
|
|
680
825
|
|
|
681
826
|
## Architecture
|
|
682
827
|
|
|
683
828
|
```
|
|
684
829
|
┌──────────────────────────────────────────────────────────┐
|
|
685
|
-
│ @xdc.org/interaction-detector
|
|
830
|
+
│ @xdc.org/interaction-detector │
|
|
686
831
|
│ │
|
|
687
832
|
│ ┌───────────────────────────────────────────────────┐ │
|
|
688
833
|
│ │ ContractWatcher (real-time monitoring) │ │
|
|
689
834
|
│ │ ├── WsManager (WebSocket subscription) │ │
|
|
690
|
-
│ │ ├── LogPoller
|
|
691
|
-
│ │ ├── ExplorerClient
|
|
835
|
+
│ │ ├── LogPoller* (eth_getLogs fallback) │ │
|
|
836
|
+
│ │ ├── ExplorerClient* (txlist + txlistinternal)│ │
|
|
692
837
|
│ │ ├── EventDecoder (ABI + XDC fallback) │ │
|
|
693
838
|
│ │ └── Checkpoint (pluggable persistence) │ │
|
|
694
839
|
│ └───────────────────────────────────────────────────┘ │
|
|
695
840
|
│ │
|
|
696
841
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
697
842
|
│ │ BlockScanner (historical queries) │ │
|
|
698
|
-
│ │
|
|
843
|
+
│ │ ├── LogPoller* (chunked eth_getLogs) │ │
|
|
844
|
+
│ │ └── ExplorerClient* (API adapter) │ │
|
|
699
845
|
│ └────────────────────────────────────────────────────┘ │
|
|
700
846
|
│ │
|
|
701
847
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
@@ -709,6 +855,8 @@ import {
|
|
|
709
855
|
│ │ (retry, timeout, │ │ (address normalize, │ │
|
|
710
856
|
│ │ fallback, WS) │ │ format, logger, cache) │ │
|
|
711
857
|
│ └────────────────────┘ └───────────────────────────┘ │
|
|
858
|
+
│ │
|
|
859
|
+
│ * = also exported as standalone classes │
|
|
712
860
|
└──────────────────────────────────────────────────────────┘
|
|
713
861
|
```
|
|
714
862
|
|
|
@@ -757,7 +905,7 @@ npm install
|
|
|
757
905
|
# Compile TypeScript
|
|
758
906
|
npm run build
|
|
759
907
|
|
|
760
|
-
# Run unit tests (
|
|
908
|
+
# Run unit tests (114 tests across 11 test files)
|
|
761
909
|
npm test
|
|
762
910
|
|
|
763
911
|
# Watch mode (auto-recompile on save)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/checkpoint/checkpoint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;GAEG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,KAAK,CAAkC;IAEzC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAGhD;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,GAAE,MAAwB;IAOvC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/checkpoint/checkpoint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;GAEG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,KAAK,CAAkC;IAEzC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAGhD;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,GAAE,MAAwB;IAOvC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAKjC,QAAQ;CAWvB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,GAAG,iBAAiB,CAapB"}
|
|
@@ -30,7 +30,9 @@ export class FileCheckpoint {
|
|
|
30
30
|
async save(key, blockNumber) {
|
|
31
31
|
const data = await this.readFile();
|
|
32
32
|
data[key] = blockNumber;
|
|
33
|
-
|
|
33
|
+
const tmpPath = this.filePath + '.tmp';
|
|
34
|
+
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
35
|
+
fs.renameSync(tmpPath, this.filePath);
|
|
34
36
|
}
|
|
35
37
|
async load(key) {
|
|
36
38
|
const data = await this.readFile();
|
|
@@ -43,7 +45,9 @@ export class FileCheckpoint {
|
|
|
43
45
|
return JSON.parse(content);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
|
-
catch {
|
|
48
|
+
catch {
|
|
49
|
+
/* ignore corrupt file */
|
|
50
|
+
}
|
|
47
51
|
return {};
|
|
48
52
|
}
|
|
49
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/checkpoint/checkpoint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACnB,KAAK,GAAwB,IAAI,GAAG,EAAE,CAAC;IAE/C,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,WAAmB;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACrC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,cAAc;IACR,QAAQ,CAAS;IAElC,YAAY,UAAkB,eAAe;QAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,WAAmB;QACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;QACxB,
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/checkpoint/checkpoint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACnB,KAAK,GAAwB,IAAI,GAAG,EAAE,CAAC;IAE/C,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,WAAmB;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACrC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,cAAc;IACR,QAAQ,CAAS;IAElC,YAAY,UAAkB,eAAe;QAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,WAAmB;QACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;QACvC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACxD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAIvC;IACC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,gBAAgB,EAAE,CAAC;IAE3C,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzC,KAAK,QAAQ;YACX,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC9E,OAAO,MAAM,CAAC,MAAM,CAAC;QACvB,KAAK,QAAQ,CAAC;QACd;YACE,OAAO,IAAI,gBAAgB,EAAE,CAAC;IAClC,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explorer-client.d.ts","sourceRoot":"","sources":["../../src/explorer/explorer-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"explorer-client.d.ts","sourceRoot":"","sources":["../../src/explorer/explorer-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEvF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,QAAQ;IAevD;;OAEG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAC1E,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAcjC;;OAEG;IACG,uBAAuB,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAC1E,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAcjC;;OAEG;IACG,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAWjF;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAgBnH;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAmB5D;;OAEG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAC1E,OAAO,CAAC,GAAG,EAAE,CAAC;IAajB;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUlD;;OAEG;IACH,OAAO,IAAI,IAAI;YAMD,OAAO;CA0CtB"}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Works with: XDCScan, Etherscan v2, BSCScan, PolygonScan, and any
|
|
5
5
|
* Etherscan-compatible API by changing the base URL.
|
|
6
6
|
*/
|
|
7
|
-
import axios from 'axios';
|
|
8
7
|
import { Logger } from '../utils/logger.js';
|
|
9
8
|
import { RateLimiter } from './rate-limiter.js';
|
|
10
9
|
export class ExplorerClient {
|
|
@@ -19,6 +18,10 @@ export class ExplorerClient {
|
|
|
19
18
|
this.chainId = config.chainId;
|
|
20
19
|
this.rateLimiter = new RateLimiter(config.rateLimitPerSec ?? 5);
|
|
21
20
|
this.logger = new Logger('ExplorerClient', logLevel ?? 'info');
|
|
21
|
+
// Warn if using Etherscan v2 without an API key (required for all endpoints)
|
|
22
|
+
if (this.baseUrl.includes('etherscan.io') && !this.apiKey) {
|
|
23
|
+
this.logger.warn('Etherscan v2 requires an API key. Requests will fail without one. Get a free key at https://etherscan.io/myapikey');
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* Get external transactions for an address (txlist).
|
|
@@ -33,7 +36,7 @@ export class ExplorerClient {
|
|
|
33
36
|
sort: options?.sort ?? 'asc',
|
|
34
37
|
};
|
|
35
38
|
const results = await this.request(params);
|
|
36
|
-
return (results ?? []).map(
|
|
39
|
+
return (results ?? []).map(tx => ({ ...tx, txType: 'external' }));
|
|
37
40
|
}
|
|
38
41
|
/**
|
|
39
42
|
* Get internal transactions for an address (txlistinternal).
|
|
@@ -48,7 +51,7 @@ export class ExplorerClient {
|
|
|
48
51
|
sort: options?.sort ?? 'asc',
|
|
49
52
|
};
|
|
50
53
|
const results = await this.request(params);
|
|
51
|
-
return (results ?? []).map(
|
|
54
|
+
return (results ?? []).map(tx => ({ ...tx, txType: 'internal' }));
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
54
57
|
* Get internal transactions within a specific transaction (txlistinternal by txhash).
|
|
@@ -60,7 +63,7 @@ export class ExplorerClient {
|
|
|
60
63
|
txhash: txHash,
|
|
61
64
|
};
|
|
62
65
|
const results = await this.request(params);
|
|
63
|
-
return (results ?? []).map(
|
|
66
|
+
return (results ?? []).map(tx => ({ ...tx, txType: 'internal' }));
|
|
64
67
|
}
|
|
65
68
|
/**
|
|
66
69
|
* Get event logs for an address (getLogs).
|
|
@@ -140,17 +143,23 @@ export class ExplorerClient {
|
|
|
140
143
|
params.chainid = String(this.chainId);
|
|
141
144
|
}
|
|
142
145
|
try {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
const queryString = new URLSearchParams(params).toString();
|
|
147
|
+
const url = `${this.baseUrl}?${queryString}`;
|
|
148
|
+
const response = await fetch(url, {
|
|
149
|
+
method: 'GET',
|
|
150
|
+
signal: AbortSignal.timeout(15000),
|
|
146
151
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return [];
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
150
154
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
const data = (await response.json());
|
|
156
|
+
if (data.status === '0') {
|
|
157
|
+
const msg = (data.message || '').toLowerCase();
|
|
158
|
+
if (msg.includes('no') && (msg.includes('found') || msg.includes('record') || msg.includes('transaction'))) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const errorMsg = data.result || data.message || 'Unknown error';
|
|
162
|
+
this.logger.warn(`Explorer API error: ${errorMsg}`);
|
|
154
163
|
return null;
|
|
155
164
|
}
|
|
156
165
|
return data.result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explorer-client.js","sourceRoot":"","sources":["../../src/explorer/explorer-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"explorer-client.js","sourceRoot":"","sources":["../../src/explorer/explorer-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,22 +10,34 @@ export declare class RateLimiter {
|
|
|
10
10
|
private queue;
|
|
11
11
|
private drainTimer;
|
|
12
12
|
/**
|
|
13
|
+
* Initializes the rate limiter with a given requests-per-second cap.
|
|
13
14
|
* @param maxPerSecond Maximum requests per second (default: 5)
|
|
14
15
|
*/
|
|
15
16
|
constructor(maxPerSecond?: number);
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
+
* Waits until a request slot is available, then consumes one token.
|
|
19
|
+
* If no tokens are available, the caller is queued and resolved later.
|
|
18
20
|
*/
|
|
19
21
|
acquire(): Promise<void>;
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
23
|
+
* Returns how many requests are currently waiting in the queue.
|
|
22
24
|
*/
|
|
23
25
|
get pending(): number;
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
27
|
+
* Cleans up the rate limiter by clearing the drain timer
|
|
28
|
+
* and resolving all queued requests so nothing is left hanging.
|
|
26
29
|
*/
|
|
27
30
|
destroy(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Refills tokens based on elapsed time since the last refill,
|
|
33
|
+
* capping at the maximum bucket size.
|
|
34
|
+
*/
|
|
28
35
|
private refill;
|
|
36
|
+
/**
|
|
37
|
+
* Starts a periodic timer that refills tokens and resolves
|
|
38
|
+
* queued requests as slots become available. Stops automatically
|
|
39
|
+
* when the queue is empty.
|
|
40
|
+
*/
|
|
29
41
|
private startDrain;
|
|
30
42
|
}
|
|
31
43
|
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -1 +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
|
|
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;;;OAGG;gBACS,YAAY,GAAE,MAAU;IAOpC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI;IAWf;;;OAGG;IACH,OAAO,CAAC,MAAM;IAQd;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAkBnB"}
|
|
@@ -10,6 +10,7 @@ export class RateLimiter {
|
|
|
10
10
|
queue = [];
|
|
11
11
|
drainTimer = null;
|
|
12
12
|
/**
|
|
13
|
+
* Initializes the rate limiter with a given requests-per-second cap.
|
|
13
14
|
* @param maxPerSecond Maximum requests per second (default: 5)
|
|
14
15
|
*/
|
|
15
16
|
constructor(maxPerSecond = 5) {
|
|
@@ -19,7 +20,8 @@ export class RateLimiter {
|
|
|
19
20
|
this.lastRefill = Date.now();
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
+
* Waits until a request slot is available, then consumes one token.
|
|
24
|
+
* If no tokens are available, the caller is queued and resolved later.
|
|
23
25
|
*/
|
|
24
26
|
async acquire() {
|
|
25
27
|
this.refill();
|
|
@@ -34,13 +36,14 @@ export class RateLimiter {
|
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
|
-
*
|
|
39
|
+
* Returns how many requests are currently waiting in the queue.
|
|
38
40
|
*/
|
|
39
41
|
get pending() {
|
|
40
42
|
return this.queue.length;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
|
-
*
|
|
45
|
+
* Cleans up the rate limiter by clearing the drain timer
|
|
46
|
+
* and resolving all queued requests so nothing is left hanging.
|
|
44
47
|
*/
|
|
45
48
|
destroy() {
|
|
46
49
|
if (this.drainTimer) {
|
|
@@ -52,6 +55,10 @@ export class RateLimiter {
|
|
|
52
55
|
}
|
|
53
56
|
this.queue = [];
|
|
54
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Refills tokens based on elapsed time since the last refill,
|
|
60
|
+
* capping at the maximum bucket size.
|
|
61
|
+
*/
|
|
55
62
|
refill() {
|
|
56
63
|
const now = Date.now();
|
|
57
64
|
const elapsed = now - this.lastRefill;
|
|
@@ -59,6 +66,11 @@ export class RateLimiter {
|
|
|
59
66
|
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
60
67
|
this.lastRefill = now;
|
|
61
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Starts a periodic timer that refills tokens and resolves
|
|
71
|
+
* queued requests as slots become available. Stops automatically
|
|
72
|
+
* when the queue is empty.
|
|
73
|
+
*/
|
|
62
74
|
startDrain() {
|
|
63
75
|
if (this.drainTimer)
|
|
64
76
|
return;
|
|
@@ -1 +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
|
|
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;;;OAGG;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;;;OAGG;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;;;OAGG;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;IAED;;;OAGG;IACK,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;IAED;;;;OAIG;IACK,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
CHANGED
|
@@ -6,6 +6,8 @@ export { BlockScanner } from './scanner/block-scanner.js';
|
|
|
6
6
|
export type { BlockScannerConfig } from './scanner/block-scanner.js';
|
|
7
7
|
export { TransactionTracer } from './tracer/transaction-tracer.js';
|
|
8
8
|
export type { TransactionTracerConfig } from './tracer/transaction-tracer.js';
|
|
9
|
+
export { LogPoller } from './watcher/log-poller.js';
|
|
10
|
+
export type { FetchLogsResult } from './watcher/log-poller.js';
|
|
9
11
|
export { ExplorerClient } from './explorer/explorer-client.js';
|
|
10
12
|
export { RateLimiter } from './explorer/rate-limiter.js';
|
|
11
13
|
export { RpcClient } from './rpc/rpc-client.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,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
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
export { ContractWatcher } from './watcher/contract-watcher.js';
|
|
6
6
|
export { BlockScanner } from './scanner/block-scanner.js';
|
|
7
7
|
export { TransactionTracer } from './tracer/transaction-tracer.js';
|
|
8
|
+
export { LogPoller } from './watcher/log-poller.js';
|
|
8
9
|
// Explorer
|
|
9
10
|
export { ExplorerClient } from './explorer/explorer-client.js';
|
|
10
11
|
export { RateLimiter } from './explorer/rate-limiter.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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;
|
|
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;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,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"}
|