diff --git a/frontend/package.json b/frontend/package.json
index 59e6967..d5496dc 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,8 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
+ "@tanstack/react-query": "^5.59.20",
"react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "react-dom": "^18.3.1",
+ "wagmi": "^2.12.27"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index dc2796a..e8a5270 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,26 +1,31 @@
import {useEffect, useState} from 'react'
import './App.css'
-import {local_accounts} from './clients/testClient'
+// import wordle hooks
import {
getAlphabet,
getWDTBalance,
getPlayerGuesses,
- getHitmap,
- canPlay,
- useFaucet,
initAttempts,
- tryGuess
+ tryGuess,
} from './hooks/WordleHooks'
+
+// import components
import {Alphabet} from './components/Alphabet/Alphabet'
import { GameSpace } from './components/GameSpace/GameSpace'
import { ApprovalModal } from './components/ApprovalModal/ApprovalModal'
import { LowFunds } from './components/LowFunds/LowFunds'
import { AlreadyPlayed } from './components/AlreadyPlayed/AlreadyPlayed'
+// wagmi connection
+import { ConnectWallet } from './clients/walletClient/ConnectWallet'
+import { wagmiConfig } from '../wagmi.config'
+import { WagmiProvider } from 'wagmi'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+
function App() {
// Current account and respetive variables
- const [currentAcc, setCurrentAcc] = useState(local_accounts[0])
+ const [currentAddress, setCurrentAddress] = useState(undefined)
const [alphabet, setAlphabet] = useState(undefined)
// TODO: remove after testing and prototyping
@@ -39,17 +44,22 @@ function App() {
// Gets the current account's WDT balance, eligibility to play, player attempts, guesses and alphabet.
// this is constantly pinging the network, so i'm unsure if this is the right way to go
useEffect(() => {
- if (currentAcc) {
- getWDTBalance(currentAcc).then(setWdtBalance)
-
- getAlphabet(currentAcc).then(res => {
- if (res.length === 0) setIsPlaying(false)
- else setIsPlaying(true)
- setAlphabet(res)
- })
- getPlayerGuesses(currentAcc).then(setPlayerGuesses)
+
+ const getParams = async (address) => {
+ const balance = await getWDTBalance(address)
+ const alphabet = await getAlphabet(address)
+ const playerGuesses = await getPlayerGuesses(address)
+ if (alphabet.length > 0) {setIsPlaying(true)}
+ if (balance) setWdtBalance(balance)
+ if (alphabet) setAlphabet(alphabet)
+ if (playerGuesses) setPlayerGuesses(playerGuesses)
+ }
+
+ if (currentAddress) {
+ getParams(currentAddress)
}
- }, [currentAcc, playerGuesses])
+
+ }, [currentAddress])
// handle the guess input
const handleGuessChange = (e) => setGuess(e.target.value)
@@ -57,11 +67,12 @@ function App() {
// handle new guess
const handleNewGuess = (account, guess) => {
tryGuess(account, guess)
+ setTimeout(() => getPlayerGuesses(account).then(setPlayerGuesses), 500)
}
const handleInitAttempts = async (account) => {
try {
- const res = await initAttempts(account)
+ await initAttempts(account)
} catch (err) {
// placeholder for token faucet user flow
if (err.message.includes("You don't have enough tokens to play")) {
@@ -81,38 +92,33 @@ function App() {
}
return (
- <>
+
+
-
+
{/* Modals for approving transactions and messages */}
- {approvalModal &&
setApprovalModal(false)}/>}
- {lowFundsWarn && setLowFundsWarn(false)}/>}
+ {approvalModal && setApprovalModal(false)}/>}
+ {lowFundsWarn && setLowFundsWarn(false)}/>}
{playedToday && setPlayedToday(false)}/>}
-
- {/* Playground: list accounts to experiment */}
- Here are some addresses:
-
WDT Balance: {wdtBalance ? `${wdtBalance.toString()} WDT` : '???'}
{/* Game space */}
{!isPlaying ?
(
-
+
) : (
)}
- >
+
+
)
}
diff --git a/frontend/src/clients/publicClient.ts b/frontend/src/clients/publicClient.ts
new file mode 100644
index 0000000..0796f9a
--- /dev/null
+++ b/frontend/src/clients/publicClient.ts
@@ -0,0 +1,24 @@
+import { createPublicClient, http, publicActions, walletActions, getContract } from 'viem';
+import { foundry } from 'viem/chains';
+import WordleABI from '../abis/WordleABI.json';
+import ERC20ABI from '../abis/ERC20ABI.json';
+
+// Public client
+export const publicClient = createPublicClient({
+ chain: foundry,
+ transport: http(),
+});
+
+// Wordle3 Contract
+export const publicWordle = getContract({
+ address: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
+ abi: WordleABI,
+ client: publicClient,
+});
+
+// Wordle Token contract
+export const publicWdt = getContract({
+ address: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
+ abi: ERC20ABI,
+ client: publicClient,
+});
diff --git a/frontend/src/clients/walletClient/ConnectWallet.tsx b/frontend/src/clients/walletClient/ConnectWallet.tsx
new file mode 100644
index 0000000..4d1098a
--- /dev/null
+++ b/frontend/src/clients/walletClient/ConnectWallet.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+import { useConnect } from 'wagmi'
+import { useAccount, useDisconnect, useEnsAvatar, useEnsName } from 'wagmi'
+
+
+/**
+ * Account component that displays connected account and passes on
+ * the address to the parent component for interaction.
+ */
+function Account({onAddressChange}) {
+ const { address } = useAccount()
+ const { disconnect } = useDisconnect()
+ const { data: ensName } = useEnsName({ address })
+ const { data: ensAvatar } = useEnsAvatar({ name: ensName! })
+
+ if (address) onAddressChange(address);
+
+ return (
+
+ {ensAvatar &&
}
+ {address &&
{ensName ? `${ensName} (${address})` : address}
}
+
+
+ )
+}
+
+/**
+ * Account component that displays available wallet connectors
+ * Connectors are also pulled from ../../wagmi.config.ts
+ */
+function WalletOptions() {
+ const { connectors, connect } = useConnect()
+
+ return connectors.map((connector) => (
+
+ ))
+}
+
+/**
+ * Wrapper for the login user flow.
+ * Receives onAddressChange from parent component to lift the address
+ * for smart contract operations
+ */
+export function ConnectWallet({onAddressChange}) {
+ const { isConnected } = useAccount()
+ if (isConnected) return
+ return
+}
\ No newline at end of file
diff --git a/frontend/src/hooks/WordleHooks.ts b/frontend/src/hooks/WordleHooks.ts
index d3ddf88..77593fd 100644
--- a/frontend/src/hooks/WordleHooks.ts
+++ b/frontend/src/hooks/WordleHooks.ts
@@ -5,36 +5,36 @@ import ERC20ABI from '../abis/ERC20ABI.json'
// Get number of remaining attempts a player still has
export async function getAttempts(account) {
- const attempts = await wordle.read.getPlayerAttempts([account.address])
- return attempts.toString()
+ const attempts = await wordle.read.getPlayerAttempts([account])
+ return formatEther(attempts)
}
// Get the current state of the player's alphabet hitmap
export async function getAlphabet(account) {
- return await wordle.read.getAlphabet([account.address])
+ return await wordle.read.getAlphabet([account])
}
// Get the current player hitmap state
export async function getHitmap(account) {
- return await wordle.read.getHiddenWord([account.address])
+ return await wordle.read.getHiddenWord([account])
}
// Gets the WDT balance of a player
export async function getWDTBalance(account) {
- const res = await wdt.read.balanceOf([account.address])
+ const res = await wdt.read.balanceOf([account])
return formatEther(res)
}
// Gets player used guesses array
export async function getPlayerGuesses(account) {
- const res = await wordle.read.getPlayerGuesses([account.address])
+ const res = await wordle.read.getPlayerGuesses([account])
return res
}
// Checks if a player is eligible to play
export async function canPlay(account) {
- return await wordle.read.canPlay([account.address])
+ return await wordle.read.canPlay([account])
}
// Give a player some WDT
@@ -44,7 +44,7 @@ export async function useFaucet(account) {
address: wordle.address,
abi: WordleABI,
functionName: 'tokenFaucet',
- args: [account.address],
+ args: [account],
})
await client.writeContract(request)
}
@@ -60,12 +60,12 @@ export async function approveGame(account) {
args: [wordle.address, 200 * decimals],
})
await client.writeContract(request)
- const allowance = await wdt.read.allowance([account.address, wordle.address])
+ const allowance = await wdt.read.allowance([account, wordle.address])
}
// Initialize the player's attempts
export async function initAttempts(account) {
- const allowance = await wdt.read.allowance([account.address, wordle.address])
+ const allowance = await wdt.read.allowance([account, wordle.address])
// simulate/validate the contract to ensure contract exists
// and is valid (+info: https://viem.sh/docs/contract/writeContract)
@@ -74,7 +74,7 @@ export async function initAttempts(account) {
address: wordle.address,
abi: WordleABI,
functionName: 'initAttempts',
- args: [account.address],
+ args: [account],
})
if (request) await client.writeContract(request)
@@ -92,7 +92,7 @@ export async function tryGuess(account, guess) {
address: wordle.address,
abi: WordleABI,
functionName: 'tryGuess',
- args: [guess, account.address],
+ args: [guess, account],
})
if (request) await client.writeContract(request)
diff --git a/frontend/wagmi.config.ts b/frontend/wagmi.config.ts
new file mode 100644
index 0000000..4752cd9
--- /dev/null
+++ b/frontend/wagmi.config.ts
@@ -0,0 +1,15 @@
+import { http, createConfig } from 'wagmi'
+import { anvil } from 'wagmi/chains'
+import { injected, metaMask, safe } from 'wagmi/connectors'
+
+export const wagmiConfig = createConfig({
+ chains: [anvil],
+ transports: {
+ [anvil.id]: http(),
+ },
+ connectors: [
+ injected(),
+ metaMask(),
+ safe(),
+ ]
+})
\ No newline at end of file