ETH Watchtower is a real-time Ethereum event monitoring tool written in Go. It connects to an Ethereum RPC node via WebSocket to detect and analyze various on-chain activities, including contract deployments, token mints, liquidity creation, and DEX trades.
When running against an RPC, ETH Watchtower performs deep inspection in two distinct phases:
As blocks arrive, the engine analyzes transaction logs to detect:
- Contract Deployments: Instantly captures new bytecode for analysis.
- Token Mints: Detects
Transferevents from the zero address, flagging potential infinite mint exploits or hidden premints. - Whale Movements: Alerts on value transfers exceeding configured thresholds.
- Suspicious Approvals: Identifies infinite approvals or approvals to known malicious entities.
- DEX Activity: Monitors liquidity events and swaps on Uniswap-compatible protocols.
- Flash Loans: Detects large capital movements via flash loan callbacks.
- Ownership Changes: Tracks ownership renouncements (often fake) or transfers.
Every new contract bytecode is disassembled and scanned against a library of heuristic patterns (detailed in the sections below) to identify vulnerabilities, honeypots, and malicious logic.
- Contract Discovery: Detects new smart contract deployments and identifies token standards (ERC20, ERC721, ERC1155).
- Mint Detection: Monitors
Transferevents to detect token minting activities (transfers from the zero address). - DEX Monitoring: Watches for liquidity pool creation and token swaps on configured DEXes (e.g., Uniswap V2).
- Whale Watch: Flags ERC20 transfers that exceed a configured value threshold.
- Large Approval: Flags ERC20 approvals that exceed a configured value threshold or are infinite.
- Static Analysis: Scans bytecode for risk factors like
SelfDestruct,HiddenMint,WriteToSlotZero,ReturnBomb,ERC777Reentrancy,DelegateCallToZero,CostlyLoop,ProxyDestruction,MetamorphicExploit,HardcodedSelfDestruct,UnsafeDelegateCall,UncheckedMath,UncheckedCall,UncheckedSend, andUncheckedLowLevelCall. - Metrics: Exposes Prometheus metrics for monitoring the watcher's health and detected events.
- Resilience: Includes a watchdog to detect stalled RPC connections, failover support for multiple RPC endpoints, and a circuit breaker to temporarily avoid failing nodes.
- Graceful Shutdown: Handles OS signals (
SIGINT,SIGTERM) for clean termination.
Clone the repository and build the executable:
git clone https://github.com/rnts08/eth-watchtower.git
cd eth-watchtower
make buildStart the watcher by providing the path to your configuration file:
./eth-watchtower -config config.json-config: Path to the configuration JSON file (default:config.json).-metrics: Address to serve Prometheus metrics (default::2112).
The config.json file supports the following sections:
| Section | Field | Type | Default | Description |
|---|---|---|---|---|
rpc |
url |
string | — | RPC endpoint URL (WebSocket or HTTPS); rpc is an array of endpoints for failover |
rpc |
apiKey |
string | "" |
Optional API key appended as query parameter |
output |
string | "eth-watchtower.jsonl" |
Path to the JSON Lines output file | |
log |
string | "eth-watchtower.log" |
Path to the log file | |
db_path |
string | "eth-watch.db" |
Path to the BoltDB persistence database | |
whale_threshold |
string | — | Minimum transfer value to flag as whale (in wei) | |
concurrency |
int | 20 |
Max concurrent RPC calls and analysis jobs | |
analyzer_pool_size |
int | 20 |
Analyzer goroutine pool size (defaults to concurrency) |
|
events |
transfers |
bool | true |
Monitor ERC20 Transfer events (mint + whale detection) |
events |
liquidity |
bool | true |
Monitor DEX PairCreated events |
events |
trades |
bool | true |
Monitor DEX Swap events |
events |
flashloans |
bool | true |
Monitor flash loan events |
events |
approvals |
bool | true |
Monitor ERC20 Approval events |
events |
ownership_transfers |
bool | true |
Monitor OwnershipTransferred events |
heuristics.max_rpc_failures |
int | 3 |
Consecutive failures before circuit-breaker trips | |
heuristics.rpc_trip_duration |
duration | "5m" |
Circuit-breaker cooldown period | |
heuristics.rpc_watchdog_interval |
duration | "10s" |
Interval between RPC health checks | |
heuristics.rpc_stalled_threshold |
duration | "60s" |
Time without new blocks before triggering stall alert | |
heuristics.max_code_cache_size |
int | 1000 |
Max cached bytecode entries to avoid re-analysis | |
heuristics.high_frequency_threshold |
int | 3 |
Deployments within window to trigger HighFrequencyDeployer |
|
heuristics.high_frequency_score |
int | 50 |
Risk score contribution for high-frequency deployment | |
heuristics.high_frequency_window |
duration | "5m" |
Time window for high-frequency deployer tracking | |
heuristics.new_contract_base_score |
int | 10 |
Baseline risk score for any new contract | |
heuristics.max_risk_score |
int | 999 |
Capped maximum risk score | |
heuristics.flash_mint_score |
int | 60 |
Risk score for flash-mint detection (mint + flash loan in same tx) | |
heuristics.rug_pull_window |
duration | "24h" |
Time window for LP token burn tracking (rug pull) | |
heuristics.snipe_window_blocks |
int | 2 |
Block window after liquidity creation for snipe detection | |
heuristics.dust_threshold |
int | 100 |
Max token value (in wei) to consider a transfer as dust | |
heuristics.dust_recipient_soft |
int | 100 |
Number of unique dust recipients before flagging DustDistribution | |
heuristics.heuristic_scores |
map | — | Per-heuristic score overrides (keys are static analysis heuristic names) | |
heuristics.event_scores |
map | — | Per-event score overrides (keys: MintDetected, WhaleTransfer, ApprovalDetected, FlashLoanDetected, MintToDeployer, EarlyBuyDetected, DustDistribution, MultipleMints, RugPullDetected, LiquidityCreated, TradingDetected, FlashMintDetected, InfiniteApproval, LargeApproval, OwnershipTransferred, OwnershipRenounced) | |
heuristics.enable |
[]string | — | Explicitly enable only these heuristics | |
heuristics.disable |
[]string | — | Disable specific heuristics | |
dexes |
name |
string | — | DEX name (e.g., UniswapV2); section is an array of DEX configs |
dexes |
pairCreatedTopic |
string | — | Topic hash for PairCreated event |
dexes |
swapTopic |
string | — | Topic hash for Swap event |
contracts |
address |
string | — | Contract address to watch; section is an array of contract configs |
contracts |
name |
string | "" |
Human-readable label for the contract |
contracts |
type |
string | "" |
Contract type hint (e.g., ERC20, ERC721) |
contracts |
risk_weight |
float | 0 |
(deprecated — use score_multiplier) |
contracts |
whale_threshold |
string | "" |
Per-contract whale threshold override (in wei) |
contracts |
enabled |
bool | true |
Enable/disable monitoring for this contract |
contracts |
score_multiplier |
float | 1.0 |
Multiplier applied to all risk scores for this contract |
contracts |
event_overrides |
map | — | Per-event enable/disable overrides (keys are event flag names) |
contracts |
max_risk_score |
int | 999 |
Per-contract cap on maximum risk score |
You can also run ETH Watchtower using Docker.
docker build -t eth-watchtower .Mount your config.json into the container:
docker run -d \
-v $(pwd)/config.json:/app/config.json \
-p 2112:2112 \
--name eth-watchtower \
eth-watchtowerThe project includes unit tests for event handling logic, token type detection, and concurrency safety.
To run the tests:
go test -v .To run tests with the Go race detector enabled (recommended to verify thread safety):
go test -race -v .Detected events are written to the configured output file (e.g., eth-watch-events.jsonl) in JSON Lines format.
Example:
{"contract":"0x...","deployer":"0x...","block":123456,"tokenType":"ERC20","mintDetected":true,"riskScore":55,"flags":["MintDetected"],"txHash":"0x..."}Prometheus metrics are exposed at http://localhost:2112/metrics (or the configured address).
Key metrics include:
eth_watcher_contracts_discovered_total: Total number of new contracts discovered.eth_watcher_mints_detected_total: Total number of mints detected.eth_watcher_trades_detected_total: Total number of trades detected.eth_watcher_flashloans_detected_total: Total number of flashloans detected.eth_watcher_rpc_latency_seconds: RPC connection latency.eth_watcher_active_subscriptions: Current number of active WebSocket subscriptions.eth_watcher_code_analysis_flags_total: Total number of times a specific code analysis flag has been detected.
A Grafana dashboard configuration is provided in grafana_dashboard.json. You can import this JSON file into your Grafana instance to visualize the metrics exported by ETH Watchtower.
ETH/ERC20: 0x968cC7D93c388614f620Ef812C5fdfe64029B92d
SOL: HB2o6q6vsW5796U5y7NxNqA7vYZW1vuQjpAHDo7FAMG8
BTC: bc1qkmzc6d49fl0edyeynezwlrfqv486nmk6p5pmta
- ReadOnlyReentrancy: Detects external calls followed by state reads (read-only reentrancy risk).
- ArbitraryStorageWrite: Detects storage writes where the slot is derived from calldata.
- UninitializedPointer: Detects writes to storage slot 0 via uninitialized pointers.
- UncheckedEcrecover: Detects
ecrecoverreturn value not checked against zero. - MissingZeroCheck: Detects missing zero-address validation in transfers.
- SignatureReplay: Detects signature usage without nonces.
- WriteToSlotZero: Detects writing to storage slot 0, often a proxy implementation bug or uninitialized pointer.
- TokenDraining: Detects calls where the token address is user-controlled.
- TxOriginPhishing: Detects
tx.originusage immediately followed by a call, a common phishing pattern. - BurstMint: Detects token minting or transfer logic occurring within a loop structure.
- SelfAllocation: Detects contracts assigning state or ownership to the caller during initialization without checking existing state.
- ArbitraryJump: Detects jumps to destinations derived from calldata.
- FrontRunning: Detects transaction order dependency patterns (e.g., hash solution verification).
- TransferTopicWithoutLogs: Detects the
Transferevent topic in bytecode without correspondingLOGopcodes. - SignatureMalleability: Detects
ecrecoverusage without strict s-value checks (EIP-2). - UninitializedConstructor: Detects owner-setting logic that can be re-called.
- GasTokenMinting: Detects patterns associated with minting gas tokens via
SELFDESTRUCTrefunds. - IntegerTruncation: Detects masking of calldata inputs that could lead to truncation.
- UninitializedLocalVariables: Detects usage of memory variables before they are written to, a common bug with storage pointers in memory.
- UninitializedState: Detects storage reads from slots that haven't been written to, implying uninitialized state usage.
- PublicBurn: Detects unprotected
burnfunctions that can be called by anyone. - UnprotectedUpgrade: Detects unprotected proxy
upgradeTofunctions. - AssemblyErrorProne: Detects patterns prone to errors in inline assembly, like misusing storage pointers for memory operations.
- ReinitializableProxy: Detects proxies with an
initializefunction that can be called multiple times. - MisleadingFunctionName: Detects emission of
Transferevents from functions that lack standard ERC20 selectors. - UnrestrictedDelegateCall: Detects
delegatecallwhere the target address is not validated. - StrictBalanceEquality: Detects strict equality checks on
address(this).balance. - DivideBeforeMultiply: Detects division before multiplication causing precision loss.
- UncheckedReturnData: Detects low-level calls where return data is ignored.
- HardcodedGasLimit: Detects calls with hardcoded gas amounts.
- LockedEther: Detects contracts that can receive ETH but have no way to withdraw it.
- ShadowingState: Detects state reads that are immediately popped (useless reads).
- UncheckedMath: Detects arithmetic operations without overflow checks (pre-0.8.0).
- ReentrancyNoGasLimit: Detects calls that forward all gas, increasing reentrancy risk.
- UnprotectedEtherWithdrawal: Detects withdrawal functions that do not check state (e.g. ownership or balance).
- UncheckedTransfer: Detects ERC20 transfer calls where the return value is ignored.
- UncheckedTransferFrom: Detects ERC20
transferFromcalls where the return value is ignored. - UncheckedCall: Detects low-level calls where the return value is ignored.
- UncheckedSend: Detects
sendcalls (gas=2300) where the return value is ignored. - UncheckedLowLevelCall: Detects
callwith custom gas where the return value is ignored. - UncheckedCreate: Detects contract creation where the result address is ignored.
- MissingReturn: Detects contracts that appear to be tokens but lack a RETURN opcode.
- UncheckedDelegateCall: Detects
delegatecallwhere the return value is ignored. - ReinitializableProxy: Detects proxies with an
initializefunction that can be called multiple times. - SelfDestruct: Detects usage of the
SELFDESTRUCTopcode. - ReentrancyGuard: Detects usage of reentrancy guards (e.g., OpenZeppelin).
- ERC777Reentrancy: Detects usage of the ERC1820 registry, often associated with ERC777 reentrancy vectors.
- FakeToken: Detects contracts mimicking ERC20 signatures but lacking storage logic.
- StrawManContract: Detects "cash out" patterns that are actually traps (e.g., hidden reverts, delegatecalls).
- GasGriefingLoop: Detects loops designed to consume gas.
- HardcodedSelfDestruct: Detects
SELFDESTRUCTwith a hardcoded beneficiary address. - HiddenFee: Detects transfers where the amount is reduced by a constant value.
- FakeHighBalance: Detects
balanceOfreturning hardcoded large values. - FakeTransferEvent: Detects
Transferevents without storage updates. - PhantomFunction: Detects do-nothing functions that trap funds.
- OwnerTransferCheck: Detects transfer functions restricted to the owner.
- TradingCooldown: Detects time-lock or cooldown mechanisms on transfers.
- TaxToken: Detects transfer logic involving division, indicative of transfer taxes.
- HiddenApproval: Detects contracts with an
approveselector but notransferlogic. - PotentialHoneypot: Detects transfer functions that write to state but don't emit Transfer events.
- SuspiciousStateChange: Detects state writes without prior reads (blind overwrites).
- ZeroAddressTransfer: Detects Transfer events to the zero address (burns) that are not from standard burn functions.
- FakeReturn: Detects a specific fake return pattern used to deceive callers.
- NoTransferEvent: Detects transfer functions that do not emit events.
- HardcodedBlacklistedAddress: Detects references to known malicious addresses (e.g., Tornado Cash router).
- HiddenMint: Detects minting logic hidden within transfer functions.
- ReturnBomb: Detects contracts that revert with large data or in a way to grief callers.
- GasGriefing: Detects usage of
INVALIDopcode or other gas-wasting patterns.
- NonStandardProxy: Detects proxies that do not follow EIP-1967.
- MinimalProxy: Detects EIP-1167 minimal proxy clones.
- ProxySelectorClash: Detects proxies with potential selector clashes between proxy and implementation.
- SuspiciousDelegate: Detects delegatecalls to hardcoded addresses.
- DelegateCallToSelf: Detects
delegatecalltoaddress(this), a pattern often used in metamorphic contracts. - Metamorphic: Detects usage of
CREATE2(base detection). - ProxyDestruction: Detects
delegatecallcombined withselfdestruct(proxy destruction risk). - MetamorphicExploit: Detects
CREATE2combined withselfdestruct(metamorphic exploit risk). - UnsafeDelegateCall: Detects
delegatecallusing calldata, allowing arbitrary code execution. - DelegateCallToZero: Detects
delegatecallto the zero address. - DelegateCall: Detects usage of
delegatecall(base detection).
- DoSGasLimit: Detects loops bounded by dynamic data (DoS vector).
- DeadCode: Detects unreachable code.
- InfiniteLoop: Detects unconditional backward jumps.
- CallInLoop: Detects calls executed inside loops.
- LoopDetected: Detects any backward jump (base loop detection).
- DelegateCallInLoop: Detects delegatecalls executed inside loops.
- FactoryInLoop: Detects contract creation inside loops.
- SelfDestructInLoop: Detects self-destructs inside loops.
- GasDependentLoop: Detects loops with gas operations.
- CostlyLoop: Detects storage writes (
SSTORE) inside loops.
- TimestampDependence: Detects logic conditional on
block.timestamp. - BadRandomness: Detects usage of
blockhashfor randomness. - WeakRandomness: Detects usage of
difficultyorprevrandao. - BlockStuffing: Detects usage of
gaslimit. - AntiContractCheck: Detects checks on
extcodesize(often used to block smart contract interactions). - CodeHashCheck: Detects checks on
extcodehash. - TxOrigin: Detects usage of
tx.originfor authorization. - BlockTimestampManipulation: Detects usage of
block.timestampin comparison operations. - GasPriceCheck: Detects logic dependent on
tx.gasprice. - CoinbaseCheck: Detects logic dependent on
block.coinbase. - BlockNumberCheck: Detects logic dependent on
block.number. - ChainIDCheck: Detects logic dependent on
chainid. - CheckOwnBalance: Detects logic checking
address(this).balance. - GasUsage: Detects usage of the
GASopcode.
- PrivilegedSelfDestruct: Detects self-destructs protected by access control.
- UnprotectedSelfDestruct: Detects self-destructs reachable without authorization checks.
- Mintable: Detects minting function selectors.
- Burnable: Detects burning function selectors.
- Ownable: Detects ownership management selectors.
- Blacklist: Detects blacklist function selectors.
- FlashLoanReceiver: Detects implementation of flash loan callback interfaces (e.g.,
onFlashLoan). - Upgradable: Detects upgradeable proxy selectors.
- InterfaceCheck: Detects ERC165 interface checks.
- FlashLoan: Detects flash loan function selectors.
- Withdrawal: Detects withdrawal function selectors.
- RenounceOwnership: Detects ownership renouncement.
- Stateless: Detects contracts with no storage operations (often logic contracts or scams).
- SuspiciousCodeSize: Detects code size checks on itself.
- IncorrectConstructor: Detects potential constructor naming errors (Solidity <0.4.22).
- LowLevelCall: Detects usage of low-level
call. - ContractFactory: Detects contract creation (
create). - CalldataSizeCheck: Detects checks on
calldatasize.
-
ETH/ERC20:
0x968cC7D93c388614f620Ef812C5fdfe64029B92d -
SOL:
HB2o6q6vsW5796U5y7NxNqA7vYZW1vuQjpAHDo7FAMG8 -
BTC:
bc1qkmzc6d49fl0edyeynezwlrfqv486nmk6p5pmta -
LTC:
ltc1q0ahxru7nwgey64agffr7x89swekj7sz8stqc6x -
XRP:
rUW7Q64vR4PwDM3F27etd6ipxK8MtuxsFsBuy me a coffee at https://buymeacoffee.com/timhbergsta, support me via Ko-Fi at https://ko-fi.com/rnts08, or send donations to
timh@tbtechvn.comvia PayPal directly.