$CHEETAH is live!
Type something to search...
Blog

Wallet Drainers: Engineering Drain-Resistant Web3 dApps

Wallet drainers stole over $600M in H1 2025 through industrialized phishing operations. Here is how they work at the technical level, and what Web3 developers can do to engineer dApps that resist them.

Wallet Drainers: Engineering Drain-Resistant Web3 dAppsWallet Drainers: Engineering Drain-Resistant Web3 dApps
Wallet Drainers: Engineering Drain-Resistant Web3 dApps
Join Our Newsletter

Subscribe to our newsletter to get the latest updates and offers

* Will send you weekly updates on new features, tips, and developer resources.

TL;DR:

  • Wallet drainers generated over $135M through Drainer-as-a-Service operations on Ethereum alone, with phishing schemes spiking to $600M across Web3 in H1 2025
  • Drainers do not need a user's private key, they need one signed approval transaction, which makes them harder to detect and trivially easy to scale
  • The Rublevka Team operation generated over $10M through affiliate-driven campaigns using custom JavaScript deployed on spoofed landing pages, with support for over 90 wallet types
  • ERC-20 approve(), ERC-721 setApprovalForAll(), and EIP-2612 permit() signatures are the three primary vectors that drain-resistant dApps must address at both the contract and frontend layer
  • Transaction simulation before signing, strict approval scoping, and real-time on-chain monitoring are the three structural pillars of a drain-resistant dApp architecture
  • Drainer-as-a-Service platforms provide affiliates with Telegram bots, landing page generators, and evasion tooling, reducing the technical barrier to launching a drain campaign to near zero
  • Building drain-resistant dApps requires security to be embedded at the contract layer, the frontend layer, and the developer tooling layer simultaneously, not treated as a final audit step

The result: Wallet drainers are an industrialized threat, and the only effective response is an equally systematic approach to engineering dApps that treat every approval as a potential attack surface.

The Mechanics Nobody Fully Explains

Most developers who build Web3 dApps understand, at least conceptually, that wallet drainers are a serious threat. What fewer understand is the precise technical mechanism that makes them so effective, and why that mechanism is so difficult to defend against without deliberate architectural choices made early in the development process. A wallet drainer does not need your private key. It does not need to compromise your seed phrase or intercept your hardware wallet. It needs exactly one thing: a signed transaction that grants it permission to move your assets. That distinction is not semantic. It is the entire threat model, and it changes how you have to think about building secure dApps.

The core of how drainers work sits inside the ERC-20 token standard's approve() function and the ERC-721 standard's setApprovalForAll() function. Both of these functions exist for legitimate reasons. They allow protocols like Uniswap, OpenSea, and Aave to move tokens on a user's behalf after the user has explicitly authorized that action. The problem is that the authorization itself is just a transaction. It looks, from the outside, like any other interaction with a smart contract. A malicious contract can request the same authorization that a legitimate protocol would, and if the user signs it, the outcome is identical: the contract now has permission to transfer assets out of the wallet, on demand, at any point in the future.

What makes this particularly dangerous from a developer's perspective is that the attack surface is not primarily in the smart contract code. It is in the user interface layer, the transaction signing flow, and the way approval requests are presented to users. A perfectly audited smart contract can still be the vehicle for a drain if the frontend that wraps it is compromised, cloned, or replaced with a spoofed version. This is why building drain-resistant dApps requires thinking about security across the entire stack, not just the Solidity layer, and why the most dangerous assumption a Web3 developer can make is that a clean audit report means their users are safe.

The Drainer-as-a-Service Economy

The scale of the wallet drainer problem becomes clearer when you look at the infrastructure that has been built to support it. Research from Zhejiang University and Mohamed bin Zayed University of Artificial Intelligence, published in a systematic study of Drainer-as-a-Service on Ethereum, identified 1,910 profit-sharing contracts, 56 operator accounts, and 6,087 affiliate accounts involved in DaaS operations, with 87,077 profit-sharing transactions recorded. The total value extracted through these operations reached $135 million on Ethereum alone. These are not opportunistic attacks by individual bad actors. They are industrialized operations with business models, affiliate programs, revenue sharing agreements, and customer support channels.

