Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app/src/app/tickets/components/tickets-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function TicketsContent({

useEffect(() => {
if (isSuccess) {
setRefreshKey((k) => k + 1)
setRefreshKey((prevKey) => prevKey + 1)
}
}, [isSuccess])

Expand Down
26 changes: 13 additions & 13 deletions packages/app/src/app/tickets/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { useTransactionNotifications } from '@/hooks/web3/useTransactionNotifica
import { InlineSpinner } from '@/components/loading-spinner'

export default function CreateTicket() {
const [name, setName] = useState('')
const [price, setPrice] = useState('0.01')
const [amount, setAmount] = useState('100')
const [ticketName, setTicketName] = useState('')
const [ticketPrice, setTicketPrice] = useState('0.01')
const [ticketSupply, setTicketSupply] = useState('100')
const [maxSellPerPerson, setMaxSellPerPerson] = useState('5')
const [infoUrl, setInfoUrl] = useState('')

const { address, chain } = useAccount()
const { Add } = useNotifications()
const { addNotification } = useNotifications()

const { address: contractAddress, abi } = useContractConfig(chain)

Expand All @@ -38,7 +38,7 @@ export default function CreateTicket() {

const handleCreateTicket = () => {
if (!address) {
Add('Please connect your wallet first', { type: 'warning' })
addNotification('Please connect your wallet first', { type: 'warning' })
return
}

Expand All @@ -48,7 +48,7 @@ export default function CreateTicket() {
address: contractAddress,
abi,
functionName: 'create',
args: [name, parseEther(price), BigInt(amount), BigInt(maxSellPerPerson), infoUrl, emptyBytes as `0x${string}`],
args: [ticketName, parseEther(ticketPrice), BigInt(ticketSupply), BigInt(maxSellPerPerson), infoUrl, emptyBytes as `0x${string}`],
})
}

Expand All @@ -64,8 +64,8 @@ export default function CreateTicket() {
<label className='label-text'>Event Name</label>
<input
type='text'
value={name}
onChange={(e) => setName(e.target.value)}
value={ticketName}
onChange={(e) => setTicketName(e.target.value)}
placeholder='Enter event name'
className='input-field'
/>
Expand All @@ -76,8 +76,8 @@ export default function CreateTicket() {
<label className='label-text'>Ticket Price (ETH)</label>
<input
type='number'
value={price}
onChange={(e) => setPrice(e.target.value)}
value={ticketPrice}
onChange={(e) => setTicketPrice(e.target.value)}
placeholder='0.01'
className='input-field'
step='0.01'
Expand All @@ -89,8 +89,8 @@ export default function CreateTicket() {
<label className='label-text'>Total Supply</label>
<input
type='number'
value={amount}
onChange={(e) => setAmount(e.target.value)}
value={ticketSupply}
onChange={(e) => setTicketSupply(e.target.value)}
placeholder='100'
className='input-field'
min='1'
Expand Down Expand Up @@ -126,7 +126,7 @@ export default function CreateTicket() {
<button
className='btn-primary w-full py-3 mt-4'
onClick={handleCreateTicket}
disabled={isLoading || !name || !address}>
disabled={isLoading || !ticketName || !address}>
{isLoading ? (
<div className='flex items-center justify-center'>
<InlineSpinner className='mr-2' />
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/components/network-guard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import React, { useEffect, useRef } from 'react'
export function NetworkGuard({ children }: { children: React.ReactNode }) {
const { chain } = useAccount()
const { switchChain, isPending, error } = useSwitchChain()
const { Add } = useNotifications()
const { addNotification } = useNotifications()
const notifiedRef = useRef<number | null>(null)

useEffect(() => {
if (chain && chain.id !== sepolia.id) {
if (notifiedRef.current !== chain.id) {
Add('You are connected to the wrong network. Please switch to Sepolia.', { type: 'warning' })
addNotification('You are connected to the wrong network. Please switch to Sepolia.', { type: 'warning' })
notifiedRef.current = chain.id
}
} else {
notifiedRef.current = null // Reset if on correct network
}
}, [chain, Add])
}, [chain, addNotification])

if (chain && chain.id !== sepolia.id) {
return (
Expand Down
14 changes: 7 additions & 7 deletions packages/app/src/context/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import { StatusIcon } from '@/components/alert'
type NotificationOptions = Partial<Omit<Notification, 'message'>>

interface NotificationContext {
Add: (message: string, options?: NotificationOptions) => void
Clear: () => void
addNotification: (message: string, options?: NotificationOptions) => void
clearNotifications: () => void
notifications: Notification[]
}

const defaultNotificationContext: NotificationContext = {
Add: () => {},
Clear: () => {},
addNotification: () => {},
clearNotifications: () => {},
notifications: [],
}

Expand All @@ -45,7 +45,7 @@ export function NotificationProvider(props: PropsWithChildren) {
}
}, [])

function Add(message: string, options?: NotificationOptions) {
function addNotification(message: string, options?: NotificationOptions) {
const notification: Notification = {
message,
type: options?.type || 'info',
Expand All @@ -58,13 +58,13 @@ export function NotificationProvider(props: PropsWithChildren) {
toast(message, { type: notification.type, icon: <StatusIcon type={notification.type} /> })
}

function Clear() {
function clearNotifications() {
localStorage.removeItem('notifications')
setNotifications([])
}

return (
<NotificationContext.Provider value={{ Add, Clear, notifications }}>
<NotificationContext.Provider value={{ addNotification, clearNotifications, notifications }}>
{props.children}
<ToastContainer
limit={5}
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/hooks/tickets/useAvailableTickets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function useAvailableTickets(
setLoading(true)
setError(null)
try {
const results: Ticket[] = []
const fetchedTickets: Ticket[] = []
for (const id of ticketIds) {
const details = await readContract<TicketDetails>(
{
Expand All @@ -59,7 +59,7 @@ export function useAvailableTickets(
chain
)
if (details) {
results.push({
fetchedTickets.push({
id,
name: details[1] || 'Unknown Ticket',
price: details[2] || BigInt(0),
Expand All @@ -69,7 +69,7 @@ export function useAvailableTickets(
})
}
}
setTickets(results)
setTickets(fetchedTickets)
} catch (_err) {
setError('Failed to load tickets')
} finally {
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/hooks/tickets/useBuyTicket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ import { useTransactionNotifications } from '@/hooks/web3/useTransactionNotifica
* ```
*/
export function useBuyTicket(contractAddress: `0x${string}`) {
const { data: buyTxData, writeContract } = useWriteContract()
const { isLoading, error, isSuccess } = useWaitForTransactionReceipt({ hash: buyTxData })
const { data: buyTransactionHash, writeContract } = useWriteContract()
const { isLoading, error, isSuccess } = useWaitForTransactionReceipt({ hash: buyTransactionHash })

// Use centralized notification handling
useTransactionNotifications(buyTxData, isSuccess, error, {
useTransactionNotifications(buyTransactionHash, isSuccess, error, {
successMessage: 'Successfully purchased ticket!',
errorMessagePrefix: 'Failed to purchase ticket',
includeExplorerLink: true,
Expand Down
16 changes: 8 additions & 8 deletions packages/app/src/hooks/tickets/useOwnedTickets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export function useOwnedTickets(
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)

const fetchOwned = useCallback(async () => {
const fetchOwnedTickets = useCallback(async () => {
if (!address || !chain) return

setLoading(true)
setError(null)
try {
const owned: OwnedTicket[] = []
const ownedTicketsList: OwnedTicket[] = []

for (const id of ticketIds) {
try {
Expand Down Expand Up @@ -81,7 +81,7 @@ export function useOwnedTickets(
// The second element (index 1) is the name
const name = ticketDetails[1]

owned.push({
ownedTicketsList.push({
id,
name: name || `Ticket #${id.toString()}`,
quantity: balance,
Expand All @@ -94,7 +94,7 @@ export function useOwnedTickets(
}
}

setOwnedTickets(owned)
setOwnedTickets(ownedTicketsList)
} catch (err) {
if (process.env.NODE_ENV === 'development') {
console.error('Error fetching owned tickets:', err)
Expand All @@ -106,15 +106,15 @@ export function useOwnedTickets(
}, [contractAddress, address, ticketIds, chain])

useEffect(() => {
if (contractAddress && address && ticketIds.length > 0 && chain) fetchOwned()
}, [contractAddress, address, ticketIds, chain, fetchOwned, refreshKey])
if (contractAddress && address && ticketIds.length > 0 && chain) fetchOwnedTickets()
}, [contractAddress, address, ticketIds, chain, fetchOwnedTickets, refreshKey])

const refetch = useCallback(() => {
if (contractAddress && address && ticketIds.length > 0 && chain) {
return fetchOwned()
return fetchOwnedTickets()
}
return Promise.resolve()
}, [contractAddress, address, ticketIds, chain, fetchOwned])
}, [contractAddress, address, ticketIds, chain, fetchOwnedTickets])

return {
ownedTickets,
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/hooks/tickets/useTicketIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function useTicketIds(
setLoading(true)
setError(null)
try {
const ids: bigint[] = []
const fetchedTicketIds: bigint[] = []
const safeLength = Math.min(Number(ticketIdsLength), 100)
for (let i = 0; i < safeLength; i++) {
try {
Expand All @@ -71,12 +71,12 @@ export function useTicketIds(
},
chain
)
if (id) ids.push(id)
if (id) fetchedTicketIds.push(id)
} catch (_) {
// Continue to next ID
}
}
setTicketIds(ids)
setTicketIds(fetchedTicketIds)
} catch (_err) {
setError('Failed to load ticket IDs')
} finally {
Expand Down
10 changes: 5 additions & 5 deletions packages/app/src/hooks/web3/useTransactionNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function useTransactionNotifications(
error: Error | null,
options: TransactionNotificationOptions
) {
const { Add } = useNotifications()
const { addNotification } = useNotifications()
const { chain } = useAccount()

// Track which transaction hash we've notified about
Expand All @@ -61,27 +61,27 @@ export function useTransactionNotifications(
const explorerUrl = chain?.blockExplorers?.default.url
const shouldIncludeLink = options.includeExplorerLink && explorerUrl

Add(options.successMessage, {
addNotification(options.successMessage, {
type: 'success',
href: shouldIncludeLink ? `${explorerUrl}/tx/${txHash}` : undefined,
})

notifiedTxHashRef.current = txHash
notifiedErrorRef.current = false
}
}, [isSuccess, txHash, Add, options.successMessage, options.includeExplorerLink, chain])
}, [isSuccess, txHash, addNotification, options.successMessage, options.includeExplorerLink, chain])

// Handle error notification
useEffect(() => {
if (error && !notifiedErrorRef.current) {
const errorPrefix = options.errorMessagePrefix || 'Transaction failed'
const errorMessage = error.message || 'Unknown error'

Add(`${errorPrefix}: ${errorMessage}`, {
addNotification(`${errorPrefix}: ${errorMessage}`, {
type: 'error',
})

notifiedErrorRef.current = true
}
}, [error, Add, options.errorMessagePrefix])
}, [error, addNotification, options.errorMessagePrefix])
}
8 changes: 4 additions & 4 deletions packages/app/src/hooks/web3/useTransactionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function useTransactionState(
const [isTransactionSuccess, setIsTransactionSuccess] = useState(false)
const [transactionError, setTransactionError] = useState<Error | null>(null)
const [transactionHash, setTransactionHash] = useState<`0x${string}` | undefined>()
const { Add } = useNotifications()
const { addNotification } = useNotifications()

const { isLoading: txLoading, error: txError, isSuccess: txSuccess } = useWaitForTransactionReceipt({ hash: txData })

Expand All @@ -58,17 +58,17 @@ export function useTransactionState(
if (txSuccess) {
setIsTransactionSuccess(true)
setTransactionHash(txData)
Add(`Transaction successful`, {
addNotification(`Transaction successful`, {
type: 'success',
href: chain?.blockExplorers?.default.url ? `${chain.blockExplorers.default.url}/tx/${txData}` : undefined,
})
} else if (txError) {
setTransactionError(txError)
Add(`Transaction failed: ${txError.cause}`, {
addNotification(`Transaction failed: ${txError.cause}`, {
type: 'error',
})
}
}, [txSuccess, txError, txLoading, txData, Add, chain])
}, [txSuccess, txError, txLoading, txData, addNotification, chain])

const resetTransactionState = () => {
setIsTransactionLoading(false)
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/services/contract/contractService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ export async function readContract<T>(
// If we have retries left, wait and try again
if (retries > 0) {
// Exponential backoff: wait longer on each retry
const delay = (4 - retries) * 1000 // 1s, 2s, 3s
await new Promise((resolve) => setTimeout(resolve, delay))
const retryDelayMs = (4 - retries) * 1000 // 1s, 2s, 3s
await new Promise((resolve) => setTimeout(resolve, retryDelayMs))

if (process.env.NODE_ENV === 'development') {
console.warn(`Contract read failed, retrying... (${retries} attempts remaining)`)
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat/test/TicketContract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from 'chai'
import hre from 'hardhat'

describe('TicketContract', function () {
async function deployMessageFixture() {
async function deployTicketContractFixture() {
const ethers = hre.ethers
const [contractOwner, alice, bob] = await ethers.getSigners()

Expand All @@ -15,7 +15,7 @@ describe('TicketContract', function () {

describe('Deployment', function () {
it('Should have correct default owner', async function () {
const { ticketContract, contractOwner } = await loadFixture(deployMessageFixture)
const { ticketContract, contractOwner } = await loadFixture(deployTicketContractFixture)

const owner = await ticketContract.owner()
expect(owner).to.equal(contractOwner.address)
Expand Down