Chapter 15: Create Solidity Banking System Smart contract and integrate with frontend

Chapter 15: Create Solidity Banking System Smart contract and integrate with frontend

In this Article we cover Solidity smart contract integration with a frontend. we create a banking system smart contract in Solidity and integrate it with a React frontend using Ethers.js.


1. Smart Contract for a Banking System

Features:

  • Users can deposit Ether
  • Users can withdraw Ether
  • Users can check their balance
  • Only the owner can pause or resume the contract
  • Store transaction history.
  • Calculate interest based on deposit duration.
  • Support ERC-20 tokens for deposits.

Solidity Smart Contract (BankingSystem.sol)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract DecentralizedBank {
    struct Transaction {
        uint256 amount;
        uint256 timestamp;
        bool isDeposit;
    }

    mapping(address => Transaction[]) public transactions;
    mapping(address => uint256) public balances;
    mapping(address => mapping(address => uint256)) public tokenBalances; // Multi-token balance mapping

    uint256 public interestRate = 5; // 5% annual interest

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event TokenDeposit(address indexed user, address token, uint256 amount);
    event TokenWithdraw(address indexed user, address token, uint256 amount);

    // Deposit ETH
    function deposit() external payable {
        require(msg.value > 0, "Deposit must be greater than 0");
        balances[msg.sender] += msg.value;
        transactions[msg.sender].push(Transaction(msg.value, block.timestamp, true));

        emit Deposit(msg.sender, msg.value);
    }

    // Withdraw ETH with interest
    function withdraw(uint256 _amount) external {
        require(balances[msg.sender] >= _amount, "Insufficient balance");
        
        uint256 interest = calculateInterest(msg.sender);
        uint256 totalPayout = _amount + interest;

        balances[msg.sender] -= _amount;
        transactions[msg.sender].push(Transaction(_amount, block.timestamp, false));

        payable(msg.sender).transfer(totalPayout);
        emit Withdraw(msg.sender, totalPayout);
    }

    // Deposit ERC-20 tokens
    function depositToken(address _token, uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than 0");

        IERC20 token = IERC20(_token);
        require(token.transferFrom(msg.sender, address(this), _amount), "Transfer failed");

        tokenBalances[msg.sender][_token] += _amount;
        emit TokenDeposit(msg.sender, _token, _amount);
    }

    // Withdraw ERC-20 tokens
    function withdrawToken(address _token, uint256 _amount) external {
        require(tokenBalances[msg.sender][_token] >= _amount, "Insufficient token balance");

        tokenBalances[msg.sender][_token] -= _amount;
        IERC20 token = IERC20(_token);
        require(token.transfer(msg.sender, _amount), "Transfer failed");

        emit TokenWithdraw(msg.sender, _token, _amount);
    }

    // Calculate interest based on time
    function calculateInterest(address _user) public view returns (uint256) {
        uint256 totalInterest = 0;
        for (uint i = 0; i < transactions[_user].length; i++) {
            if (transactions[_user][i].isDeposit) {
                uint256 duration = block.timestamp - transactions[_user][i].timestamp;
                uint256 yearsElapsed = duration / 365 days;
                totalInterest += (transactions[_user][i].amount * interestRate * yearsElapsed) / 100;
            }
        }
        return totalInterest;
    }

    // Get transaction history
    function getTransactionHistory(address _user) external view returns (Transaction[] memory) {
        return transactions[_user];
    }

    // Get balance
    function getBalance() external view returns (uint256) {
        return balances[msg.sender];
    }
}

2. Deploying the Smart Contract

We can deploy this contract using Hardhat, Remix, or Foundry. Here’s how to do it with Hardhat:

Install Dependencies

npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv ethers

Deploy Script (deploy.js)

const hre = require("hardhat");

async function main() {
    const BankingSystem = await hre.ethers.getContractFactory("BankingSystem");
    const bank = await BankingSystem.deploy();

    await bank.deployed();
    console.log("BankingSystem deployed to:", bank.address);
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
});

Run Deployment

npx hardhat run scripts/deploy.js --network goerli

3. Frontend Integration with React & Ethers.js

Install Dependencies

npm install ethers

React Component for Banking DApp

import { useState, useEffect } from "react";
import { ethers } from "ethers";

const CONTRACT_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS";
const ABI = [
  {
    "inputs": [],
    "name": "deposit",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [{"internalType": "uint256", "name": "_amount", "type": "uint256"}],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getBalance",
    "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
    "stateMutability": "view",
    "type": "function"
  }
];

