Skip to content
D

Drain.me

SecOps Tooling - Approval Checker

Created on 19th February 2026

•

D

Drain.me

SecOps Tooling - Approval Checker

The problem Drain.me solves

The Problem It Solves
Every DeFi interaction starts with an approval — a signed transaction granting a smart contract permission to move your tokens. Most wallets request unlimited approvals by default, and these permissions never expire. Users forget about them. Contracts get upgraded. Keys get compromised. But the approvals remain, silently exposing billions in dormant risk.

This isn't hypothetical. In January 2026 alone, attackers stole $370 million in crypto — $311 million from phishing and social engineering. The SwapNet exploit drained $13.3 million from users who had granted persistent token approvals to its router contract. Signature phishing spiked 207%, stealing $6.27 million across 4,741 victims in a single month. In 2024, the Li.Fi protocol lost $9.7M, SenecaUSD lost $6.5M, and SocketDotTech lost $3.3M — all exploited through existing infinite approvals that users had forgotten about.

Chainalysis estimates approval phishing alone has stolen over $1 billion since 2021, driven by a small number of highly successful attackers — the top 10 addresses account for 15.9% of all stolen value.

The Gap:
Tools like revoke.cash let you revoke approvals one by one, but nothing shows you the full picture — who are these spenders? Are they verified contracts or raw wallets? Are they upgradeable proxies whose implementations could change at any time? How much total value is exposed, and to whom?

What drain.me does
drain.me is a risk analytics dashboard that scans Ethereum mainnet and answers the questions no one else is asking:

"How much money is at risk?" — Calculates min(allowance, balance) × price for every active approval to show real USD exposure, not theoretical maximums
"Who has access to my tokens?" — Classifies every approved spender as a verified contract, unverified contract, upgradeable proxy, or EOA (a raw wallet — the highest risk)
"Can I trust this proxy?" — Reads the EIP-1967 storage slot on-chain to resolve the actual current implementation address, not Etherscan's cached (potentially stale) version, then verifies the implementation's source code
"Which wallets are most exposed?" — Shows the top approvers per spender, filterable by token — if you select USDC, you see who has the most USDC at risk for each spender
"What's the contract's background?" — Surfaces language, compiler version, deploy block, creator address, factory deployment, and license for every spender

Who it's for

Individual users reviewing their own approval hygiene
Security researchers analyzing approval patterns across the network
Protocol teams auditing which contracts hold the most approval exposure
Anyone who wants to understand the silent risk sitting in their wallet right now

Challenges we ran into

Challenges We Ran Into

Approval values are lies

The biggest conceptual challenge was realizing that the value field in an ERC-20 Approval event doesn't tell you what's actually at risk. An approval for type(uint256).max (the "unlimited" approval that most DeFi protocols request) doesn't mean the user has unlimited tokens — it means the spender could take everything, but only up to the user's actual balance. And a legitimate transferFrom doesn't reduce the approval amount on-chain in most implementations; it just reduces the remaining allowance internally.

This means raw approval logs massively overstate risk. To get the real exposure, I had to calculate min(allowance, current_balance) × token_price for every approval — which meant fetching live balances for every owner-token pair and live prices for every token. That turned a simple log-scraping project into a multi-stage pipeline hitting Alchemy batch RPC, Chainlink oracles, and Uniswap V2 reserves.

Proxy implementations are a moving target

We assumed Etherscan's "getsourcecode" API would reliably tell us whether a proxy's implementation was verified. It doesn't. Etherscan caches the implementation address it first saw when the proxy was deployed — but for upgradeable proxies (which are everywhere in DeFi), the actual implementation can change at any time. The Aave staked token proxy (0x0fe58fe1caa69951dc924a8c222be19013b89476) was showing "impl_verified: false" because Etherscan was pointing to an old implementation address, while the current on-chain implementation at the EIP-1967 storage slot was a completely different, fully verified contract.

The fix was to read the implementation address directly from the blockchain via "eth_getStorageAt" on the EIP-1967 slot ("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"), then verify that address. This also caught proxies that Etherscan didn't even flag as proxies — contracts with a valid EIP-1967 implementation slot that Etherscan's heuristics had missed.

Etherscan rate limits fail silently

Etherscan's free API has a 5 calls/second rate limit, but it doesn't always return HTTP 429 when you exceed it. Instead, it often returns HTTP 200 with {"status": "0", "result": "Max rate limit reached"}. My initial code only checked for HTTP 429, so rate-limited responses were treated as "contract not verified" — silently marking verified contracts like the 1inch Aggregation Router as unverified with all metadata fields empty.

The fix was threefold: detect rate limits in the JSON response (not just HTTP status), add automatic retry with escalating backoff (up to 5 rounds with increasing cooldowns), and introduce a three-state verification model — "true", "false", or "UNKNOWN" — so that a failed fetch is never confused with a genuinely unverified contract.

Boolean type hell between Python and JavaScript

CSV files don't have a boolean type. Python's "csv.DictWriter" writes "True" and "False" as strings. Pandas reads them back as strings. The JSON builder then emitted "True" (a string) instead of true (a boolean). The dashboard's JavaScript compared with === true, which never matched a string — so every contract showed as unverified and no filters worked.

This one took an embarrassingly long time to find because the data looked correct when you opened the CSV or JSON in a text editor. The fix was adding explicit type conversion in the JSON builder to coerce string "True"/"False" back to native Python booleans before serialization.

Scope and scale

The current deployment tracks one day's worth of approvals as a proof of concept. Even at that scale, the pipeline processes thousands of unique spender contracts with multiple API calls each (source code, creation info, proxy resolution, implementation verification). Expanding to the full historical set of approvals on Ethereum mainnet — hundreds of millions of events — will require moving from Etherscan's free tier to a paid API key, batching more aggressively, and potentially storing intermediate results to avoid re-fetching on each run. That's the next step.

Use of AI tools and agents

Use of AI Tools and Agents

Claude was my coding partner throughout this entire project — not generating code blindly, but working through problems collaboratively the way you would with a senior engineer sitting next to you.

How I used it

Architecture and design decisions — I'd describe what I was trying to build (e.g., "I need to figure out the real USD risk of an ERC-20 approval") and we'd talk through the approach before writing any code. Claude helped me realize that raw approval values are meaningless without balances and prices, which shaped the entire multi-stage pipeline design.

Debugging real issues — The most valuable use was diagnosing bugs I couldn't see. When proxy implementations were showing as unverified despite being verified on Etherscan, I'd paste the API response and my code, and we'd trace through the logic together until we found that Etherscan caches stale implementation addresses for upgraded proxies. Same with the boolean serialization bug — I showed Claude my CSV output and dashboard behavior, and it identified the Python string "True" vs JavaScript boolean true mismatch that was breaking every filter.

Iterative refinement — The dashboard went through seven or eight iterations. I'd screenshot what it looked like, describe what felt wrong, and we'd adjust. The expanded row layout, the token-filter-aware approver tables, the three-state verification badges — all evolved through back-and-forth conversation, not a single prompt.

Research and validation — When I suspected Etherscan's API was silently rate-limiting me, Claude helped me test the actual API responses and confirm that Etherscan returns HTTP 200 with a JSON error body instead of HTTP 429. That led to the retry logic with escalating backoff.

What I didn't use it for

All of the on-chain domain knowledge — understanding EIP-1967 proxy storage slots, how ERC-20 approvals work, what makes an approval dangerous, and which contracts to be suspicious of — came from my own experience in the Ethereum ecosystem. Claude helped me implement and debug, but the security intuition driving the project is mine.

The tool

Claude Opus via claude.ai with computer use for file creation, code execution, and iterative development.

Tracks Applied (1)

Devtopia

How It Fits: Infrastructure → Security drain.me is a security analytics tool for the Ethereum approval layer — the perm...Read More

Discussion

Builders also viewed

See more projects on Devfolio