The Rublevka Team, identified by Recorded Future's Insikt Group, is a concrete example of how this ecosystem operates in practice. Since its inception in 2023, the group generated over $10 million through affiliate-driven wallet draining campaigns. Their infrastructure is fully automated and scalable, offering affiliates access to Telegram bots, landing page generators, evasion features, and support for over 90 wallet types. The technical barrier to launching a draining campaign through their platform is near zero. An affiliate does not need to understand howEVM opcodes or Solidity internals to run a drain campaign. They need a Telegram account and a target audience.

This industrialization has a direct implication for dApp developers. The threat is not a sophisticated attacker spending weeks reverse-engineering your contract. The threat is a high-volume affiliate operation that clones your frontend, registers a domain that looks like yours with one character swapped, and drives traffic to it through Discord DMs, fake Twitter accounts, and SEO-poisoned search results. The malicious page looks identical to yours. It connects to the same wallet providers. It just routes the approval transaction to a different contract address. By the time a user realizes something is wrong, the drain has already executed.

The Three Approval Vectors That Matter Most

If you are building a Web3 dApp and you want to understand where your users are most exposed, the answer comes down to three specific mechanisms: ERC-20 approve(), ERC-721 setApprovalForAll(), and EIP-2612 permit() signatures. Each of these serves a legitimate purpose in the ecosystem, and each of them has been weaponized by drainer operations in ways that are worth understanding in detail.

The ERC-20 approve() function takes two arguments: a spender address and an amount. When a user calls it, they are telling the token contract that the specified address is allowed to transfer up to that amount of tokens from their wallet. The problem is that most dApps, for convenience, request approval for the maximum uint256 value, which is effectively unlimited. This means a single approval transaction, if directed at a malicious contract, grants that contract the ability to drain the entire token balance at any future point. The approval persists on-chain indefinitely. It does not expire. It does not require any further action from the user. The attacker can wait days, weeks, or months before executing the drain, which makes detection and response significantly harder.

ERC-721 setApprovalForAll() is even more aggressive in scope. Rather than approving a specific amount of a specific token, it grants an operator address permission to transfer any and all NFTs in a collection that the user holds. This function exists because NFT marketplaces need to be able to list and transfer tokens on behalf of sellers, but it is also the reason that a single signed transaction can empty an entire NFT wallet. The EIP-2612 permit() mechanism introduces a third vector that is particularly insidious because it does not require an on-chain transaction at all. Permit() allows a user to sign an off-chain message that authorizes a token transfer, and that signature can then be submitted by anyone to execute the approval. This means a drainer can collect permit signatures through a phishing page without the user ever seeing a transaction confirmation in their wallet, depending on how the wallet software handles off-chain signing requests.

Why Frontend Security Is the Real Battleground

Understanding the approval vectors is necessary but not sufficient. The more important insight for dApp developers is that the frontend is where most drain attacks actually happen, and it is the layer that receives the least security attention during development. Smart contract audits are now a standard part of the Web3 development lifecycle. Frontend security reviews are not. This asymmetry is exactly what drainer operations exploit.

There are several distinct ways a dApp frontend can become a drain vector. The most straightforward is a complete clone: an attacker copies your entire frontend, deploys it on a lookalike domain, and substitutes the contract addresses in the JavaScript bundle. Users who land on the cloned site interact with what appears to be your dApp but are actually submitting transactions to malicious contracts. A more sophisticated variant involves supply chain compromise, where a malicious package is introduced into your frontend's dependency tree. If a widely used npm package that your dApp depends on is compromised, every user of your dApp is exposed without any visible change to your site. The Rublevka Team's approach of deploying custom JavaScript scripts via spoofed landing pages is a direct example of how this plays out at scale.