function BankingApp() {
  const [account, setAccount] = useState(null);
  const [balance, setBalance] = useState("0");
  const [amount, setAmount] = useState("");

  useEffect(() => {
    if (account) fetchBalance();
  }, [account]);

  const connectWallet = async () => {
    if (window.ethereum) {
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const accounts = await provider.send("eth_requestAccounts", []);
      setAccount(accounts[0]);
    } else {
      alert("Install MetaMask to use this app.");
    }
  };

  const fetchBalance = async () => {
    if (!account) return;
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
    const balance = await contract.getBalance();
    setBalance(ethers.utils.formatEther(balance));
  };

  const deposit = async () => {
    if (!amount || isNaN(amount)) return alert("Enter a valid amount");
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
    const tx = await contract.deposit({ value: ethers.utils.parseEther(amount) });
    await tx.wait();
    fetchBalance();
  };

  const withdraw = async () => {
    if (!amount || isNaN(amount)) return alert("Enter a valid amount");
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
    const tx = await contract.withdraw(ethers.utils.parseEther(amount));
    await tx.wait();
    fetchBalance();
  };

  return (
    <div className="banking-app">
      <h1>Decentralized Bank</h1>
      {account ? (
        <>
          <p>Connected Wallet: {account}</p>
          <p>Balance: {balance} ETH</p>
          <input
            type="text"
            placeholder="Amount in ETH"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
          />
          <button onClick={deposit}>Deposit</button>
          <button onClick={withdraw}>Withdraw</button>
        </>
      ) : (
        <button onClick={connectWallet}>Connect Wallet</button>
      )}
    </div>
  );
}

export default BankingApp;

4. Running the Frontend

  1. Save the React component in src/BankingApp.js.
  2. Import and use it in App.js: import BankingApp from "./BankingApp"; function App() { return <BankingApp />; } export default App;
  3. Start your React app: npm start

Create frontend for this smart contract

Here’s a React.Js frontend in your Banking System Smart Contract, built with Ethers.Js and styled the usage of Tailwind CSS for a modern-day UI. This will permit customers to connect their wallets, deposit, withdraw, and test balances.


1. Install Dependencies

Before starting, ensure you have Node.js installed. Then, create a new React app and install necessary dependencies:

npx create-react-app banking-dapp
cd banking-dapp
npm install ethers
npm install tailwindcss postcss autoprefixer
npx tailwindcss init -p

2. Configure Tailwind CSS

Open tailwind.config.js and update it:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Add Tailwind directives to src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

3. Create the Frontend Component

Now, create a new file src/BankingApp.js and add the following code:

import { useState, useEffect } from "react";
import { ethers } from "ethers";

const CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS"; // Replace with your deployed contract address
const ABI = [ /* Your Contract ABI */ ];
const TOKEN_ADDRESS = "YOUR_ERC20_TOKEN_ADDRESS"; // Replace with an ERC-20 token address

