Skip to content

Commit

Permalink
πŸ‘©β€πŸš’ Add bids provider (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
b-tarczynski authored Apr 16, 2024
1 parent 5800c37 commit 1223af1
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 22 deletions.
6 changes: 6 additions & 0 deletions packages/frontend/src/blockchain/auctionAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ export const AUCTION_ADDRESSES: Record<SupportedChainId, Hex> = {
[arbitrumSepolia.id]: '0xF53d383525117d1f51BF234966E39bD1508a5948',
[hardhat.id]: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
}

export const DEPLOYMENT_BLOCK: Record<SupportedChainId, bigint> = {
[arbitrum.id]: BigInt(16977962),
[arbitrumSepolia.id]: BigInt(33877486),
[hardhat.id]: BigInt(0),
}
18 changes: 18 additions & 0 deletions packages/frontend/src/config/wagmiConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createConfig, webSocket } from 'wagmi'
import { arbitrum, arbitrumSepolia, hardhat } from 'wagmi/chains'

export const wagmiConfig = createConfig({
chains: [arbitrum, arbitrumSepolia, hardhat],
ssr: true,
transports: {
[arbitrum.id]: webSocket(`wss://arbitrum-mainnet.infura.io/ws/v3/${process.env.NEXT_PUBLIC_INFURA_KEY}`),
[arbitrumSepolia.id]: webSocket(`wss://arbitrum-sepolia.infura.io/ws/v3/${process.env.NEXT_PUBLIC_INFURA_KEY}`),
[hardhat.id]: webSocket('http://127.0.0.1:8545'),
},
})

declare module 'wagmi' {
interface Register {
config: typeof wagmiConfig
}
}
7 changes: 5 additions & 2 deletions packages/frontend/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { GlobalStyles } from '@/styles/GobalStyles'
import type { AppProps } from 'next/app'
import { BlockchainProviders } from '@/providers/wagmi'
import { BidsProvider } from '@/providers/BidsProvider/provider'

export default function App({ Component, pageProps }: AppProps) {
return (
<BlockchainProviders>
<GlobalStyles />
<Component {...pageProps} />
<BidsProvider>
<GlobalStyles />
<Component {...pageProps} />
</BidsProvider>
</BlockchainProviders>
)
}
2 changes: 2 additions & 0 deletions packages/frontend/src/providers/BidsProvider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { useBids, BidsProvider } from './provider'
export type { Bid } from './types'
20 changes: 20 additions & 0 deletions packages/frontend/src/providers/BidsProvider/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createContext, ReactNode, useContext, useReducer } from 'react'
import { Hex } from 'viem'
import { defaultBidsState, reduceBids } from '@/providers/BidsProvider/reduceBids'
import { useWatchEvents } from '@/providers/BidsProvider/useWatchEvents'
import { Bid } from '@/providers/BidsProvider/types'

const BidsContext = createContext({
bids: new Map<Hex, Bid>(),
bidList: new Array<Bid>(),
isLoading: true,
})

export const useBids = () => useContext(BidsContext)

export const BidsProvider = ({ children }: { children: ReactNode }) => {
const [bidsState, updateBids] = useReducer(reduceBids, defaultBidsState)
const { isLoading } = useWatchEvents(updateBids)

return <BidsContext.Provider value={{ ...bidsState, isLoading }}>{children}</BidsContext.Provider>
}
75 changes: 75 additions & 0 deletions packages/frontend/src/providers/BidsProvider/reduceBids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Bid } from '@/providers/BidsProvider/types'
import { Hex } from 'viem'
import { SupportedChainId } from '@/blockchain/chain'

interface BidEvent {
args: {
bidder?: Hex
bidderID?: bigint
bidAmount?: bigint
}
}

interface BidsState {
bids: Map<Hex, Bid>
bidList: Bid[]
startBlock: bigint | undefined
chainId: SupportedChainId | undefined
}

export const defaultBidsState: BidsState = {
bids: new Map<Hex, Bid>(),
bidList: [],
startBlock: undefined,
chainId: undefined,
}

export interface BidEventsState {
events: BidEvent[]
startBlock: bigint | undefined
chainId: SupportedChainId
}

export const reduceBids = (previousState: BidsState, state: BidEventsState): BidsState => {
const { events, startBlock, chainId } = state
const bids = getInitialBids(previousState, state)
events.forEach((event) => handleBid(bids, event.args))

return {
bids,
bidList: Array.from(bids.values()).sort(biggerFirst),
startBlock: startBlock,
chainId: chainId,
}
}

