-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
103 lines (94 loc) · 3.47 KB
/
Copy pathindex.js
File metadata and controls
103 lines (94 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* Real-time EVM mempool watcher.
*
* Subscribes to `newPendingTransactions` over WebSocket. For each pending tx
* hash, fetches the full transaction body, applies your configured filter,
* and prints a structured event line.
*
* Run:
* CHAIN=base node src/index.js
*
* Customize filters in src/filters.js or the FILTER constant below.
*/
import { createPublicClient, webSocket, formatEther, formatGwei } from "viem";
import { getChain } from "./chains.js";
import {
allOf,
anyOf,
minValueWei,
methodSelector,
isEthTransfer,
methodSignature,
} from "./filters.js";
// ─── Configure your filter here. Default: ETH transfers >= 1 native unit
// OR token approvals
// OR DEX swaps. ──────────────────
const FILTER = anyOf(
allOf(isEthTransfer(), minValueWei(10n ** 18n)), // >= 1 native unit transferred
methodSelector("0x095ea7b3"), // approve()
methodSelector("0x7ff36ab5"), // swapExactETHForTokens
methodSelector("0x18cbafe5"), // swapExactTokensForETH
methodSelector("0x38ed1739"), // swapExactTokensForTokens
methodSelector("0x3593564c"), // Universal Router execute()
);
async function main() {
const chain = getChain();
const client = createPublicClient({ transport: webSocket(chain.wsUrl) });
console.log(`Mempool watcher`);
console.log(` Chain: ${chain.name} (${chain.slug})`);
console.log(` WebSocket: ${chain.wsUrl.replace(/key=[^&]+/, "key=***")}`);
console.log();
console.log(`Waiting for pending txs... Ctrl-C to stop.`);
console.log();
let total = 0;
let printed = 0;
let lastReport = Date.now();
client.watchPendingTransactions({
onTransactions: async (hashes) => {
total += hashes.length;
for (const hash of hashes) {
try {
const tx = await client.getTransaction({ hash });
if (!tx) continue;
if (!FILTER(tx)) continue;
const value = formatEther(tx.value ?? 0n);
const gasPrice = tx.maxFeePerGas
? formatGwei(tx.maxFeePerGas) + " gwei (maxFee)"
: tx.gasPrice
? formatGwei(tx.gasPrice) + " gwei"
: "-";
const method = methodSignature(tx);
const methodStr = method
? `${method.selector}${method.name ? ` (${method.name})` : ""}`
: "(eth transfer)";
printed++;
console.log(`${new Date().toISOString()}`);
console.log(` from: ${tx.from}`);
console.log(` to: ${tx.to ?? "(contract creation)"}`);
console.log(` value: ${value} ${chain.nativeSymbol}`);
console.log(` method: ${methodStr}`);
console.log(` gas: ${gasPrice}`);
console.log(` tx: ${chain.explorerTx}${hash}`);
console.log();
} catch {
/* tx may have already been mined or dropped — skip */
}
}
// Periodic stats line
const now = Date.now();
if (now - lastReport > 10_000) {
const elapsed = (now - lastReport) / 1000;
const rate = (total / elapsed).toFixed(1);
process.stderr.write(
`[stats] ${total} pending seen, ${printed} matched filter (${rate}/s)\n`
);
total = 0;
lastReport = now;
}
},
});
}
main().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});