Withdrawal patterns help safely move funds out of a smart contract. They avoid problems like losing money or blocking other actions.
Withdrawal patterns in Blockchain / Solidity
mapping(address => uint) public balances;
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0, "No funds to withdraw");
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}This example shows a simple withdrawal pattern in Solidity.
First, it checks the user's balance, then sets it to zero before sending funds.
transfer to send funds, which forwards limited gas and reverts on failure.mapping(address => uint) public balances;
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0, "No funds to withdraw");
balances[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}call which is more flexible but requires careful handling to avoid security risks.mapping(address => uint) public balances;
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0, "No funds to withdraw");
balances[msg.sender] = 0;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}mapping(address => uint) public balances;
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0, "No funds to withdraw");
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}This contract lets users deposit Ether and withdraw it safely using the withdrawal pattern.
It updates the user's balance before sending funds to avoid security issues.
pragma solidity ^0.8.20; contract SimpleWithdrawal { mapping(address => uint) public balances; // Function to deposit Ether into contract function deposit() public payable { balances[msg.sender] += msg.value; } // Withdrawal pattern: update balance before sending function withdraw() public { uint amount = balances[msg.sender]; require(amount > 0, "No funds to withdraw"); balances[msg.sender] = 0; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }
Always update user balances before sending funds to prevent reentrancy attacks.
Use call with checks instead of transfer for better gas handling.
Withdrawal patterns separate balance tracking from sending money, making contracts safer.
Withdrawal patterns help safely send funds from smart contracts.
Always update balances before transferring money.
This pattern prevents common security problems like reentrancy.
Practice
withdrawal pattern in blockchain smart contracts?Solution
Step 1: Understand withdrawal pattern purpose
The withdrawal pattern lets users request their funds instead of sending automatically.Step 2: Identify safety benefits
This prevents security issues like reentrancy by updating balances before sending.Final Answer:
To let users safely withdraw their funds by calling a function -> Option AQuick Check:
Withdrawal pattern = user-initiated safe withdrawal [OK]
- Thinking funds are sent automatically
- Confusing withdrawal with deposit
- Ignoring security benefits
Solution
Step 1: Check balance update before transfer
function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); } sets balance to zero before sending funds, preventing reentrancy.Step 2: Verify other options
function withdraw() public { payable(msg.sender).transfer(balances[msg.sender]); balances[msg.sender] = 0; } sends funds before zeroing balance (unsafe). Options C and D do not handle balances properly.Final Answer:
function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); } -> Option CQuick Check:
Update balance before transfer = function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); } [OK]
- Sending funds before updating balance
- Not updating balance at all
- Sending fixed amounts ignoring user balance
withdraw() twice in a row on this contract snippet?mapping(address => uint) balances;
function withdraw() public {
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}Solution
Step 1: Analyze first withdraw call
First call reads balance, sends funds, then sets balance to zero.Step 2: Analyze second withdraw call
Second call reads balance which is now zero, so sends zero funds.Final Answer:
The user receives funds only the first time, second call sends zero -> Option DQuick Check:
Balance zero after first withdraw = second call sends zero [OK]
- Assuming funds sent twice
- Expecting error on second call
- Thinking balance resets automatically
function withdraw() public {
payable(msg.sender).transfer(balances[msg.sender]);
balances[msg.sender] = 0;
}Solution
Step 1: Check order of operations
The function sends funds before setting balance to zero.Step 2: Understand security risk
This allows reentrancy attacks where attacker calls withdraw repeatedly before balance is zeroed.Final Answer:
Balance is updated after transfer, risking reentrancy attack -> Option BQuick Check:
Update balance before transfer to prevent reentrancy [OK]
- Updating balance after transfer
- Ignoring reentrancy risks
- Not using payable correctly
Solution
Step 1: Check balance update and transfer order
event Withdraw(address indexed user, uint amount); function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); emit Withdraw(msg.sender, amount); } updates balance before transfer, preventing reentrancy.Step 2: Verify event emits correct amount
event Withdraw(address indexed user, uint amount); function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); emit Withdraw(msg.sender, amount); } emits event after transfer with correct amount saved before zeroing balance.Step 3: Check other options
The other snippets emit event with wrong amount or update balance after transfer, causing errors or security risks.Final Answer:
event Withdraw(address indexed user, uint amount); function withdraw() public { uint amount = balances[msg.sender]; balances[msg.sender] = 0; payable(msg.sender).transfer(amount); emit Withdraw(msg.sender, amount); } -> Option AQuick Check:
Update balance, transfer, then emit event with saved amount [OK]
- Emitting event before saving amount
- Updating balance after transfer
- Emitting event with zero amount
Ready to prove it?
Pass the test and this pattern moves unknown