const getInitialBids = (previousState: BidsState, { startBlock, chainId }: BidEventsState) => {
if (startBlock !== previousState.startBlock || chainId !== previousState.chainId) {
return new Map<Hex, Bid>()
}
return previousState.bids
}

const handleBid = (bids: Map<Hex, Bid>, eventArgs: BidEvent['args']) => {
if (!eventArgs.bidder || !eventArgs.bidAmount || !eventArgs.bidderID) {
return
}
const existingBid = bids.get(eventArgs.bidder)
if (existingBid) {
existingBid.amount += eventArgs.bidAmount
bids.set(eventArgs.bidder, existingBid)
return
}
bids.set(eventArgs.bidder, {
address: eventArgs.bidder,
bidderId: eventArgs.bidderID,
amount: eventArgs.bidAmount,
})
}

const biggerFirst = (a: Bid, b: Bid) => {
if (a.amount === b.amount) {
return 0
}
return a.amount > b.amount ? -1 : 1
}
7 changes: 7 additions & 0 deletions packages/frontend/src/providers/BidsProvider/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Hex } from 'viem'

export interface Bid {
address: Hex
amount: bigint
bidderId: bigint
}
46 changes: 46 additions & 0 deletions packages/frontend/src/providers/BidsProvider/useWatchEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { parseAbiItem } from 'viem'
import { BidEventsState } from '@/providers/BidsProvider/reduceBids'
import { useBlockNumber, useChainId, useConfig, useWatchContractEvent } from 'wagmi'
import { useQuery } from '@tanstack/react-query'
import { getLogs } from 'viem/actions'
import { AUCTION_ADDRESSES, DEPLOYMENT_BLOCK } from '@/blockchain/auctionAddresses'
import { AUCTION_ABI } from '@/blockchain/abi/auction'

const newBidEvent = parseAbiItem('event NewBid(address bidder, uint256 bidderID, uint256 bidAmount)')

export const useWatchEvents = (onEvents: (eventsState: BidEventsState) => void) => {
const chainId = useChainId()
const config = useConfig()
const client = config.getClient({ chainId })

const { data: blockNumber, isLoading: isBlockLoading } = useBlockNumber()

const { isLoading: areInitialBidsLoading } = useQuery({
queryKey: ['bids', chainId],
queryFn: async () => {
const logs = await getLogs(client, {
address: AUCTION_ADDRESSES[chainId],
event: newBidEvent,
fromBlock: DEPLOYMENT_BLOCK[chainId],
toBlock: blockNumber,
})
onEvents({ events: logs, chainId, startBlock: blockNumber })
return logs
},
enabled: !isBlockLoading,
staleTime: Infinity,
gcTime: Infinity,
})

useWatchContractEvent({
chainId,
abi: AUCTION_ABI,
address: AUCTION_ADDRESSES[chainId],
fromBlock: blockNumber,
eventName: 'NewBid',
onLogs: (logs) => onEvents({ events: logs, chainId, startBlock: blockNumber }),
enabled: !isBlockLoading && !areInitialBidsLoading,
})

return { isLoading: isBlockLoading || areInitialBidsLoading }
}
22 changes: 3 additions & 19 deletions packages/frontend/src/providers/wagmi.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createConfig, http, WagmiProvider } from 'wagmi'
import { arbitrum, arbitrumSepolia, hardhat } from 'wagmi/chains'
import { WagmiProvider } from 'wagmi'
import { ReactNode } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { wagmiConfig } from '@/config/wagmiConfig'

interface ProviderProps {
children: ReactNode
Expand All @@ -11,24 +11,8 @@ const queryClient = new QueryClient()

export const BlockchainProviders = ({ children }: ProviderProps) => {
return (
<WagmiProvider config={config}>
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</WagmiProvider>
)
}

const config = createConfig({
chains: [arbitrum, arbitrumSepolia, hardhat],
ssr: true,
transports: {
[arbitrum.id]: http(),
[arbitrumSepolia.id]: http(),
[hardhat.id]: http(),
},
})

declare module 'wagmi' {
interface Register {
config: typeof config
}
}
2 changes: 1 addition & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"env": ["NEXT_VOUCHER_REDEEM_DEADLINE"],
"env": ["NEXT_VOUCHER_REDEEM_DEADLINE", "NEXT_PUBLIC_INFURA_KEY"],
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "build/**", "cache"]
},
Expand Down

0 comments on commit 1223af1

Please sign in to comment.