A third vector is DNS hijacking or BGP route manipulation, which can redirect traffic from your legitimate domain to an attacker-controlled server. This is less common but has been used in high-profile attacks against DeFi protocols. The common thread across all of these vectors is that the attack does not require any vulnerability in your smart contract. It requires only that a user be presented with a transaction request that looks legitimate and sign it. This is why drain-resistant dApp architecture has to treat the frontend as a security boundary, not just a presentation layer, and why the developer tooling used to build and deploy that frontend matters as much as the Solidity code it wraps.

Engineering Strict Approval Scoping

The first concrete engineering decision that separates drain-resistant dApps from vulnerable ones is how they handle token approvals. The default pattern in most dApp codebases is to request maximum approval once and never revisit it. This is convenient for users because they only have to approve once, but it creates a permanent, unlimited authorization that persists long after the user has finished interacting with the protocol. A drain-resistant approach requires rethinking this pattern at the contract and frontend level simultaneously.

At the contract level, the most effective mitigation is to scope approvals to the exact amount required for the current transaction. Rather than requesting approve(spender, type(uint256).max), your contract interaction should request approve(spender, exactAmount) where exactAmount is the precise value needed for the operation the user is about to perform. This limits the blast radius of a compromised approval to a single transaction's worth of tokens rather than the user's entire balance. Some protocols implement this through a pattern where the approval and the operation are bundled into a single transaction using a router contract, so the approval is consumed immediately and no residual authorization remains on-chain after the transaction completes.

At the frontend level, drain-resistant dApps should display the exact approval amount in human-readable form before the user signs, and should include a clear explanation of what the approval authorizes. This sounds obvious, but the majority of dApp frontends present approval requests with minimal context, relying on the wallet software to surface the details. Wallet software varies significantly in how it presents this information, and users who have been conditioned to click through approval dialogs quickly are exactly the population that drainer operations target. Building explicit approval context into your frontend UI is a low-cost intervention that meaningfully reduces the likelihood that a user will sign a malicious approval without noticing.

Transaction Simulation as a First-Class Defense

One of the most powerful tools available to dApp developers for drain resistance is transaction simulation, and it is still underused in production dApps. Transaction simulation means executing a transaction against a forked or simulated version of the current chain state before presenting it to the user for signing, and then displaying the predicted outcome in terms the user can understand. Instead of showing a raw hex calldata string, you show the user: "This transaction will transfer 500 USDC from your wallet to contract 0x1234." If the predicted outcome does not match what the user expects, they have the information they need to reject the transaction before it is signed.

Several tools exist for implementing transaction simulation in a dApp frontend. Tenderly's simulation API allows you to fork mainnet state and execute transactions against it, returning a detailed trace of what would happen including token transfers, approval changes, and state mutations. Alchemy's Transact product offers similar capabilities. At the contract level, you can implement simulation hooks that return expected outcomes before committing state changes, following patterns similar to ERC-4337's validation phase. The key engineering decision is where in the transaction flow you insert the simulation step. It needs to happen after the user has initiated an action but before the wallet signing dialog appears, so that the user sees the predicted outcome as part of the normal interaction flow rather than as an optional advanced feature.

The practical challenge with transaction simulation is latency. Forking mainnet state and executing a simulation adds time to the transaction flow, and users who are accustomed to fast interactions may find the delay frustrating. The engineering tradeoff is real, but it is worth making. A 500-millisecond delay before a signing dialog is a minor inconvenience. A drained wallet is not. For high-value operations, particularly those involving large token amounts or NFT collections, the simulation step should be non-negotiable regardless of the latency cost.

Monitoring, Revocation, and Incident Response

Building drain-resistant dApps does not end at deployment. The on-chain state of your users' approvals is a live attack surface that changes over time, and a drain-resistant architecture needs to include monitoring and response capabilities that operate continuously after launch. This means instrumenting your dApp to track approval events emitted by the token contracts your users interact with, and surfacing that information in a way that allows users to understand and manage their current authorization state.

The Approval and ApprovalForAll events emitted by ERC-20 and ERC-721 contracts respectively are the primary data sources for approval monitoring. Indexing these events for your users' wallet addresses and presenting the current approval state in your dApp's UI gives users visibility into what they have authorized and makes it easy to revoke approvals that are no longer needed. Tools like Revoke.cash have built entire products around this use case, which is a signal that the demand for approval management is real and that users are not getting this visibility from the dApps they use. Building it natively into your dApp is both a security improvement and a trust signal.

