E1: NextJs Front End - Update

Front end updated with useEffect and useState for automatic balance updates.

I will outline the changes below, but you can find the full repo here - https://github.com/SimSimButDifferent/L3-L4-EthWalletFrontEnd

For the lesson 4 code, go the the L4 branch in the repo above.

Or check the deployment here - https://ethwalletapp.on.fleek.co/

EthWallet.tsx

updates:

  • Set userBalance as "Loading..." as default.

  • Added an if statement before setUserBalance in the handle functions, to also handle empty wallets.

  • Added useEffect to populate userBalance message when page launches. and to update depending on wether a wallet is connected or not and wether they have eth deposited.

  • Removed getUserBalance button, in favor of a auto-updated userBalance bar.

"use client"

import { ethers, Contract, BrowserProvider } from "ethers"
import { useWeb3ModalAccount } from "@web3modal/ethers/react"
import { contractAddresses, abi } from "../context"

import React, { useEffect, useState } from "react"
import * as dotenv from "dotenv"

dotenv.config()

const EthWallet: React.FC = () => {
    const { address, chainId, isConnected } = useWeb3ModalAccount()

    const contractAddress =
        chainId in contractAddresses ? contractAddresses[chainId][0] : null

    const [depositAmount, setDepositAmount] = useState("")
    const [withdrawAmount, setWithdrawAmount] = useState("")
    const [successMessage, setSuccessMessage] = useState("")
    
    // ------------Added Loading as default---------------------
    const [userBalance, setUserBalance] = useState("Loading...")
    // ---------------------------------------------------------
    
    const [isLoading, setIsLoading] = useState(false)

    async function deposit(value: any) {
        if (!isConnected) throw Error("User Disconnected")
        const provider = new BrowserProvider(window.ethereum)

        const getSigner = provider.getSigner()
        const signer = await getSigner
        const ethWallet = new Contract(contractAddress, abi, signer)
        const tx = await ethWallet.deposit({ value: ethers.parseEther(value) })
        const receipt = await tx.wait()

        console.log(`${value.toString()} ETH Deposited!`)
        return receipt
    }

    async function withdraw(value: any) {
        if (!isConnected) throw Error("User Disconnected")
        const provider = new BrowserProvider(window.ethereum)

        const getSigner = provider.getSigner()
        const signer = await getSigner
        const ethWallet = new Contract(contractAddress, abi, signer)
        const tx = await ethWallet.withdraw(ethers.parseEther(value))
        const receipt = await tx.wait()

        console.log(`${value.toString()} ETH Withdrawn!`)
        return receipt
    }

    const getUserBalance = async () => {
        const provider = new BrowserProvider(window.ethereum)

        const getSigner = provider.getSigner()
        const signer = await getSigner
        const ethWallet = new Contract(contractAddress, abi, signer)
        console.log(signer)

        const balance = await ethWallet.getUserBalance()

        setUserBalance(ethers.formatEther(balance))

        return balance.toString()
    }

    const handleDepositSubmit = async (
        event: React.FormEvent<HTMLFormElement>,
    ) => {
        event.preventDefault()
        setIsLoading(true)
        try {
            await deposit(depositAmount)

            setSuccessMessage(`Successfully deposited ${depositAmount} ETH!`)
            const balance = await getUserBalance()
            
            // ----- Added conditional statement to handle empty wallet -----
            if (balance == 0) {
                setUserBalance("Wallet empty!")
            } else {
                setUserBalance(
                    `User Contract Balance: ${ethers.formatEther(balance)} ETH`,
                )
            }
            // --------------------------------------------------------------

            setDepositAmount("") // Optional: Reset input field after successful deposit
            setTimeout(() => setSuccessMessage(""), 10000)
        } catch (error) {
            console.error("Error during deposit: ", error)
            setSuccessMessage("User Disconnected!")
        }
        setIsLoading(false)
    }

    const handleWithdrawSubmit = async (event) => {
        event.preventDefault()
        setIsLoading(true)
        try {
            await withdraw(withdrawAmount)

            setSuccessMessage(`Successfully withdrawn ${withdrawAmount} ETH!`)
            const balance = await getUserBalance()
            
            // ----- Added conditional statement to handle empty wallet -----
            if (balance == 0) {
                setUserBalance("Wallet Empty!")
            } else {
                setUserBalance(
                    `User Contract Balance: ${ethers.formatEther(balance)} ETH`,
                )
            }
            // --------------------------------------------------------------

            setWithdrawAmount("") // Optional: Reset input field after successful deposit
            setTimeout(() => setSuccessMessage(""), 10000)
        } catch (error) {
            console.error("Error during withdraw: ", error)
            setSuccessMessage("User Disconnected!")
        }
        setIsLoading(false)
    }

    const handleGetUserBalance = async (event) => {
        event.preventDefault()
        setIsLoading(true)
        try {
            const balance = await getUserBalance()

            // ----- Added conditional statement to handle empty wallet -----
            if (balance == 0) {
                setUserBalance("Wallet Empty!")
            } else {
                setUserBalance(
                    `User Contract Balance: ${ethers.formatEther(balance)} ETH`,
                )
            }
            // --------------------------------------------------------------

            setTimeout(() => 10000)
        } catch (error) {
            console.error("User does not exist!", error)
            setSuccessMessage("User does not exist!")
        }
        setIsLoading(false)
    }
    
    // -- useEffect to load userBalance message depending on users balance --
    useEffect(() => {
        const fetchUserBalance = async () => {
            try {
                const balance = await getUserBalance()
                if (balance == 0) {
                    setUserBalance("Wallet Empty!")
                } else {
                    setUserBalance(
                        `User Contract Balance: ${ethers.formatEther(
                            balance,
                        )} ETH`,
                    )
                }
            } catch (error) {
                console.error("Failed to fetch user balance: ", error)
            }
        }
        if (isConnected) {
            fetchUserBalance()
        } else {
            setUserBalance("Connect wallet!")
        }
    }, [isConnected])
    // ---------------------------------------------------------------------

    return (
        <div className="">
            <form onSubmit={handleDepositSubmit}>
                <div className="flex justify-center p-4">
                    <h1 className="text-6xl text-center rounded-lg border border-transparent px-5 py-4 transition-colors border-neutral-300 bg-gray-100 dark:border-neutral-700 dark:bg-neutral-800/30">
                        Eth Wallet App
                    </h1>
                </div>
                <div className="flex flex-col p-4">
                    <input
                        type="text"
                        value={depositAmount}
                        onChange={(e) => setDepositAmount(e.target.value)}
                        className="text-black text-xl text-align-center py-2"
                        placeholder="Deposit amount..."
                    />
                    <button
                        type="submit"
                        disabled={isLoading}
                        className={`px-4 pb-1 ${
                            isLoading
                                ? "bg-violet-600"
                                : "rounded-lg border border-transparent transition-colors border-neutral-300 bg-gray-100 dark:border-neutral-700 dark:bg-neutral-800/30 hover:bg-transparent"
                        } text-white rounded-lg`}
                    >
                        Deposit
                    </button>
                </div>
            </form>
            <form onSubmit={handleWithdrawSubmit}>
                <div className="flex flex-col p-4">
                    <input
                        type="text"
                        value={withdrawAmount}
                        onChange={(e) => setWithdrawAmount(e.target.value)}
                        className="text-black text-xl text-align-center py-2"
                        placeholder="Withdraw amount..."
                    />
                    <button
                        type="submit"
                        disabled={isLoading}
                        className={`px-4 pb-1 ${
                            isLoading
                                ? "bg-violet-600"
                                : "rounded-lg border border-transparent transition-colors border-neutral-300 bg-gray-100 dark:border-neutral-700 dark:bg-neutral-800/30 hover:bg-transparent"
                        } text-white rounded-lg`}
                    >
                        Withdraw
                    </button>
                </div>
            </form>
            <div className="flex justify-center pt-4">
                <div className="flex p-4 text-2xl font-light rounded-lg border border-transparent transition-colors border-neutral-300 bg-gray-100 dark:border-neutral-700 dark:bg-neutral-800/30">
                    {userBalance}
                </div>
            </div>

            {successMessage && (
                <div className="flex items-center justify-center pt-10">
                    <div className="flex justify-center px-4 py-2 text-2xl rounded-lg border border-transparent transition-colors border-neutral-300 bg-gray-100 dark:border-neutral-700 dark:bg-neutral-800/30 animate-fadeOut">
                        {successMessage}
                    </div>
                </div>
            )}
        </div>
    )
}

export default EthWallet

Last updated