The Anatomy of transferFrom Attacks and How to Stop Them
Understanding the transferFrom Mechanism
In the ERC‑20 specification the transferFrom function allows a contract or an externally owned account (EOA) to move tokens from another address.
The function signature is
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
The allowance system that precedes transferFrom lets an owner grant a spender a fixed amount of tokens that the spender may move on behalf of the owner, a pattern that underpins many ERC20 approval vulnerabilities for developers. The spender typically calls transferFrom until the allowance is exhausted. Because transferFrom is a public function, any account that has been granted an allowance can call it, making it a powerful but also potentially dangerous tool.
The usual flow for a legitimate user is:
- Owner calls
approve(spender, amount)on the token contract. - Spender, such as a DeFi protocol, calls
transferFrom(owner, recipient, amount)repeatedly until the allowance reaches zero.
When this flow is not carefully guarded, a malicious actor can abuse the same pattern to siphon tokens, execute reentrancy attacks, or manipulate state in ways that benefit the attacker, as detailed in How to Protect Your DeFi Funds from transferFrom Attacks.
How Attackers Exploit transferFrom
1. The Unlimited Approve Attack
Many projects, in an attempt to simplify user experience, grant an unlimited allowance to a protocol, a tactic highlighted in Unpacking DeFi Risks: The Perilous transferFrom Feature.
An example pattern:
token.approve(protocol, type(uint256).max);
A malicious contract can then repeatedly call transferFrom to drain any balance the owner has at any time, even after the owner has removed funds from the protocol. Because the allowance never reaches zero, the attacker can keep draining new deposits or re‑depositing stolen tokens back into the protocol for further exploitation.
Why it Works
- The
transferFromfunction checks only that the allowance is greater than or equal to the amount being transferred. - It does not require that the allowance be decremented by the exact amount each time, nor does it enforce any time‑based restrictions.
- Once a contract holds an unlimited allowance, it can call
transferFromwhenever it wishes, as long as the token balance of the owner is positive.
2. The Race Condition (Approve–Transfer Race)
The ERC‑20 approve function allows an owner to set a new allowance. If a user calls approve(newSpender, newAmount) while a pending transaction is still using the old allowance, the old transaction may still succeed, causing an unintended transfer. Attackers can deliberately trigger this race condition by submitting a new approve request just before the old transaction executes.
The classic scenario:
- Owner approves a contract to spend 100 tokens.
- Owner submits a transaction that calls
transferFromfor 100 tokens. - Before the
transferFromtransaction is mined, the owner callsapprove(spender, 0)orapprove(spender, newAmount)on a different contract. - The original
transferFromtransaction executes and still transfers 100 tokens, because the allowance check occurs after the transaction is executed, not at the time of approval.
This race condition can be exploited when a user is unaware that the approval is not atomic with respect to the transfer.
3. Front‑Running and Manipulation of Allowances
Front‑running is a common threat in blockchain systems. Attackers can monitor pending transferFrom calls and submit their own transaction with a higher priority (by increasing gas price) to alter state before the original transaction executes. If the attacker can manipulate the allowance (e.g., by calling approve themselves or by using a flash loan to temporarily increase the allowance), they can cause the original transferFrom to fail or to transfer a larger amount than intended.
An illustrative front‑running attack:
- User calls
approve(protocol, 500)and then submits atransferFromfor 500 tokens. - Attacker sees the pending transaction, calls
approve(protocol, 1e30)(a huge allowance) using a flash loan. - The attacker’s transaction is mined first, giving the protocol an enormous allowance.
- When the original
transferFromexecutes, the protocol can now transfer more tokens than the user intended, or the attacker can use the huge allowance for their own profit.
4. Reentrancy via transferFrom
When a contract uses transferFrom to pull tokens into itself, it often calls an external function after the transfer, or updates state that can be re‑entered by a malicious token that has a fallback function. If the contract does not guard against reentrancy, the malicious token can call transferFrom again before the first call finishes, effectively draining the contract’s balance. The classic “pull over push” pattern mitigates this, but many contracts still use the “push” pattern, which is more vulnerable.
5. Approval to a Malicious Contract
An attacker can create a contract that pretends to be a legitimate DeFi protocol. They lure users into approving the contract, often by offering high yield or free gas. Once the user has approved, the attacker can call transferFrom at will. Because the user’s allowance is granted to the attacker’s address, the user has no control over the subsequent transfers. The attacker can then move the tokens to an address that they control, sometimes after re‑depositing stolen tokens to hide the movement.
Real‑World Examples
- The DODO “Zero” Attack – A DeFi protocol accidentally allowed an attacker to call
transferFromfor any amount because the protocol never set an allowance to zero after withdrawing liquidity. - Uniswap V2 “Approve All” Vulnerability – Users who approved an unlimited allowance to the Uniswap V2 router could be drained by any contract that could call
transferFrom. - Sushiswap Flash Loan Exploit – An attacker used a flash loan to call
approveon behalf of a user, granting themselves an unlimited allowance and then usingtransferFromto siphon funds.
Defensive Strategies
1. Use the Pull Over Push Pattern
Instead of a contract pulling tokens with transferFrom, have the user push tokens to the contract, and let the contract record the intent – a strategy outlined in Secure Your ERC20 Tokens: Best Practices for Approval and transferFrom.
// Pseudo‑code for pull pattern
function deposit(uint256 amount) external {
require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
balances[msg.sender] += amount;
}
2. Enforce Allowance Checks with SafeERC20
The OpenZeppelin SafeERC20 library wraps ERC‑20 calls and handles return values and non‑standard behavior. It also allows developers to check that the allowance is not set to the maximum value unless explicitly intended.
using SafeERC20 for IERC20;
function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
token.safeTransferFrom(from, to, amount);
}
3. Avoid Unlimited Allowances
If a protocol needs to interact with a token on behalf of a user, it should request the minimum necessary allowance and then set it back to zero after each operation. The standard pattern is:
approve(spender, 0)approve(spender, requiredAmount)
This pattern ensures that the allowance never remains high for long periods, reducing the window of opportunity for attackers.
4. Use Permit and Permit2
The permit function defined in EIP‑2612 allows approvals to be granted via an off‑chain signature. The spender never needs to hold an unlimited allowance, and the approval can be time‑limited. Permit2 extends this concept to allow multiple approvals in a single transaction. By using permit, the contract can avoid storing long‑lived allowances.
// Example with permit
token.permit(owner, spender, amount, deadline, v, r, s);
5. Implement Reentrancy Guards
Adding a nonReentrant modifier, such as the one from OpenZeppelin, protects functions that manipulate token balances or interact with external contracts, a principle discussed in Guarding Against transferFrom Attacks: A Guide for DeFi Projects.
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureTokenHandler is ReentrancyGuard {
function withdraw(uint256 amount) external nonReentrant {
// withdrawal logic
}
}
6. Check Allowance Before Transfer
Some contracts perform a manual allowance check and fail early if the allowance is insufficient. This can prevent a malicious spender from gaining more allowance through race conditions.
require(token.allowance(owner, spender) >= amount, "Allowance too low");
token.transferFrom(owner, recipient, amount);
7. Use Approve‑and‑Call Patterns with Care
If a contract relies on an approveAndCall pattern (where the token owner calls approve and then the spender’s receiveApproval function is triggered), the spender’s code must be audited and protected against reentrancy. The function should be marked as internal or use a reentrancy guard.
8. Monitor Pending Transactions
Auditors and automated tools can scan mempools for pending transferFrom transactions that involve unusually high allowances or known malicious addresses. If a threat is detected, the contract can pause operations or require a fresh approval.
Auditing Checklist for Developers
| Item | Description | Why It Matters |
|---|---|---|
| 1 | Confirm the use of SafeERC20 |
Prevents silent failures and non‑standard token behavior |
| 2 | Verify that allowances are never set to type(uint256).max |
Eliminates unlimited approval attacks |
| 3 | Ensure approve is followed by an immediate transferFrom if needed |
Prevents race condition exploits |
| 4 | Add nonReentrant guard to functions that call external contracts |
Stops reentrancy attacks |
| 5 | Use permit or permit2 where possible |
Avoids on‑chain approvals entirely |
| 6 | Document the intent of each allowance and provide a user interface for resetting it | Improves transparency and user control |
| 7 | Include a function to reset allowance to zero when a token is no longer needed | Reduces attack surface |
What Users Should Do
- Never Approve Unlimited Allowances – Whenever a protocol asks for an allowance, check the amount. If it requests the maximum possible value, avoid approving or contact the protocol’s developers.
- Use the Minimal Approval Needed – Only approve the exact amount you intend to let the protocol use.
- Review Smart Contract Source – Before approving, verify that the contract you are interacting with is audited and trusted.
- Check the Token’s Implementation – Some tokens deviate from the ERC‑20 standard (e.g., missing
safeTransfercompatibility). - Keep Tokens in a Hardware Wallet – If you’re not actively using DeFi, store your tokens offline to avoid any on‑chain approvals.
Future Directions in ERC‑20 Safety
The ecosystem is actively developing safer token standards. EIP‑2612’s permit and the forthcoming ERC‑777 standard offer more granular control over approvals and event emissions. Moreover, protocols are moving towards permit‑first approaches, where the token holder signs an approval off‑chain and the spender submits that signature to the token contract, eliminating on‑chain approval transactions entirely.
Additionally, the concept of token gating—where a contract can verify a user’s token balance without pulling tokens—reduces the need for allowances. By integrating ERC‑1155 or ERC‑721 for access control, DeFi protocols can provide functionality without exposing token balances to the protocol.
Conclusion
The transferFrom function is a cornerstone of ERC‑20 token interactions, enabling a wide range of DeFi functionalities. However, its very flexibility introduces a vector for a variety of attacks: unlimited approvals, race conditions, front‑running, reentrancy, and malicious contract approvals. By understanding the anatomy of these attacks, developers can adopt best practices such as the pull‑over‑push pattern, safe wrappers, and permissionless approvals, while users can protect themselves by avoiding unlimited allowances and verifying contract intent. Security in the DeFi space is a shared responsibility. Protocol designers must write defensively and auditors must scrutinize allowance logic, and users must remain vigilant about the permissions they grant. When all parties follow these guidelines, the risk associated with transferFrom can be dramatically reduced, leading to a more robust and trustworthy ecosystem.
Lucas Tanaka
Lucas is a data-driven DeFi analyst focused on algorithmic trading and smart contract automation. His background in quantitative finance helps him bridge complex crypto mechanics with practical insights for builders, investors, and enthusiasts alike.
Random Posts
Exploring Advanced DeFi Projects with Layer Two Scaling and ZK EVM Compatibility
Explore how top DeFi projects merge layer two scaling with zero knowledge EVM compatibility, cutting costs, speeding transactions, and enhancing privacy for developers and users.
8 months ago
Deep Dive Into Advanced DeFi Projects With NFT-Fi GameFi And NFT Rental Protocols
See how NFT, Fi, GameFi and NFT, rental protocols intertwine to turn digital art into yield, add gaming mechanics, and unlock liquidity in advanced DeFi ecosystems.
2 weeks ago
Hedging Smart Contract Vulnerabilities with DeFi Insurance Pools
Discover how DeFi insurance pools hedge smart contract risks, protecting users and stabilizing the ecosystem by pooling capital against bugs and exploits.
5 months ago
Token Bonding Curves Explained How DeFi Prices Discover Their Worth
Token bonding curves power real, time price discovery in DeFi, linking supply to price through a smart, contracted function, no order book needed, just transparent, self, adjusting value.
3 months ago
From Theory to Trading - DeFi Option Valuation, Volatility Modeling, and Greek Sensitivity
Learn how DeFi options move from theory to practice and pricing models, volatility strategies, and Greek sensitivity explained for traders looking to capitalize on crypto markets.
1 week ago
Latest Posts
Foundations Of DeFi Core Primitives And Governance Models
Smart contracts are DeFi’s nervous system: deterministic, immutable, transparent. Governance models let protocols evolve autonomously without central authority.
1 day ago
Deep Dive Into L2 Scaling For DeFi And The Cost Of ZK Rollup Proof Generation
Learn how Layer-2, especially ZK rollups, boosts DeFi with faster, cheaper transactions and uncovering the real cost of generating zk proofs.
1 day ago
Modeling Interest Rates in Decentralized Finance
Discover how DeFi protocols set dynamic interest rates using supply-demand curves, optimize yields, and shield against liquidations, essential insights for developers and liquidity providers.
1 day ago