I just want to share a security issue in smart contracts that many developers often overlook — reentrancy attacks. If you're building smart contracts on Solidity, this is something you must understand thoroughly.



Simply put, reentrancy occurs when a contract calls another contract, and that called contract can call back into the original contract while it is still executing. Imagine you have ContractA holding 10 Ether, and ContractB sends 1 Ether to it. When ContractB withdraws funds, ContractA checks if the balance is greater than 0, and if so, sends Ether back. However, if ContractB has a fallback function(, it can call the withdraw function of ContractA again before the first call completes. The result? ContractB's recorded balance still shows 1 Ether, so it receives another 1 Ether, and this loop continues until ContractA runs out of funds.

How does this attack work? The attacker needs two things: an attack)( function to initiate the attack, and a fallback function to call back the withdraw function. The fallback function is a special external function with no name, no parameters, which anyone can trigger by calling a non-existent function, sending Ether without data, or sending Ether with no data.

Here's a concrete example: the EtherStore contract has a deposit)( function that stores balances and a withdrawAll)( function that withdraws all funds. The problem is, withdrawAll)( checks the balance, sends Ether, then updates the balance to zero. This creates a window for reentrancy attack.

So, how to protect against this? I'll show three methods.

First, use the noReentrant modifier. The idea is simple: lock the contract during the execution of a function. If someone tries to call the function again, they must pass the lock check, but the lock only releases after the function finishes. The modifier is a special function that adds conditions to other functions without rewriting all logic.

Second, apply the Check-Effect-Interaction pattern. Instead of checking conditions, sending funds, then updating balances, you should check first, update the balance immediately)before sending funds(, and only then perform external interactions. This way, even if reentrancy occurs, the balance has already been set to zero, preventing further withdrawals.

Third, if your project involves multiple interacting contracts, you need a GlobalReentrancyGuard. Instead of locking individual functions, lock the entire system with a state variable stored in a separate contract. Whenever any function in any contract is called, it checks whether the system is locked. If it is, the transaction is rejected. This is especially useful if you have contracts like ScheduledTransfer sending funds to AttackTransfer — GlobalReentrancyGuard will prevent the entire reentrancy attack chain.

The great thing about these three methods is that you can combine them depending on the situation. Important functions? Use noReentrant. Multiple related functions? Use Check-Effect-Interaction. Entire complex project? Use GlobalReentrancyGuard. Understanding reentrancy and how to defend against it will help you build much safer smart contracts.
View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Repost
  • Share
Comment
Add a comment
Add a comment
No comments
  • Pin