DEV Community

Cover image for 🪙 Day 6 of 30 Days of Solidity: Build a Digital Piggy Bank
Saurav Kumar
Saurav Kumar

Posted on

🪙 Day 6 of 30 Days of Solidity: Build a Digital Piggy Bank

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.sender to 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

🧩 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

  1. Go to Remix IDE.
  2. Paste the code into a new Solidity file.
  3. Compile with Solidity 0.8.20 or above.
  4. Deploy using Injected Web3 (e.g., MetaMask).
  5. 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() or send().
  • 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)