All articles
Article 3 min read

Revolutionizing Group Finances: Building a Decentralized Expense Tracker on Blockchain

Discover how to create a trustless expense-sharing application using blockchain technology, with Solidity smart contracts and a React front end for seamless user experiences.

Introduction

Managing shared expenses can often lead to misunderstandings and disputes among friends or family. Traditional methods—like spreadsheets or messy apps—can be cumbersome and error-prone. Enter ExpenseShare, a decentralized application (dApp) built on blockchain technology that streamlines expense tracking and ensures transparency among all parties involved. In this article, we’ll explore the process of building such a solution, leveraging Solidity for smart contracts and React for the user interface.

Understanding the Concept

Before diving into development, it's essential to understand what ExpenseShare aims to achieve:

1.

Transparency: All transactions are recorded on-chain, offering a clear, immutable history.

2.

Trustlessness: Users don’t need to trust a central authority; the code governs all transactions.

3.

User-Friendly: The application will have a straightforward frontend to facilitate easy interactions.

Tech Stack Overview

For this project, the chosen technologies include:

Ethereum: The blockchain platform where the smart contracts will live.

Solidity: The programming language for writing smart contracts on Ethereum.

React: A popular JavaScript library for building user interfaces.

Web3.js: A library to interact with Ethereum nodes from the frontend.

MetaMask: A browser extension that allows users to manage their Ethereum wallets.

Smart Contract Development

Developing the smart contracts with Solidity is the backbone of our application. Here's a simplified version of the ExpenseShare contract:

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

contract ExpenseShare {

    struct Expense {
        uint id;
        string description;
        uint amount;
        address[] participants;
        mapping(address => uint) balances;
    }

    mapping(uint => Expense) public expenses;
    uint public expenseCount;

    event ExpenseCreated(uint id, string description, uint amount);
    event BalanceUpdated(uint id, address participant, uint amount);

    function createExpense(string memory _description, uint _amount, address[] memory _participants) public {
        expenseCount++;
        Expense storage newExpense = expenses[expenseCount];
        newExpense.id = expenseCount;
        newExpense.description = _description;
        newExpense.amount = _amount;
        newExpense.participants = _participants;

        emit ExpenseCreated(expenseCount, _description, _amount);
    }

    function settleExpense(uint _expenseId) public {
        Expense storage expense = expenses[_expenseId];
        require(expense.id > 0, "Expense does not exist.");

        uint totalParticipants = expense.participants.length;
        uint share = expense.amount / totalParticipants;

        for (uint i = 0; i < totalParticipants; i++) {
            address participant = expense.participants[i];
            expense.balances[participant] = share;
            emit BalanceUpdated(_expenseId, participant, share);
        }
    }

    function getBalance(uint _expenseId) public view returns(uint) {
        return expenses[_expenseId].balances[msg.sender];
    }
}

Contract Breakdown

Expense Struct: Stores information about each expense, including a list of participants.

Mapping: To track all expenses and their associated IDs.

Events: To keep the frontend updated when an expense is created or settled.

Functions: For creating expenses and settling balances.

This smart contract establishes a controlled environment for expense tracking, automatically calculating shares.

Building the Frontend with React

Once the smart contract is deployed, it's time to build a user-friendly interface. Here’s a skeleton of how to set up your React component:

Setting Up a React Component

Install dependencies first:

bash
npm install web3 ethers

Now, implement the basic functionality in a React component:

javascript
import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
import ExpenseShareContract from './contracts/ExpenseShare.json';

const ExpenseTracker = () => {
    const [account, setAccount] = useState('');
    const [expenses, setExpenses] = useState([]);

    useEffect(() => {
        const init = async () => {
            const web3 = new Web3(Web3.givenProvider || "http://localhost:7545");
            const networkId = await web3.eth.net.getId();
            const deployedNetwork = ExpenseShareContract.networks[networkId];
            const contract = new web3.eth.Contract(
                ExpenseShareContract.abi,
                deployedNetwork && deployedNetwork.address,
            );

            const accounts = await web3.eth.getAccounts();
            setAccount(accounts[0]);
            // Fetch expenses
            const expenseCount = await contract.methods.expenseCount().call();
            const loadedExpenses = [];
            for (let