For incident response, the key capability is the ability to detect anomalous approval patterns in real time and alert users before a drain executes. If your dApp's monitoring infrastructure detects that a large number of users have suddenly granted approval to an unfamiliar contract address, that is a strong signal that a phishing campaign is active and targeting your user base. Having a response playbook that includes user notification, social media alerts, and coordination with wallet providers to flag the malicious contract address can meaningfully reduce the total damage from a drain campaign even after it has started. BlockSec's Phalcon platform and similar on-chain monitoring tools provide the infrastructure for this kind of real-time detection, and integrating them into your dApp's operational stack is a reasonable investment for any protocol with meaningful TVL.

The Permit() Problem and How to Address It

EIP-2612's permit() mechanism deserves its own treatment because it represents a qualitatively different kind of risk compared to standard on-chain approvals. The core issue is that permit() signatures are off-chain messages, which means they do not appear as transactions in a user's wallet history and do not require gas to create. A phishing page can collect a permit() signature from a user under the guise of a "free" interaction, a wallet connection, or a signature verification step, and then submit that signature on-chain at any later point to execute the approval and subsequent drain.

From a developer perspective, the mitigation for permit() risks involves several layers. First, if your protocol does not need to support permit(), do not implement it. The convenience benefit of gasless approvals is real, but so is the additional attack surface. If you do implement permit(), ensure that your frontend clearly distinguishes between transaction signing requests and message signing requests, and provides explicit context about what a permit() signature authorizes. Many wallet interfaces present message signing requests with less prominent warnings than transaction requests, which is exactly the gap that drainer operations exploit.

At the contract level, you can implement permit() with tight deadline parameters, requiring that the signature be used within a short window of time after it is created. This does not eliminate the risk but limits the window during which a stolen signature can be exploited. Some protocols also implement domain separation carefully to ensure that permit() signatures created for one contract cannot be replayed against another, which is a basic requirement of the EIP-2612 specification but one that is sometimes implemented incorrectly in practice. Reviewing your permit() implementation against the specification, and testing it with tools like Foundry's fuzzing capabilities, should be a standard part of your pre-deployment checklist.

Subresource Integrity and Frontend Hardening

The supply chain attack vector against dApp frontends is one of the most underappreciated risks in the ecosystem, and it is one that developers have concrete tools to address. Subresource Integrity, or SRI, is a browser security feature that allows you to specify a cryptographic hash for external scripts and stylesheets. If the content of a loaded resource does not match the specified hash, the browser refuses to execute it. For dApps that load any external JavaScript, implementing SRI for those resources is a straightforward mitigation against the scenario where a CDN or third-party script host is compromised and begins serving malicious code.

Content Security Policy headers are a complementary control that restricts which origins your dApp's pages are allowed to load scripts, styles, and other resources from. A well-configured CSP that whitelists only the specific origins your dApp legitimately uses will block a large class of injection attacks, including the scenario where an attacker manages to inject a script tag into your page through a compromised dependency or a server-side vulnerability. Implementing CSP for a complex dApp frontend requires careful testing because overly restrictive policies can break legitimate functionality, but the security benefit justifies the engineering investment.

Beyond SRI and CSP, drain-resistant dApp development requires treating your build pipeline as a security boundary. This means pinning dependency versions in your package.json rather than using range specifiers, auditing your dependency tree regularly with tools like npm audit or Snyk, and reviewing the permissions and network access patterns of the packages you depend on. The attack surface of a modern JavaScript frontend is large, and the npm ecosystem has a documented history of malicious package incidents. Building a dApp that is resistant to supply chain compromise requires the same discipline around dependency management that you would apply to any production software system handling financial assets.

What AI-Assisted Development Changes About This Problem

