diff --git a/.changeset/two-coats-destroy.md b/.changeset/two-coats-destroy.md new file mode 100644 index 0000000000..72ed6efc6f --- /dev/null +++ b/.changeset/two-coats-destroy.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/explorer": patch +--- + +For the hosted Explorer, transactions are now preloaded, with infinite loading available for previous transactions. diff --git a/packages/explorer/package.json b/packages/explorer/package.json index 3dee0f4f51..b4b343d6a1 100644 --- a/packages/explorer/package.json +++ b/packages/explorer/package.json @@ -75,6 +75,7 @@ "react": "^18", "react-dom": "^18", "react-hook-form": "^7.52.1", + "react-intersection-observer": "^9.15.1", "react18-json-view": "^0.2.9", "sonner": "^1.5.0", "sql-autocomplete": "^1.1.1", diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx index 3568f6b215..ef8a97dc91 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx @@ -1,13 +1,28 @@ "use client"; import { BoxIcon, CheckCheckIcon, ReceiptTextIcon, UserPenIcon, XIcon } from "lucide-react"; -import React, { useState } from "react"; +import { parseAsString, useQueryState } from "nuqs"; +import React, { useEffect, useMemo, useState } from "react"; +import { useInView } from "react-intersection-observer"; import { ExpandedState, flexRender, getCoreRowModel, getExpandedRowModel, useReactTable } from "@tanstack/react-table"; import { createColumnHelper } from "@tanstack/react-table"; import { Badge } from "../../../../../../components/ui/Badge"; +import { Input } from "../../../../../../components/ui/Input"; import { Skeleton } from "../../../../../../components/ui/Skeleton"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../../../../../components/ui/Table"; +import { + Table, + TableBody, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "../../../../../../components/ui/Table"; import { TruncatedHex } from "../../../../../../components/ui/TruncatedHex"; +import { cn } from "../../../../../../utils"; +import { useChain } from "../../../../hooks/useChain"; +import { useIndexerForChainId } from "../../../../hooks/useIndexerForChainId"; +import { useTransactionsQuery } from "../../../../queries/useTransactionsQuery"; import { BlockExplorerLink } from "./BlockExplorerLink"; import { TimeAgo } from "./TimeAgo"; import { TimingRowHeader } from "./TimingRowHeader"; @@ -16,7 +31,7 @@ import { ObservedTransaction, useMergedTransactions } from "./useMergedTransacti const columnHelper = createColumnHelper(); export const columns = [ - columnHelper.accessor("receipt.blockNumber", { + columnHelper.accessor("blockNumber", { header: "Block", cell: (row) => { const status = row.row.original.status; @@ -101,11 +116,53 @@ export const columns = [ ]; export function TransactionsTable() { + const { ref, inView } = useInView(); + const { id: chainId } = useChain(); + const indexer = useIndexerForChainId(chainId); const transactions = useMergedTransactions(); + const { data: indexedTransactions, fetchNextPage } = useTransactionsQuery(); + const loadedInitialTransactions = Array.isArray(indexedTransactions) && indexedTransactions.length > 0; const [expanded, setExpanded] = useState({}); + const [blockNumberFilter, setBlockNumberFilter] = useQueryState("blockNumber", parseAsString.withDefault("")); + const [fromFilter, setFromFilter] = useQueryState("from", parseAsString.withDefault("")); + const [callsFilter, setCallsFilter] = useQueryState("calls", parseAsString.withDefault("")); + const [hashFilter, setHashFilter] = useQueryState("hash", parseAsString.withDefault("")); + const [timestampFilter, setTimestampFilter] = useQueryState("timestamp", parseAsString.withDefault("")); + + useEffect(() => { + if (inView) { + fetchNextPage(); + } + }, [fetchNextPage, inView]); + + // Filter transactions based on filter values + const filteredTransactions = useMemo(() => { + return transactions.filter((transaction) => { + if (blockNumberFilter && !transaction.blockNumber?.toString().includes(blockNumberFilter)) { + return false; + } + if (fromFilter && !transaction.from?.toLowerCase().includes(fromFilter.toLowerCase())) { + return false; + } + if ( + callsFilter && + !transaction.calls?.some((call) => call.functionName?.toLowerCase().includes(callsFilter.toLowerCase())) + ) { + return false; + } + if (hashFilter && !transaction.hash?.toLowerCase().includes(hashFilter.toLowerCase())) { + return false; + } + if (timestampFilter && !transaction.timestamp?.toString().includes(timestampFilter)) { + return false; + } + return true; + }); + }, [transactions, blockNumberFilter, fromFilter, callsFilter, hashFilter, timestampFilter]); + const table = useReactTable({ - data: transactions, + data: filteredTransactions, columns, state: { expanded, @@ -117,34 +174,110 @@ export function TransactionsTable() { }); return ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - - ); +
+ {/* Filter Row */} +
+
+ + setBlockNumberFilter(e.target.value)} + className="h-8 text-xs" + /> +
+
+ + setFromFilter(e.target.value)} + className="h-8 text-xs" + /> +
+
+ + setCallsFilter(e.target.value)} + className="h-8 text-xs" + /> +
+
+ + setHashFilter(e.target.value)} + className="h-8 text-xs" + /> +
+
+ + setTimestampFilter(e.target.value)} + className="h-8 text-xs" + /> +
+
+ +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ) + ) : ( + + +

+ Waiting + for transactions… +

+
+
+ )} +
+ + {indexer.type === "hosted" && ( + - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ) - ) : ( - - -

- Waiting for - transactions… -

-
-
+ > + + +
+ + Loading more transactions... +
+
+
+
)} - -
+ + ); } diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx index 1fcc279f73..2a9e59d6de 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx @@ -3,7 +3,6 @@ import { Address, BaseError, Hash, - Transaction, TransactionReceipt, decodeFunctionData, getAddress, @@ -17,17 +16,22 @@ import { useStore } from "zustand"; import { useCallback, useEffect } from "react"; import { store as observerStore } from "../../../../../../observer/store"; import { useChain } from "../../../../hooks/useChain"; +import { useIndexerForChainId } from "../../../../hooks/useIndexerForChainId"; +import { useTransactionsQuery } from "../../../../queries/useTransactionsQuery"; import { useWorldAbiQuery } from "../../../../queries/useWorldAbiQuery"; import { store as worldStore } from "../store"; import { userOperationEventAbi } from "./abis/userOperationEventAbi"; +import { PartialTransaction } from "./useMergedTransactions"; import { getDecodedUserOperationCalls } from "./utils/getDecodedUserOperationCalls"; export function TransactionsWatcher() { const { id: chainId } = useChain(); const { worldAddress } = useParams<{ worldAddress: Address }>(); + const indexer = useIndexerForChainId(chainId); const wagmiConfig = useConfig(); const { data: worldAbiData } = useWorldAbiQuery(); const abi = worldAbiData?.abi; + const { data: indexedTransactions, error: indexedTransactionsError } = useTransactionsQuery(); const { transactions, setTransaction, updateTransaction } = useStore(worldStore); const observerWrites = useStore(observerStore, (state) => state.writes); @@ -41,10 +45,11 @@ export function TransactionsWatcher() { userOperation, }: { hash: Hash; + blockNumber?: bigint; writeId?: string; timestamp: bigint; receipt: TransactionReceipt; - transaction: Transaction; + transaction: PartialTransaction; userOperation: UserOperation<"0.7">; }) => { if (!abi) return; @@ -73,6 +78,7 @@ export function TransactionsWatcher() { setTransaction({ hash, + blockNumber: receipt.blockNumber, writeId: writeId ?? hash, from: calls[0]?.from ?? transaction.from, timestamp, @@ -93,7 +99,15 @@ export function TransactionsWatcher() { ); const handleUserOperations = useCallback( - async ({ writeId, timestamp, transaction }: { writeId?: string; timestamp: bigint; transaction: Transaction }) => { + async ({ + writeId, + timestamp, + transaction, + }: { + writeId?: string; + timestamp: bigint; + transaction: PartialTransaction; + }) => { if (!abi) return; const hash = transaction.hash; @@ -117,11 +131,13 @@ export function TransactionsWatcher() { hash, timestamp, transaction, + blockNumber, }: { hash: Hash; writeId?: string; timestamp: bigint; - transaction: Transaction; + transaction: PartialTransaction; + blockNumber?: bigint; }) => { if (!abi || !transaction.to) return; @@ -140,6 +156,7 @@ export function TransactionsWatcher() { setTransaction({ hash, + blockNumber, writeId: writeId ?? hash, from: transaction.from, timestamp, @@ -188,6 +205,7 @@ export function TransactionsWatcher() { }); updateTransaction(hash, { + blockNumber: receipt?.blockNumber, receipt, logs, status, @@ -198,14 +216,26 @@ export function TransactionsWatcher() { ); const handleTransaction = useCallback( - async ({ hash, writeId, timestamp }: { hash: Hash; timestamp: bigint; writeId?: string }) => { + async ({ + hash, + writeId, + timestamp, + transaction: initialTransaction, + blockNumber, + }: { + hash: Hash; + timestamp: bigint; + writeId?: string; + transaction?: PartialTransaction; + blockNumber?: bigint; + }) => { if (!abi) return; - const transaction = await getTransaction(wagmiConfig, { hash }); + const transaction = initialTransaction ?? (await getTransaction(wagmiConfig, { hash })); if (transaction.to && getAddress(transaction.to) === getAddress(entryPoint07Address)) { handleUserOperations({ writeId, timestamp, transaction }); } else if (transaction.to && getAddress(transaction.to) === getAddress(worldAddress)) { - handleAuthenticTransaction({ hash, writeId, timestamp, transaction }); + handleAuthenticTransaction({ hash, writeId, timestamp, transaction, blockNumber }); } }, [abi, wagmiConfig, worldAddress, handleUserOperations, handleAuthenticTransaction], @@ -222,6 +252,30 @@ export function TransactionsWatcher() { } }, [handleTransaction, observerWrites, transactions, worldAddress]); + useEffect(() => { + if (indexedTransactions) { + for (const indexedTransaction of indexedTransactions) { + const { tx_hash, tx_signer, tx_to, tx_value, tx_input, block_time, block_num } = indexedTransaction; + const transaction = transactions.find((tx) => tx.hash === tx_hash); + if (!transaction) { + handleTransaction({ + hash: tx_hash, + writeId: tx_hash, + timestamp: BigInt(block_time), + transaction: { + hash: tx_hash, + from: tx_signer, + to: tx_to, + value: tx_value, + input: tx_input, + }, + blockNumber: BigInt(block_num), + }); + } + } + } + }, [abi, indexedTransactions, chainId, handleTransaction, setTransaction, transactions, wagmiConfig]); + useWatchBlocks({ chainId, async onBlock(block) { @@ -234,6 +288,7 @@ export function TransactionsWatcher() { handleTransaction({ hash, timestamp: block.timestamp }); } }, + enabled: indexer.type === "sqlite" || !!indexedTransactionsError, }); return null; diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useMergedTransactions.ts b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useMergedTransactions.ts index 10344a89a7..49bcbcc2aa 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useMergedTransactions.ts +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useMergedTransactions.ts @@ -14,12 +14,15 @@ export type DecodedUserOperationCall = { value?: bigint; }; +export type PartialTransaction = Pick; + export type ObservedTransaction = { writeId: string; hash?: Hex; + blockNumber?: bigint; from?: Address; timestamp?: bigint; - transaction?: Transaction; + transaction?: PartialTransaction; calls: DecodedUserOperationCall[]; value?: bigint; receipt?: TransactionReceipt; diff --git a/packages/explorer/src/app/(explorer)/api/transactions/route.ts b/packages/explorer/src/app/(explorer)/api/transactions/route.ts new file mode 100644 index 0000000000..3427dde64c --- /dev/null +++ b/packages/explorer/src/app/(explorer)/api/transactions/route.ts @@ -0,0 +1,52 @@ +import { Client } from "pg"; +import { Hex } from "viem"; +import { entryPoint07Address } from "viem/account-abstraction"; + +export const dynamic = "force-dynamic"; + +const postgresConnectionUrl = process.env.SHOVEL_DATABASE_URL; + +export async function GET(req: Request) { + const client = new Client({ connectionString: postgresConnectionUrl }); + const { searchParams } = new URL(req.url); + const worldAddress = searchParams.get("worldAddress") as Hex; + const pageSize = Number(searchParams.get("pageSize")) || 30; + const lastBlockNumber = searchParams.get("lastBlockNumber") as Hex; + + if (!worldAddress) { + return Response.json({ error: "Missing worldAddress" }, { status: 400 }); + } + + try { + await client.connect(); + const transactions = await client.query( + ` + SELECT + block_num, + '0x' || encode(tx_hash, 'hex') as tx_hash, + '0x' || encode(tx_to, 'hex') as tx_to, + '0x' || encode(tx_signer, 'hex') as tx_signer, + '0x' || encode(tx_input, 'hex') as tx_input, + tx_value, block_time + FROM transactions + WHERE tx_to IN (decode($1, 'hex'), decode($2, 'hex')) + ${lastBlockNumber ? "AND block_num < $4" : ""} + ORDER BY block_num DESC + LIMIT $3 + `, + [ + worldAddress.replace("0x", ""), + entryPoint07Address.replace("0x", ""), + pageSize, + ...(lastBlockNumber ? [lastBlockNumber] : []), + ], + ); + + return Response.json({ transactions: transactions.rows }); + } catch (error) { + console.error("Database error:", error); + return Response.json({ error: "Failed to fetch transactions" }, { status: 500 }); + } finally { + await client.end(); + } +} diff --git a/packages/explorer/src/app/(explorer)/queries/useTransactionsQuery.ts b/packages/explorer/src/app/(explorer)/queries/useTransactionsQuery.ts new file mode 100644 index 0000000000..e2f17c0cfe --- /dev/null +++ b/packages/explorer/src/app/(explorer)/queries/useTransactionsQuery.ts @@ -0,0 +1,44 @@ +import { useParams } from "next/navigation"; +import { Hex } from "viem"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useChain } from "../hooks/useChain"; +import { useIndexerForChainId } from "../hooks/useIndexerForChainId"; + +export function useTransactionsQuery() { + const { worldAddress, chainName } = useParams(); + const { id: chainId } = useChain(); + const indexer = useIndexerForChainId(chainId); + + return useInfiniteQuery({ + queryKey: ["transactions", worldAddress, chainName], + queryFn: async ({ pageParam = 0 }) => { + const response = await fetch( + `/api/transactions?${new URLSearchParams({ + worldAddress: worldAddress as Hex, + ...(pageParam ? { lastBlockNumber: pageParam.toString() } : {}), + })}`, + { + method: "GET", + }, + ); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.msg || "Network response was not ok"); + } + + return data; + }, + + initialPageParam: 0, + getNextPageParam: (lastPage) => { + if (!lastPage?.transactions?.length) return null; + const lastTransaction = lastPage.transactions[lastPage.transactions.length - 1]; + return lastTransaction?.block_num ?? null; + }, + select: (data) => data.pages[data.pages.length - 1].transactions, + retry: false, + enabled: indexer.type === "hosted", + refetchInterval: (query) => (!query.state.error ? 2000 : false), + }); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db740ef93f..c6d1e83b9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -446,7 +446,7 @@ importers: version: 2.20.2(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8) connectkit: specifier: ^1.9.0 - version: 1.9.0(@babel/core@7.27.3)(@tanstack/react-query@5.56.2(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.5(@tanstack/query-core@5.56.2)(@tanstack/react-query@5.56.2(react@18.2.0))(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + version: 1.9.0(@babel/core@7.25.2)(@tanstack/react-query@5.56.2(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.5(@tanstack/query-core@5.56.2)(@tanstack/react-query@5.56.2(react@18.2.0))(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) debug: specifier: ^4.3.4 version: 4.3.7 @@ -655,6 +655,9 @@ importers: react-hook-form: specifier: ^7.52.1 version: 7.52.2(react@18.2.0) + react-intersection-observer: + specifier: ^9.15.1 + version: 9.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react18-json-view: specifier: ^0.2.9 version: 0.2.9(react@18.2.0) @@ -903,10 +906,10 @@ importers: version: 8.3.4 jest: specifier: ^29.3.1 - version: 29.5.0(@types/node@22.15.24) + version: 29.5.0(@types/node@20.17.16) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.27.3)(@jest/types@29.6.3)(babel-jest@29.5.0(@babel/core@7.27.3))(esbuild@0.23.1)(jest@29.5.0(@types/node@22.15.24))(typescript@5.4.2) + version: 29.0.5(@babel/core@7.25.7)(@jest/types@29.6.3)(babel-jest@29.5.0(@babel/core@7.25.7))(esbuild@0.23.1)(jest@29.5.0(@types/node@20.17.16))(typescript@5.4.2) type-fest: specifier: ^2.14.0 version: 2.14.0 @@ -1292,10 +1295,10 @@ importers: version: 27.4.1 jest: specifier: ^29.3.1 - version: 29.5.0(@types/node@20.17.16) + version: 29.5.0(@types/node@22.15.24) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.25.7)(@jest/types@29.6.3)(babel-jest@29.5.0(@babel/core@7.25.7))(esbuild@0.23.1)(jest@29.5.0(@types/node@20.17.16))(typescript@5.4.2) + version: 29.0.5(@babel/core@7.27.3)(@jest/types@29.6.3)(babel-jest@29.5.0(@babel/core@7.27.3))(esbuild@0.23.1)(jest@29.5.0(@types/node@22.15.24))(typescript@5.4.2) packages/vite-plugin-mud: devDependencies: @@ -2128,10 +2131,6 @@ packages: resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.25.7': - resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.3': resolution: {integrity: sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ==} engines: {node: '>=6.9.0'} @@ -5369,6 +5368,9 @@ packages: '@types/node@22.15.24': resolution: {integrity: sha512-w9CZGm9RDjzTh/D+hFwlBJ3ziUaVw7oufKA3vOFSOZlzmW9AkZnfjPb+DLnrV6qtgL/LNmP0/2zBNCFHL3F0ng==} + '@types/node@22.7.4': + resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} + '@types/openurl@1.0.0': resolution: {integrity: sha512-fUHH4T8FmEl3NBtGbUYYzMo1Ev47uVCVEGVjVNjorOMzgjls6zH82yr/zqkkcEOHY2HUC5PZ8dRFwGed/NR7wQ==} @@ -9836,6 +9838,15 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-intersection-observer@9.16.0: + resolution: {integrity: sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react-dom: + optional: true + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -12201,7 +12212,8 @@ snapshots: '@babel/compat-data@7.25.7': {} - '@babel/compat-data@7.27.3': {} + '@babel/compat-data@7.27.3': + optional: true '@babel/core@7.25.2': dependencies: @@ -12234,7 +12246,7 @@ snapshots: '@babel/parser': 7.25.7 '@babel/template': 7.25.7 '@babel/traverse': 7.27.3(supports-color@5.5.0) - '@babel/types': 7.25.7 + '@babel/types': 7.27.3 convert-source-map: 2.0.0 debug: 4.4.1(supports-color@5.5.0) gensync: 1.0.0-beta.2 @@ -12262,6 +12274,7 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color + optional: true '@babel/generator@7.17.7': dependencies: @@ -12285,7 +12298,7 @@ snapshots: '@babel/generator@7.25.7': dependencies: - '@babel/types': 7.25.7 + '@babel/types': 7.27.3 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 @@ -12325,6 +12338,7 @@ snapshots: browserslist: 4.25.0 lru-cache: 5.1.1 semver: 6.3.1 + optional: true '@babel/helper-environment-visitor@7.24.7': dependencies: @@ -12341,7 +12355,7 @@ snapshots: '@babel/helper-module-imports@7.24.7': dependencies: - '@babel/traverse': 7.25.6 + '@babel/traverse': 7.27.3(supports-color@5.5.0) '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -12381,6 +12395,7 @@ snapshots: '@babel/traverse': 7.27.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color + optional: true '@babel/helper-plugin-utils@7.24.8': {} @@ -12390,7 +12405,7 @@ snapshots: '@babel/helper-simple-access@7.24.7': dependencies: - '@babel/traverse': 7.25.6 + '@babel/traverse': 7.27.3(supports-color@5.5.0) '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -12426,7 +12441,8 @@ snapshots: '@babel/helper-validator-option@7.25.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.27.1': + optional: true '@babel/helpers@7.25.6': dependencies: @@ -12442,6 +12458,7 @@ snapshots: dependencies: '@babel/template': 7.27.2 '@babel/types': 7.27.3 + optional: true '@babel/highlight@7.24.7': dependencies: @@ -12537,9 +12554,9 @@ snapshots: '@babel/core': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.3)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.27.3 + '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.7)': @@ -12689,18 +12706,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/traverse@7.25.7': - dependencies: - '@babel/code-frame': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/template': 7.25.7 - '@babel/types': 7.25.7 - debug: 4.4.1(supports-color@5.5.0) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - '@babel/traverse@7.27.3(supports-color@5.5.0)': dependencies: '@babel/code-frame': 7.27.1 @@ -13524,7 +13529,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.17.16 + '@types/node': 22.7.4 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -16570,6 +16575,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.7.4': + dependencies: + undici-types: 6.19.8 + '@types/openurl@1.0.0': dependencies: '@types/node': 18.19.50 @@ -18201,14 +18210,14 @@ snapshots: '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.18.3 - babel-plugin-styled-components@2.1.4(@babel/core@7.27.3)(styled-components@5.3.11(@babel/core@7.27.3)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0))(supports-color@5.5.0): + babel-plugin-styled-components@2.1.4(@babel/core@7.25.2)(styled-components@5.3.11(@babel/core@7.25.2)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0))(supports-color@5.5.0): dependencies: '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.3) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.25.2) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.11(@babel/core@7.27.3)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.25.2)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@babel/core' - supports-color @@ -18344,6 +18353,7 @@ snapshots: electron-to-chromium: 1.5.161 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.0) + optional: true bs-logger@0.2.6: dependencies: @@ -18458,7 +18468,8 @@ snapshots: caniuse-lite@1.0.30001667: {} - caniuse-lite@1.0.30001720: {} + caniuse-lite@1.0.30001720: + optional: true chai@5.1.1: dependencies: @@ -18676,7 +18687,7 @@ snapshots: confbox@0.1.7: {} - connectkit@1.9.0(@babel/core@7.27.3)(@tanstack/react-query@5.56.2(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.5(@tanstack/query-core@5.56.2)(@tanstack/react-query@5.56.2(react@18.2.0))(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + connectkit@1.9.0(@babel/core@7.25.2)(@tanstack/react-query@5.56.2(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.5(@tanstack/query-core@5.56.2)(@tanstack/react-query@5.56.2(react@18.2.0))(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): dependencies: '@tanstack/react-query': 5.56.2(react@18.2.0) buffer: 6.0.3 @@ -18689,7 +18700,7 @@ snapshots: react-transition-state: 1.1.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-use-measure: 2.1.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) resize-observer-polyfill: 1.5.1 - styled-components: 5.3.11(@babel/core@7.27.3)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.25.2)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0) viem: 2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8) wagmi: 2.15.5(@tanstack/query-core@5.56.2)(@tanstack/react-query@5.56.2(react@18.2.0))(@types/react@18.2.22)(@upstash/redis@1.34.9)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) transitivePeerDependencies: @@ -19044,7 +19055,8 @@ snapshots: electron-to-chromium@1.5.13: {} - electron-to-chromium@1.5.161: {} + electron-to-chromium@1.5.161: + optional: true electron-to-chromium@1.5.32: {} @@ -19367,7 +19379,7 @@ snapshots: debug: 4.4.0 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 @@ -19379,7 +19391,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -19400,7 +19412,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -20951,7 +20963,7 @@ snapshots: '@babel/generator': 7.25.7 '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.7) - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.27.3(supports-color@5.5.0) '@babel/types': 7.25.7 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 @@ -20976,7 +20988,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.16 + '@types/node': 22.7.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -21684,7 +21696,8 @@ snapshots: node-releases@2.0.18: {} - node-releases@2.0.19: {} + node-releases@2.0.19: + optional: true node-sql-parser@5.3.3: dependencies: @@ -22504,6 +22517,12 @@ snapshots: dependencies: react: 18.2.0 + react-intersection-observer@9.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + optionalDependencies: + react-dom: 18.2.0(react@18.2.0) + react-is@16.13.1: {} react-is@17.0.2: {} @@ -22565,7 +22584,7 @@ snapshots: react: 18.2.0 react-remove-scroll-bar: 2.3.6(@types/react@18.2.22)(react@18.2.0) react-style-singleton: 2.2.1(@types/react@18.2.22)(react@18.2.0) - tslib: 2.7.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(@types/react@18.2.22)(react@18.2.0) use-sidecar: 1.1.2(@types/react@18.2.22)(react@18.2.0) optionalDependencies: @@ -23344,14 +23363,14 @@ snapshots: hey-listen: 1.0.8 tslib: 2.8.1 - styled-components@5.3.11(@babel/core@7.27.3)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0): + styled-components@5.3.11(@babel/core@7.25.2)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0): dependencies: '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) '@babel/traverse': 7.27.3(supports-color@5.5.0) '@emotion/is-prop-valid': 1.3.1 '@emotion/stylis': 0.8.5 '@emotion/unitless': 0.7.5 - babel-plugin-styled-components: 2.1.4(@babel/core@7.27.3)(styled-components@5.3.11(@babel/core@7.27.3)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0))(supports-color@5.5.0) + babel-plugin-styled-components: 2.1.4(@babel/core@7.25.2)(styled-components@5.3.11(@babel/core@7.25.2)(react-dom@18.2.0(react@18.2.0))(react-is@18.2.0)(react@18.2.0))(supports-color@5.5.0) css-to-react-native: 3.2.0 hoist-non-react-statics: 3.3.2 react: 18.2.0 @@ -23946,6 +23965,7 @@ snapshots: browserslist: 4.25.0 escalade: 3.2.0 picocolors: 1.1.1 + optional: true uqr@0.1.2: {}