DEV Community

Freecodingboss
Freecodingboss

Posted on

πŸ’Έ Step-by-Step Guide: Building a Split Payment DApp (for Beginners)

Send ETH to multiple people in one transaction, with a frontend to manage it!


βœ… What We’re Building

We’ll create a Split Payment DApp that:

  • Lets users input multiple Ethereum addresses and amounts
  • Splits the ETH accordingly and sends it
  • Shows a history of all payments made through the contract

🧱 Step 1: Set Up the Smart Contract

1. Install Dependencies
Install Hardhat, a dev tool for Ethereum.

npm init -y
npm install --save-dev hardhat
npx hardhat

Enter fullscreen mode Exit fullscreen mode

Choose "Create a basic sample project" and follow the prompts.

Then, install these:

npm install --save-dev @nomicfoundation/hardhat-toolbox

Enter fullscreen mode Exit fullscreen mode

2. Create the Smart Contract
Create contracts/SplitPayment.sol:

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

contract SplitPayment {
    // Owner of the contract
    address public owner;

    // Struct to record each payment details
    struct Payment {
        address sender;              // Address of the sender
        address[] recipients;       // List of recipient addresses
        uint256[] amounts;          // List of amounts sent to each recipient
        uint256 timestamp;          // Timestamp of when the payment was made
    }

    // Array to store all payment history
    Payment[] public payments;

    // Event emitted when a payment is successfully sent
    event PaymentSent(address indexed sender, address[] recipients, uint256[] amounts, uint256 timestamp);

    // Constructor sets the contract deployer as the owner
    constructor() {
        owner = msg.sender;
    }

    /**
     * @dev Splits the sent ETH among recipients based on specified amounts
     * @param recipients Array of recipient addresses
     * @param amounts Array of ETH amounts to be sent to each recipient
     */
    function splitPayment(address[] calldata recipients, uint256[] calldata amounts) external payable {
        // Ensure the lengths of recipients and amounts match
        require(recipients.length == amounts.length, "Recipients and amounts length mismatch");

        uint256 totalAmount;
        // Calculate total amount to be distributed
        for (uint256 i = 0; i < amounts.length; i++) {
            totalAmount += amounts[i];
        }

        // Ensure the sent ETH matches the total distribution amount
        require(msg.value == totalAmount, "Incorrect ETH sent");

        // Transfer respective amounts to each recipient
        for (uint256 i = 0; i < recipients.length; i++) {
            payable(recipients[i]).transfer(amounts[i]);
        }

        // Record the payment in history
        payments.push(Payment({
            sender: msg.sender,
            recipients: recipients,
            amounts: amounts,
            timestamp: block.timestamp
        }));

        // Emit the PaymentSent event
        emit PaymentSent(msg.sender, recipients, amounts, block.timestamp);
    }

    /**
     * @dev Returns the total number of payments made
     */
    function getPaymentsCount() external view returns (uint256) {
        return payments.length;
    }

    /**
     * @dev Retrieves the details of a specific payment by index
     * @param index The index of the payment to retrieve
     * @return sender The address who sent the payment
     * @return recipients The list of recipients
     * @return amounts The list of amounts sent to each recipient
     * @return timestamp The timestamp when the payment was made
     */
    function getPayment(uint256 index) external view returns (address sender, address[] memory recipients, uint256[] memory amounts, uint256 timestamp) {
        require(index < payments.length, "Invalid index");
        Payment storage payment = payments[index];
        return (payment.sender, payment.recipients, payment.amounts, payment.timestamp);
    }
}

Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Step 2: Compile & Deploy Smart Contract

1. Compile

npx hardhat compile

Enter fullscreen mode Exit fullscreen mode

2. Deploy to Testnet (e.g. Sepolia)

  • Get test ETH from faucet
  • Configure hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  networks: {
    sepolia: {
      url: "https://sepolia.infura.io/v3/",
      accounts: ["YOUR_PRIVATE_KEY"]
    }
  },
  solidity: "0.8.19",
};

Enter fullscreen mode Exit fullscreen mode
  • Create scripts/deploy.js:
async function main() {
  const SplitPayment = await ethers.getContractFactory("SplitPayment");
  const contract = await SplitPayment.deploy();
  await contract.waitForDeployment();
  console.log("Contract deployed to:", contract.target);
}

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

Enter fullscreen mode Exit fullscreen mode

Run deployment:

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

Enter fullscreen mode Exit fullscreen mode

🎨 Step 3: Build the Frontend (React)

1. Set up a React App

npx create-react-app split-dapp --template typescript
cd split-dapp
npm install ethers
Enter fullscreen mode Exit fullscreen mode

2. Add Your Contract ABI
Save your ABI from Hardhat (artifacts/contracts/SplitPayment.sol/SplitPayment.json) into a file called SplitPaymentABI.ts:

export const SplitPaymentABI = [
  // paste the ABI here
];

Enter fullscreen mode Exit fullscreen mode

3. Add the Frontend Code
Paste your frontend code into App.tsx. (You already shared this in your message.)

Replace the contract address with the deployed one:

const CONTRACT_ADDRESS = "0x..."; // your deployed address

Enter fullscreen mode Exit fullscreen mode

🌐 Step 4: Connect to MetaMask
When you run the app (npm start), the browser will prompt you to connect MetaMask. Once connected:

  • Enter recipient addresses and ETH values
  • Click Send Payment
  • Use Load Payment History to see past transactions

πŸ” Security Notes

  • Always test on testnets before mainnet
  • Add checks for duplicate addresses or suspicious inputs
  • Consider using OpenZeppelin libraries for added safety

βœ… Summary

You now have:

  • A Solidity smart contract to split ETH
  • A React frontend to interact with the contract
  • Payment history tracking
  • MetaMask integration for ease of use

🧠 What to Learn Next?

  • Use Hardhat test scripts for automated testing
  • Upgrade to ERC20 token splits
  • Add login with WalletConnect or RainbowKit
  • Secure backend for storing analytics or events

Link to the github repo

Top comments (0)

OSZAR »