The emergence of AI-assisted development tools introduces a new dimension to the drain resistance problem that is worth addressing directly. AI code generation tools can produce Solidity and JavaScript code quickly, but they are not inherently security-aware. Research has shown that developers using AI code generation are more likely to introduce security vulnerabilities, and the specific patterns that wallet drainers exploit, including unlimited approvals, missing input validation on approval parameters, and insecure permit() implementations, are exactly the kind of subtle issues that AI-generated code can introduce without obvious signals.

This does not mean AI-assisted development is incompatible with building secure dApps. It means that the tooling layer needs to be security-aware in ways that generic code generation tools are not. An AI development environment that understands Web3 security patterns can flag an approve(spender, type(uint256).max) call and suggest a scoped alternative. It can recognize a permit() implementation that is missing deadline validation and surface that as a warning before the code reaches a testnet. It can analyze a frontend component that handles wallet connections and identify missing transaction simulation steps. The difference between AI-assisted development that increases security risk and AI-assisted development that reduces it is whether the tooling has been built with the specific threat model of Web3 applications in mind.

Building the Habit of Drain-Resistant Thinking

The technical controls described in this article, scoped approvals, transaction simulation, permit() hardening, SRI, CSP, dependency auditing, and on-chain monitoring, are individually well-understood. The harder problem is building a development culture and workflow where these controls are applied consistently, from the first line of code through deployment and ongoing operations. Most drain incidents are not the result of developers being unaware that drainers exist. They are the result of security considerations being deferred, deprioritized, or treated as someone else's responsibility in a development process that is moving fast.

The practical answer to this is to make drain-resistant patterns the path of least resistance in your development workflow. If your project scaffolding generates approval handling code that is scoped by default, developers will write scoped approvals without having to think about it. If your CI pipeline runs static analysis that flags unlimited approval patterns as errors rather than warnings, those patterns will not reach production. If your development environment surfaces security context alongside code suggestions, the security decision is made at the moment the code is written rather than discovered in an audit six weeks later. This is the architectural argument for purpose-built Web3 development tooling over generic tools adapted for blockchain development.

Where Cheetah AI Fits Into This Picture

If you are building a Web3 dApp and you are thinking seriously about drain resistance, the tooling you use during development is not a neutral choice. Cheetah AI is built specifically for the Web3 development context, which means it understands the threat model described in this article at the level of the code you are writing. When you are working on approval handling logic, transaction signing flows, or permit() implementations, Cheetah AI can surface the relevant security patterns and flag the specific anti-patterns that drainer operations exploit, not as a post-hoc audit but as part of the development flow itself.

The goal is not to replace security audits or manual review. Those remain essential, particularly for protocols with significant TVL. The goal is to close the gap between when a vulnerability is introduced and when it is caught, and to make drain-resistant patterns the default rather than the exception. The $600 million lost to phishing and drain operations in H1 2025 is not primarily a story about sophisticated attackers. It is a story about preventable vulnerabilities that made it to production because the development process did not catch them early enough. Building with tooling that treats Web3 security as a first-class concern is one of the most direct ways to change that outcome.

Related Posts

Reasoning Agents: Rewriting Smart Contract Development

Reasoning Agents: Rewriting Smart Contract Development

TL;DR:Codex CLI operates as a multi-surface coding agent with OS-level sandboxing, 1M context windows via GPT-5.4, and the ability to read, patch, and execute against live codebases, making it

user
Cheetah AI Team
09 Mar, 2026
Web3 Game Economies: AI Dev Tools That Scale

Web3 Game Economies: AI Dev Tools That Scale

TL;DR:On-chain gaming attracted significant capital throughout 2025, with the Blockchain Game Alliance's State of the Industry Report confirming a decisive shift from speculative token launche

user
Cheetah AI Team
09 Mar, 2026
Token Unlock Engineering: Build Safer Vesting Contracts

Token Unlock Engineering: Build Safer Vesting Contracts

TL;DR:Vesting contracts control token release schedules for teams, investors, and ecosystems, often managing hundreds of millions in locked supply across multi-year unlock windows Time-lock

user
Cheetah AI Team
09 Mar, 2026