Smart contracts aren’t just about tokens — they can also help users save and manage Ether securely.
Today, we’ll build a Digital Piggy Bank — a simple yet powerful example of how to store, withdraw, and track Ether safely on the blockchain.
This project demonstrates real-world Solidity skills, including secure fund handling, user-specific balances, reentrancy protection, and event logging.
🎯 What You’ll Learn
By completing this project, you’ll gain hands-on experience in:
- Managing user balances using mappings.
- Handling Ether deposits and withdrawals.
- Using
msg.senderto identify users. - Emitting events for better traceability.
- Implementing security best practices like the Checks-Effects-Interactions pattern and reentrancy guards.
🧠 Concept Overview
A Digital Piggy Bank lets each user deposit and withdraw Ether independently.
When a user deposits Ether, it gets recorded under their address.
They can later withdraw their funds safely without affecting others.
This mimics a personalized savings system, showing how decentralized finance (DeFi) apps handle value securely.
💻 Smart Contract Code
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title DigitalPiggyBank * @notice A secure and gas-efficient smart contract that allows users to deposit and withdraw Ether. * Includes proper checks, events, and a safe withdrawal mechanism. */ contract DigitalPiggyBank { /// @dev Mapping to store each user's Ether balance mapping(address => uint256) private _balances; /// @dev Emitted when a user deposits Ether event Deposited(address indexed user, uint256 amount); /// @dev Emitted when a user withdraws Ether event Withdrawn(address indexed user, uint256 amount); /// @dev Reentrancy guard to prevent reentrancy attacks bool private _locked; modifier nonReentrant() { require(!_locked, "Reentrancy detected"); _locked = true; _; _locked = false; } /** * @notice Deposit Ether into your digital piggy bank. * @dev Ether sent is credited to the sender's balance. */ function deposit() external payable { require(msg.value > 0, "Deposit amount must be greater than zero"); _balances[msg.sender] += msg.value; emit Deposited(msg.sender, msg.value); } /** * @notice Withdraw Ether from your balance. * @param amount The amount of Ether to withdraw (in wei). */ function withdraw(uint256 amount) external nonReentrant { uint256 userBalance = _balances[msg.sender]; require(amount > 0, "Withdrawal amount must be greater than zero"); require(userBalance >= amount, "Insufficient balance"); // Update balance before transferring (Checks-Effects-Interactions pattern) _balances[msg.sender] = userBalance - amount; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); emit Withdrawn(msg.sender, amount); } /** * @notice View the balance of the caller. * @return The Ether balance of the caller in wei. */ function getMyBalance() external view returns (uint256) { return _balances[msg.sender]; } /** * @notice View the total Ether held in this contract. * @return The total Ether in wei. */ function getContractBalance() external view returns (uint256) { return address(this).balance; } } 🧩 Key Features
| Feature | Description |
|---|---|
| 💰 Deposit & Withdraw | Users can deposit and withdraw Ether anytime. |
| 🔒 Reentrancy Protection | Prevents reentrancy attacks using a custom guard. |
| 🧾 Event Logging | Transparent tracking of deposits and withdrawals. |
| ⚙️ Safe Transfers | Uses .call for modern, gas-efficient Ether transfers. |
| 🧱 Industry Best Practices | Implements Checks-Effects-Interactions and private state variables. |
🧪 How to Test
- Go to Remix IDE.
- Paste the code into a new Solidity file.
- Compile with Solidity 0.8.20 or above.
- Deploy using Injected Web3 (e.g., MetaMask).
- Try depositing and withdrawing Ether to test it out!
Example tests:
- Deposit 0.1 ETH → check your balance.
- Withdraw 0.05 ETH → verify balance update.
- Check contract balance to confirm total deposits.
🔐 Security Highlights
- Non-reentrant withdrawals: Prevents attacks that exploit recursive calls.
- Validated inputs: Ensures only valid deposit and withdrawal amounts.
- Safe transfer pattern: Avoids gas limit issues common with
transfer()orsend(). - Immutable data flow: Each user can only control their own balance.
🌍 Real-World Use Cases
This project can evolve into:
- A DeFi savings vault where users lock funds for rewards.
- A donation wallet that records every contributor.
- A microbanking system for learning blockchain-based finance.
🔗 Source Code
Check out the full code and other Solidity projects in my repository 👇
👉 GitHub Repository
💬 Closing Thoughts
This Digital Piggy Bank is a major step toward understanding how real-world DeFi systems handle funds securely.
By mastering Ether management and safe coding patterns, you’re moving closer to becoming a professional blockchain developer.
Stay tuned for Day 7, where we’ll continue exploring more advanced Solidity concepts!
Top comments (0)