function BankingApp() {
  const [account, setAccount] = useState(null);
  const [balance, setBalance] = useState("0");
  const [amount, setAmount] = useState("");
  const [history, setHistory] = useState([]);
  const [interest, setInterest] = useState("0");

  useEffect(() => {
    if (account) {
      fetchBalance();
      fetchHistory();
      fetchInterest();
    }
  }, [account]);

  const connectWallet = async () => {
    if (window.ethereum) {
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const accounts = await provider.send("eth_requestAccounts", []);
      setAccount(accounts[0]);
    } else {
      alert("Please install MetaMask!");
    }
  };

  const fetchBalance = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
    const balance = await contract.getBalance();
    setBalance(ethers.utils.formatEther(balance));
  };

  const fetchHistory = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
    const transactions = await contract.getTransactionHistory(account);
    setHistory(transactions.map(tx => ({
      amount: ethers.utils.formatEther(tx.amount),
      timestamp: new Date(tx.timestamp * 1000).toLocaleString(),
      type: tx.isDeposit ? "Deposit" : "Withdraw"
    })));
  };

  const fetchInterest = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
    const interest = await contract.calculateInterest(account);
    setInterest(ethers.utils.formatEther(interest));
  };

  const deposit = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
    const tx = await contract.deposit({ value: ethers.utils.parseEther(amount) });
    await tx.wait();
    fetchBalance();
    fetchHistory();
  };

  const withdraw = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
    const tx = await contract.withdraw(ethers.utils.parseEther(amount));
    await tx.wait();
    fetchBalance();
    fetchHistory();
  };

  const depositToken = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
    const tx = await contract.depositToken(TOKEN_ADDRESS, ethers.utils.parseUnits(amount, 18));
    await tx.wait();
    fetchBalance();
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white">
      <h1 className="text-4xl font-bold mb-6">Decentralized Bank</h1>
      {account ? (
        <div className="bg-gray-800 p-6 rounded-xl shadow-lg">
          <p className="mb-2">Wallet: <span className="text-blue-400">{account}</span></p>
          <p className="mb-2">ETH Balance: <span className="text-green-400">{balance} ETH</span></p>
          <p className="mb-4">Interest Earned: <span className="text-yellow-400">{interest} ETH</span></p>

          <input type="text" placeholder="Amount in ETH" value={amount} onChange={(e) => setAmount(e.target.value)} className="p-2 rounded-md text-black w-full mb-4" />
          
          <button onClick={deposit} className="bg-blue-500 hover:bg-blue-700 text-white py-2 px-4 rounded w-full mb-2">Deposit ETH</button>
          <button onClick={withdraw} className="bg-red-500 hover:bg-red-700 text-white py-2 px-4 rounded w-full mb-2">Withdraw ETH</button>
          <button onClick={depositToken} className="bg-purple-500 hover:bg-purple-700 text-white py-2 px-4 rounded w-full">Deposit ERC-20 Token</button>

          <h2 className="text-xl mt-6">Transaction History</h2>
          <ul className="mt-2">
            {history.map((tx, i) => <li key={i} className="text-sm">{tx.timestamp} - {tx.type}: {tx.amount} ETH</li>)}
          </ul>
        </div>
      ) : (
        <button onClick={connectWallet} className="bg-green-500 hover:bg-green-700 text-white py-3 px-6 rounded text-lg">Connect Wallet</button>
      )}
    </div>
  );
}

export default BankingApp;


4. Integrate with App.js

Modify src/App.js to import and use the BankingApp component:

import BankingApp from "./BankingApp";

function App() {
  return <BankingApp />;
}

export default App;

5. Start the React App

Run the following command to start your frontend:

npm start

FAQ for Decentralized Banking DApp

1. What is a Decentralized Banking DApp?

A Decentralized Banking DApp is a blockchain-based software that allows customers to deposit, withdraw, and earn interest on their cryptocurrency with out relying on traditional banks.

2. What cryptocurrencies does this DApp support?

The DApp supports ETH for deposits and withdrawals. Additionally, it supports ERC-20 tokens for multi-token deposits.

3. How does the interest calculation work?

Interest is calculated annually at a fixed rate of 5% based on the deposit duration. The longer funds remain in the contract, the more interest the user earns.

4. How can I check my transaction history?

Users can view their transaction history (deposits and withdrawals) through the frontend interface, wherein every transaction’s quantity, timestamp, and type are displayed.

5. Can I deposit ERC-20 tokens?

Yes, the DApp supports ERC-20 token deposits. However, users must approve the contract to spend their tokens before depositing.

6. How do I withdraw my funds?

Users can withdraw ETH or ERC-20 tokens anytime. The DApp automatically calculates interest for ETH deposits before transferring funds back to the user.

7. Is my deposited ETH locked?

No, ETH can be withdrawn anytime. However, interest accrues over time, so longer deposits yield higher interest.

8. How do I connect my wallet?

Click the “Connect Wallet” button in the frontend to link your MetaMask wallet. Ensure you’re on the correct blockchain network.

9. Is this DApp secure?

Yes, the contract follows security best practices using OpenZeppelin’s ERC-20 standard. However, always audit smart contracts before depositing large amounts.

10. Can I modify the interest rate?

Currently, the interest rate is hardcoded at 5% annually, but it can be modified in the smart contract if needed.

11. What happens if I deposit twice?

Each deposit is tracked separately in the transaction history, and interest is calculated on each deposit individually.

12. Can I add staking or lending features?

Yes! You can modify the contract to support staking pools or peer-to-peer lending to enhance functionality.

13. Do I need gas fees for transactions?

Yes, every deposit, withdrawal, and token transfer requires gas fees, which are paid in ETH.

14. How do I add more tokens to the DApp?

You can modify the contract to support additional ERC-20 tokens by adding their contract addresses and approval functions.

15. What’s next for this DApp?

Future updates can include:

  • Staking options for higher returns
  • Stablecoin deposits (DAI, USDC)
  • Mobile-friendly UI

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *