E1: NextJs Front End
I decided to do a front end and deploy the wallet app to ipfs, to refresh my mind on NextJs, and using wallet providers, which I also briefly explored, going through Patrick Collins' youtube course.
Last updated
I decided to do a front end and deploy the wallet app to ipfs, to refresh my mind on NextJs, and using wallet providers, which I also briefly explored, going through Patrick Collins' youtube course.
Last updated
Have a front end that properly connects and works with the backend.
Have a working wallet button, so I could interact with the contract using Metamask
Have links to all my other information.
All built with a production ready framework.
I will outline the code below, but you can find the full repo here - https://github.com/SimSimButDifferent/L3-EthWalletFrontEnd
Or check the deployment here - https://ethwalletapp.on.fleek.co/
import { ReactNode } from "react"
import { Web3ModalProvider } from "@/context/Web3Modal"
import Header from "@/components/Header"
import EthWallet from "@/components/EthWallet"
import { Poppins } from "next/font/google"
import "./globals.css"
const inter = Poppins({
weight: ["200", "300", "400", "500", "600"],
subsets: ["latin"],
})
export const metadata = {
title: "EthWalletApp",
description: "Simple EthWalletApp by simsimbutdifferent",
}
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body className="flex flex-col justify-between bg-[url(../public/Space-Background.jpg)] bg-cover bg-fixed">
<Web3ModalProvider>
<Header />
<EthWallet />
{children}
</Web3ModalProvider>
</body>
</html>
)
}
import Head from "next/head"
export default function Home() {
return (
<div>
<Head>
<title>Eth Wallet App</title>
<meta name="description" content="Eth Wallet App"></meta>
</Head>
<main className="flex min-h-screen flex-col items-center justify-between p-24 pt-4 ">
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-3 lg:text-left">
<a
href="https://github.com/SimSimButDifferent/L3-EthWalletFrontEnd"
className="group rounded-lg border border-transparent px-10 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Front End Repo{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
->
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Check out the repo if you want to mess around with
the code yourself.
</p>
</a>
<a
href="https://github.com/SimSimButDifferent/L3-EthWallet"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Backend Repo{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
->
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Take a look at the hardhat deployment of the smart
contract.
</p>
</a>
<a
href="https://simsimbutdifferent.gitbook.io/prompt_web3/"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
My Journey{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
->
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Have a look into my self-learning journey into web3
using chatGpt.
</p>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
A template to learn anything software related.
</p>
</a>
</div>
</main>
</div>
)
}
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
button,
label,
input {
align-self: center;
margin-bottom: 5px;
font-size: 24px;
border-radius: 8px;
margin-top: 10px;
text-align: center;
font-weight: 300;
}
"use client"
import { ethers, Contract, BrowserProvider } from "ethers"
import { useWeb3ModalAccount } from "@web3modal/ethers/react"
import { contractAddresses, abi } from "../context"
import React, { 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("")
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
}
async function getUserBalance() {
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()
return balance.toString()
}
const handleDepositSubmit = async (
event: React.FormEvent<HTMLFormElement>,
) => {
event.preventDefault()
setIsLoading(true)
try {
await deposit(depositAmount)
setSuccessMessage(`Successfully deposited ${depositAmount} 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!`)
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()
setSuccessMessage(
`User Balance: ${ethers.formatEther(balance)} ETH!`,
)
setTimeout(() => setSuccessMessage(""), 10000)
} catch (error) {
console.error("User does not exist!", error)
setSuccessMessage("User does not exist!")
}
setIsLoading(false)
}
return (
<div className="">
<form onSubmit={handleDepositSubmit}>
<div className="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"
: "bg-sky-500 hover:bg-sky-400"
} 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"
: "bg-sky-500 hover:bg-sky-400"
} text-white rounded-lg`}
>
Withdraw
</button>
</div>
</form>
<div className="flex flex-col justify-center pt-8">
<button
onClick={handleGetUserBalance}
className="flex justify-center px-4 py-2 text-2xl bg-sky-500 hover:bg-sky-400 text-white rounded-lg"
>
Get user balance
</button>
</div>
{successMessage && (
<div className="flex items-center justify-center pt-10">
<div className="flex justify-center px-4 py-2 text-2xl bg-purple-900 rounded-lg max-w-fit font-light animate-fadeOut">
{successMessage}
</div>
</div>
)}
</div>
)
}
export default EthWallet
"use client"
import React from "react"
const Header: React.FC = () => {
return (
<nav>
<div className="flex flex-cols-2 justify-end">
<div className="p-2">
<w3m-button size="md" />
</div>
<div className="p-2">
<w3m-network-button />
</div>
</div>
</nav>
)
}
export default Header
"use client"
import { createWeb3Modal, defaultConfig } from "@web3modal/ethers/react"
// 1. Get projectId at https://cloud.walletconnect.com
const projectId = "cf80e8dfdf111fd756361f3117399405"
// 2. Set chains
const mainnet = {
chainId: 1,
name: "Ethereum",
currency: "ETH",
explorerUrl: "https://etherscan.io",
rpcUrl: "https://cloudflare-eth.com",
}
const sepolia = {
chainId: 11155111,
name: "Sepolia",
currency: "ETH",
explorerUrl: "https://sepolia.etherscan.io",
rpcUrl: "https://rpc.sepolia.org",
}
const localhost = {
chainId: 31337,
name: "Localhost 8545",
currency: "ETH",
explorerUrl: "",
rpcUrl: "http://localhost:8545",
}
// 3. Create modal
const metadata = {
name: "Eth Wallet",
description: "Simple Eth Wallet app created by Simeon Campbell",
url: "ethwalletapp.on.fleek.co",
icons: ["https://avatars.mywebsite.com/"],
}
createWeb3Modal({
ethersConfig: defaultConfig({ metadata }),
chains: [mainnet, sepolia, localhost],
projectId,
})
import { ReactNode } from "react"
export function Web3ModalProvider({ children }: { children: ReactNode }) {
return children
}
[
{
"inputs": [],
"name": "EthWallet_InsufficientContractBalance",
"type": "error"
},
{
"inputs": [],
"name": "EthWallet__DepositMustBeAboveZero",
"type": "error"
},
{
"inputs": [],
"name": "EthWallet__WalletIsEmpty",
"type": "error"
},
{
"inputs": [],
"name": "EthWallet__WithdrawalExceedsUserBalance",
"type": "error"
},
{
"inputs": [],
"name": "EthWallet__WithdrawalMustBeAboveZero",
"type": "error"
},
{
"inputs": [],
"name": "dailyWithdrawalLimit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "getUserBalance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_withdrawalAmount",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
{
"11155111": ["0x0597071313ae58624FFbbDAB8643aD96E27eD3bc"],
"31337": ["0x5FbDB2315678afecb367f032d93F642f64180aa3"]
}
const contractAddresses = require("./contractAddresses.json")
const abi = require("./abi.json")
module.exports = {
contractAddresses,
abi,
}