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).
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.
Buy the developers a beer (https://buymeacoffee.com/timhbergsta)