xchange-create
Version:
Create Xchange Project
175 lines (157 loc) • 4.88 kB
text/typescript
import { useCallback, useEffect, useState } from "react"
import {
Block,
createTestClient,
Hash,
publicActions,
Transaction,
TransactionReceipt,
walletActions,
webSocket,
} from "viem"
import { hardhat } from "viem/chains"
import { decodeTransactionData } from "~~/utils/xchange"
const BLOCKS_PER_PAGE = 20
export const testClient = createTestClient({
chain: hardhat,
mode: "hardhat",
transport: webSocket("ws://127.0.0.1:8545"),
})
.extend(publicActions)
.extend(walletActions)
export const useFetchBlocks = () => {
const [blocks, setBlocks] = useState<Block[]>([])
const [transactionReceipts, setTransactionReceipts] = useState<{
[key: string]: TransactionReceipt
}>({})
const [currentPage, setCurrentPage] = useState(0)
const [totalBlocks, setTotalBlocks] = useState(0n)
const [error, setError] = useState<Error | null>(null)
const fetchBlocks = useCallback(async () => {
setError(null)
try {
const blockNumber = await testClient.getBlockNumber()
setTotalBlocks(blockNumber)
const startingBlock = blockNumber - BigInt(currentPage * BLOCKS_PER_PAGE)
const blockNumbersToFetch = Array.from(
{
length: Number(
BLOCKS_PER_PAGE < startingBlock + 1n
? BLOCKS_PER_PAGE
: startingBlock + 1n
),
},
(_, i) => startingBlock - BigInt(i)
)
const blocksWithTransactions = blockNumbersToFetch.map(
async blockNumber => {
try {
return testClient.getBlock({
blockNumber,
includeTransactions: true,
})
} catch (err) {
setError(
err instanceof Error ? err : new Error("An error occurred.")
)
throw err
}
}
)
const fetchedBlocks = await Promise.all(blocksWithTransactions)
fetchedBlocks.forEach(block => {
block.transactions.forEach(tx =>
decodeTransactionData(tx as Transaction)
)
})
const txReceipts = await Promise.all(
fetchedBlocks.flatMap(block =>
block.transactions.map(async tx => {
try {
const receipt = await testClient.getTransactionReceipt({
hash: (tx as Transaction).hash,
})
return { [(tx as Transaction).hash]: receipt }
} catch (err) {
setError(
err instanceof Error ? err : new Error("An error occurred.")
)
throw err
}
})
)
)
setBlocks(fetchedBlocks)
setTransactionReceipts(prevReceipts => ({
...prevReceipts,
...Object.assign({}, ...txReceipts),
}))
} catch (err) {
setError(err instanceof Error ? err : new Error("An error occurred."))
}
}, [currentPage])
useEffect(() => {
fetchBlocks()
}, [fetchBlocks])
useEffect(() => {
const handleNewBlock = async (newBlock: any) => {
try {
if (currentPage === 0) {
if (newBlock.transactions.length > 0) {
const transactionsDetails = await Promise.all(
newBlock.transactions.map((txHash: string) =>
testClient.getTransaction({ hash: txHash as Hash })
)
)
newBlock.transactions = transactionsDetails
}
newBlock.transactions.forEach((tx: Transaction) =>
decodeTransactionData(tx as Transaction)
)
const receipts = await Promise.all(
newBlock.transactions.map(async (tx: Transaction) => {
try {
const receipt = await testClient.getTransactionReceipt({
hash: (tx as Transaction).hash,
})
return { [(tx as Transaction).hash]: receipt }
} catch (err) {
setError(
err instanceof Error
? err
: new Error("An error occurred fetching receipt.")
)
throw err
}
})
)
setBlocks(prevBlocks => [
newBlock,
...prevBlocks.slice(0, BLOCKS_PER_PAGE - 1),
])
setTransactionReceipts(prevReceipts => ({
...prevReceipts,
...Object.assign({}, ...receipts),
}))
}
if (newBlock.number) {
setTotalBlocks(newBlock.number)
}
} catch (err) {
setError(err instanceof Error ? err : new Error("An error occurred."))
}
}
return testClient.watchBlocks({
onBlock: handleNewBlock,
includeTransactions: true,
})
}, [currentPage])
return {
blocks,
transactionReceipts,
currentPage,
totalBlocks,
setCurrentPage,
error,
}
}