# Create a Dapp

#### **Building a React App for Solidity Contracts with Vite, Tailwind, and Web3**

This guide will show you how to create a React app to interact with Ethereum smart contracts using **Web3.js**. We will set up a **Vite project** with **Tailwind CSS** styling and build interfaces for two example contracts: a **Vending Machine** and a **Lottery**.

## **Steps to Set Up the React App**

**Step 1: Initialize a Vite Project**

Run the following commands to create a Vite React app, install dependencies, and start the development server:

```bash
npm init vite@latest
cd <project name>
npm install
npm run dev
```

**Step 2: Install and Configure Tailwind CSS**

To add Tailwind CSS, install the necessary packages:

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
```

In the `tailwind.config.js` file, configure Tailwind to remove unused styles in production by specifying the files to scan:

```javascript
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Add the Tailwind directives to your CSS file (e.g., `src/index.css`):

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

**Step 3: Install Web3.js**

Install Web3.js to connect your React app to the Ethereum blockchain:

```bash
npm install web3
```

***

#### **Vending Machine Contract**

The **Vending Machine** smart contract allows users to purchase items with Ether and allows the owner to restock inventory.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract VendingMachine {

    address public owner;
    mapping (address => uint) public cokeBalances;

    constructor() {
        owner = msg.sender;
        cokeBalances[address(this)] = 100;
    }

    function getVendingMachineBalance() public view returns (uint) {
        return cokeBalances[address(this)];
    }

    function restock(uint amount) public {
        require(msg.sender == owner, "Only the owner can restock.");
        cokeBalances[address(this)] += amount;
    }

    function purchase(uint amount) public payable {
        require(msg.value >= amount * 2 ether, "You must pay at least 2 ETH per item");
        require(cokeBalances[address(this)] >= amount, "Not enough items in stock to complete this purchase");
        cokeBalances[address(this)] -= amount;
        cokeBalances[msg.sender] += amount;
    }
}
```

#### **Lottery Contract**

The **Lottery** smart contract allows players to join by paying an entry fee, with the contract owner able to pick a winner at random.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Lottery {
    address public owner;
    address payable[] public players;
    uint public lotteryId;
    mapping (uint => address payable) public lotteryHistory;

    constructor() {
        owner = msg.sender;
        lotteryId = 1;
    }

    function getWinnerByLottery(uint lottery) public view returns (address payable) {
        return lotteryHistory[lottery];
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

    function getPlayers() public view returns (address payable[] memory) {
        return players;
    }

    function enter() public payable {
        require(msg.value > .001 ether);
        players.push(payable(msg.sender));
    }

    function getRandomNumber() public view returns (uint) {
        return uint(keccak256(abi.encodePacked(owner, block.timestamp)));
    }

    function pickWinner() public onlyowner {
        uint index = getRandomNumber() % players.length;
        players[index].transfer(address(this).balance);

        lotteryHistory[lotteryId] = players[index];
        lotteryId++;
        players = new address payable ;
    }

    modifier onlyowner() {
      require(msg.sender == owner);
      _;
    }
}
```

***

#### **Integrating the Contracts with the React App**

1. **Set up Web3 Provider and ABI files** for each contract in `src/contracts/`.
2. **Create components** to interact with the contracts.

#### **Example React Component for Vending Machine**

```javascript
import React, { useEffect, useState } from "react";
import Web3 from "web3";
import VendingMachineABI from "./contracts/VendingMachine.json";

const VendingMachine = () => {
    const [web3, setWeb3] = useState(null);
    const [contract, setContract] = useState(null);
    const [balance, setBalance] = useState(0);
    const [amount, setAmount] = useState(1);
    const [userAddress, setUserAddress] = useState("");

    useEffect(() => {
        async function loadWeb3() {
            const web3Instance = new Web3(window.ethereum);
            setWeb3(web3Instance);

            const vendingMachineContract = new web3Instance.eth.Contract(
                VendingMachineABI.abi,
                "<VENDING_MACHINE_CONTRACT_ADDRESS>"
            );
            setContract(vendingMachineContract);

            const accounts = await web3Instance.eth.requestAccounts();
            setUserAddress(accounts[0]);

            const balance = await vendingMachineContract.methods.getVendingMachineBalance().call();
            setBalance(balance);
        }
        loadWeb3();
    }, []);

    const handlePurchase = async () => {
        await contract.methods.purchase(amount).send({ from: userAddress, value: web3.utils.toWei((2 * amount).toString(), "ether") });
        const newBalance = await contract.methods.getVendingMachineBalance().call();
        setBalance(newBalance);
    };

    return (
        <div className="container mx-auto p-4">
            <h1 className="text-2xl font-bold">Vending Machine</h1>
            <p>Available Balance: {balance}</p>
            <input
                type="number"
                value={amount}
                onChange={(e) => setAmount(e.target.value)}
                className="border rounded p-2"
            />
            <button onClick={handlePurchase} className="bg-blue-500 text-white p-2 rounded">Buy Item</button>
        </div>
    );
};

export default VendingMachine;
```

***

#### **Testing the Contracts with Hardhat**

**Step 1: Install Hardhat**

If you haven’t already:

```bash
npm install --save-dev hardhat
```

**Step 2: Create Test Files**

Create test files for each contract. For instance, a `VendingMachine` test might look like this:

```javascript
const { expect } = require("chai");

describe("VendingMachine", function () {
    let VendingMachine, vendingMachine, owner, user;

    beforeEach(async function () {
        [owner, user] = await ethers.getSigners();
        VendingMachine = await ethers.getContractFactory("VendingMachine");
        vendingMachine = await VendingMachine.deploy();
        await vendingMachine.deployed();
    });

    it("should initialize with a balance of 100", async function () {
        expect(await vendingMachine.getVendingMachineBalance()).to.equal(100);
    });

    it("should allow owner to restock", async function () {
        await vendingMachine.connect(owner).restock(50);
        expect(await vendingMachine.getVendingMachineBalance()).to.equal(150);
    });

    it("should allow user to purchase items", async function () {
        await vendingMachine.connect(user).purchase(1, { value: ethers.utils.parseEther("2") });
        expect(await vendingMachine.cokeBalances(user.address)).to.equal(1);
    });
});
```

Run the tests with:

```bash
npx hardhat test
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://abc-71.gitbook.io/curriculum/week-4/create-a-dapp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
