diff --git a/.prettierignore b/.prettierignore index 039f1d4a..4ee6753d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,4 +4,5 @@ node_modules .storybook graphql contracts.ts +**/src/generated/** **/rollups-wagmi/src/index.tsx \ No newline at end of file diff --git a/apps/dave/.storybook/preview.tsx b/apps/dave/.storybook/preview.tsx index 663e954b..2652230b 100644 --- a/apps/dave/.storybook/preview.tsx +++ b/apps/dave/.storybook/preview.tsx @@ -1,10 +1,11 @@ -import { MantineProvider } from "@mantine/core"; +import { useMantineColorScheme } from "@mantine/core"; import "@mantine/core/styles.css"; -import { Notifications } from "@mantine/notifications"; import type { Preview, StoryContext, StoryFn } from "@storybook/nextjs"; +import { ReactNode, useCallback, useEffect, useState } from "react"; +import { UPDATE_GLOBALS } from "storybook/internal/core-events"; +import { addons, useGlobals } from 'storybook/preview-api'; import Layout from "../src/components/layout/Layout"; import { Providers } from '../src/providers/Providers'; -import theme from "../src/providers/theme"; import './global.css'; try { @@ -17,6 +18,8 @@ try { console.info((error as Error).message) } +type Globals = ReturnType[0] + const withLayout = (StoryFn: StoryFn, context: StoryContext) => { const { title } = context; const [sectionType] = title.split("/"); @@ -27,22 +30,71 @@ const withLayout = (StoryFn: StoryFn, context: StoryContext) => { return <>{StoryFn(context.args, context)}; }; -const withProviders = (StoryFn: StoryFn, context: StoryContext) => { - return {StoryFn(context.args, context)} +const withProviders = (StoryFn: StoryFn, context: StoryContext) => { + return ( + + + {StoryFn(context.args, context)} + + + ) } -const withMantine = (StoryFn: StoryFn, context: StoryContext) => { - const currentBg = context.globals.backgrounds?.value ?? "light"; +const channel = addons.getChannel(); - return ( - - - {StoryFn(context.args, context)} - - ); -}; +const generateNewBackgroundEvt = (colorScheme: unknown) => ({globals: { backgrounds: {value: colorScheme, grid: false}}}) + +// eslint-disable-next-line react-refresh/only-export-components +function ColorSchemeWrapper({ children, context}: { children: ReactNode, context: StoryContext }) { + const { colorScheme, setColorScheme } = useMantineColorScheme(); + const [latestGlobalsBg, setLatestGlobalBg] = useState(colorScheme); + + const handleColorScheme = useCallback(({ globals }: { globals: Globals }) => { + const bgValue = globals.backgrounds?.value + const newMode = bgValue ?? 'light'; + if(newMode !== colorScheme) { + setColorScheme(newMode); + setLatestGlobalBg(newMode); + } else if (newMode !== latestGlobalsBg) { + setLatestGlobalBg(newMode) + } + // update the handler function every time both variables change + // as the handler is outside of React's detection. We want + // to make sure the handler works with fresh values. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [colorScheme, latestGlobalsBg]); + + + useEffect(() => { + // Only when on story mode i.e. not on autodocs view. + // Due to the many re-renders until its finished. That cause slow but infinite loops. + if(context.viewMode === 'story') { + // on-mount emit single event to sync whatever is the default color-scheme on mantine + channel.emit(UPDATE_GLOBALS, generateNewBackgroundEvt(colorScheme)) + } + }, []) + + useEffect(() => { + channel.on("updateGlobals", handleColorScheme); + return () => { + // unsubscribe to subscribe again with fresher handler. + channel.off("updateGlobals", handleColorScheme); + } + }, [handleColorScheme]); + + useEffect(() => { + if(colorScheme !== latestGlobalsBg) { + console.log('color-scheme new value came from App ui interaction. emitting event to sync.'); + channel.emit(UPDATE_GLOBALS, generateNewBackgroundEvt(colorScheme)); + } + }, [colorScheme, latestGlobalsBg]) + + + + return <>{children}; +} -const preview: Preview = { +const preview: Preview = { initialGlobals: { backgrounds: { value: "light" }, }, @@ -64,10 +116,10 @@ const preview: Preview = { }, }, decorators: [ - // Order matters. So layout decorator first. Fn calling is router(mantine(layout)) - withLayout, + // Order matters. So layout decorator first. Fn calling is providers(layout(Story)) + withLayout, withProviders, - withMantine, + ], }; diff --git a/apps/dave/mantine.d.ts b/apps/dave/mantine.d.ts index c0d8b914..6a41b670 100644 --- a/apps/dave/mantine.d.ts +++ b/apps/dave/mantine.d.ts @@ -8,6 +8,15 @@ type ExtendedCustomColors = | core.DefaultMantineColor; declare module "@mantine/core" { export { core }; + + /** + * Making it optional as default values to the + * declared interface members are added in the theme. + */ + export interface SpoilerProps extends core.SpoilerProps { + hideLabel?: core.SpoilerProps["hideLabel"]; + showLabel?: core.SpoilerProps["showLabel"]; + } export interface MantineThemeOther { lgIconSize: number; mdIconSize: number; diff --git a/apps/dave/package.json b/apps/dave/package.json index 5571904f..f1f8df35 100644 --- a/apps/dave/package.json +++ b/apps/dave/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist && rm -rf src/generated", "dev": "next dev", "build": "next build", "start": "next start", @@ -25,6 +25,7 @@ "@mantine/notifications": "^8.3.13", "@rainbow-me/rainbowkit": "^2.2.10", "@raugfer/jazzicon": "^1.0.6", + "@react-spring/web": "^10.0.1", "@tabler/icons-react": "^3.35.0", "@tanstack/react-query": "catalog:", "@vercel/analytics": "^1.6.1", diff --git a/apps/dave/src/app/apps/[application]/page.tsx b/apps/dave/src/app/apps/[application]/page.tsx new file mode 100644 index 00000000..560b821e --- /dev/null +++ b/apps/dave/src/app/apps/[application]/page.tsx @@ -0,0 +1,6 @@ +import { ApplicationSummaryContainer } from "../../../containers/ApplicationSummaryContainer"; + +export default async function Page(props: PageProps<"/apps/[application]">) { + const { application } = await props.params; + return ; +} diff --git a/apps/dave/src/components/CenteredText.tsx b/apps/dave/src/components/CenteredText.tsx new file mode 100644 index 00000000..9a4021ef --- /dev/null +++ b/apps/dave/src/components/CenteredText.tsx @@ -0,0 +1,28 @@ +import { + Card, + Center, + Text, + type CardProps, + type TextProps, +} from "@mantine/core"; +import type { FC } from "react"; + +interface CenteredTextProps { + text: string; + cardProps?: CardProps; + textProps?: TextProps; +} + +const CenteredText: FC = (props) => { + return ( + +
+ + {props.text} + +
+
+ ); +}; + +export default CenteredText; diff --git a/apps/dave/src/components/QueryPagination.tsx b/apps/dave/src/components/QueryPagination.tsx index 44985289..f742e502 100644 --- a/apps/dave/src/components/QueryPagination.tsx +++ b/apps/dave/src/components/QueryPagination.tsx @@ -1,5 +1,6 @@ import type { Pagination as QPagination } from "@cartesi/viem"; import { Group, Pagination, type GroupProps } from "@mantine/core"; +import { isNil } from "ramda"; import type { FC } from "react"; const getActivePage = (offset: number, limit: number) => { @@ -7,6 +8,11 @@ const getActivePage = (offset: number, limit: number) => { return offset / safeLimit + 1; }; +const getTotalPages = (totalCount: number, limit: number) => { + const denominator = limit === 0 || isNil(limit) ? 1 : limit; + return Math.ceil(totalCount / denominator); +}; + type QueryPaginationProps = { pagination: QPagination; onPaginationChange?: (newOffset: number) => void; @@ -20,11 +26,12 @@ export const QueryPagination: FC = ({ groupProps, hideIfSinglePage = true, }) => { - const totalPages = Math.ceil(pagination.totalCount / pagination.limit); + const totalPages = getTotalPages(pagination.totalCount, pagination.limit); const activePage = getActivePage(pagination.offset, pagination.limit); - const displayPagination = totalPages > 1 && hideIfSinglePage; + const hasNoPages = totalPages === 0; + const isSinglePage = totalPages === 1; - if (!displayPagination) return ""; + if (hasNoPages || (isSinglePage && hideIfSinglePage)) return ""; return ( diff --git a/apps/dave/src/components/SummaryCard.tsx b/apps/dave/src/components/SummaryCard.tsx new file mode 100644 index 00000000..b8716f1c --- /dev/null +++ b/apps/dave/src/components/SummaryCard.tsx @@ -0,0 +1,44 @@ +"use client"; +import { Card, Flex, Skeleton, Text } from "@mantine/core"; +import { type FC } from "react"; +import { type IconType } from "react-icons"; +import TweenedNumber from "./TweenedNumber"; + +export type SummaryCardProps = { + icon?: IconType; + title: string; + value: number; + displaySkeleton: boolean; +}; + +const SummarySkeletonCard = () => ( + + + + + + +); + +export const SummaryCard: FC = (props) => { + if (props.displaySkeleton) return ; + + return ( + + + {props.icon && ( + + )} + + {props.title} + + + + + + + ); +}; diff --git a/apps/dave/src/components/TweenedNumber.tsx b/apps/dave/src/components/TweenedNumber.tsx new file mode 100644 index 00000000..807be092 --- /dev/null +++ b/apps/dave/src/components/TweenedNumber.tsx @@ -0,0 +1,19 @@ +"use client"; +import { animated, useSpring } from "@react-spring/web"; + +interface TweenedNumber { + value: number; +} + +const TweenedNumber = ({ value }: TweenedNumber) => { + const { number } = useSpring({ + from: { number: 0 }, + number: value, + delay: 200, + config: { mass: 1, tension: 20, friction: 10 }, + }); + + return {number.to((n) => n.toFixed(0))}; +}; + +export default TweenedNumber; diff --git a/apps/dave/src/components/application/ApplicationCard.tsx b/apps/dave/src/components/application/ApplicationCard.tsx index 59cdce40..18bcc74d 100644 --- a/apps/dave/src/components/application/ApplicationCard.tsx +++ b/apps/dave/src/components/application/ApplicationCard.tsx @@ -26,7 +26,7 @@ export const ApplicationCard: FC = ({ application }) => { application; const stateColour = getStateColour(state); const appConfig = useAppConfig(); - const url = pathBuilder.epochs({ application: application.name }); + const url = pathBuilder.application({ application: application.name }); const inputsLabel = processedInputs === 0n ? "no inputs" diff --git a/apps/dave/src/components/epoch/EpochCard.tsx b/apps/dave/src/components/epoch/EpochCard.tsx index fc8f50f3..b8eab2a0 100644 --- a/apps/dave/src/components/epoch/EpochCard.tsx +++ b/apps/dave/src/components/epoch/EpochCard.tsx @@ -2,15 +2,20 @@ import type { Epoch } from "@cartesi/viem"; import { Badge, Card, Group, Text, useMantineTheme } from "@mantine/core"; import { useMediaQuery } from "@mantine/hooks"; import Link from "next/link"; +import { useParams } from "next/navigation"; import type { FC } from "react"; +import { pathBuilder } from "../../routes/routePathBuilder"; import { useEpochStatusColor } from "./useEpochStatusColor"; type Props = { epoch: Epoch }; export const EpochCard: FC = ({ epoch }) => { const theme = useMantineTheme(); - const epochIndex = epoch.index?.toString() ?? "0"; - const url = `epochs/${epochIndex}`; + const params = useParams<{ application: string }>(); + const url = pathBuilder.epoch({ + application: params.application, + epochIndex: epoch.index, + }); const isMobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`); const color = useEpochStatusColor(epoch); const inDispute = false; // XXX: how to know if an epoch is in dispute? diff --git a/apps/dave/src/components/input/InputCard.tsx b/apps/dave/src/components/input/InputCard.tsx index a64a2edd..7d469d86 100644 --- a/apps/dave/src/components/input/InputCard.tsx +++ b/apps/dave/src/components/input/InputCard.tsx @@ -10,6 +10,7 @@ import { Stack, Text, Tooltip, + useMantineTheme, type MantineColor, } from "@mantine/core"; import { Activity, useMemo, useState, type FC } from "react"; @@ -41,9 +42,9 @@ const getStatusColor = (status: InputStatus): MantineColor => { type ViewControl = "payload" | "output" | "report"; const maxHeight = 450; -const iconSize = 21; // TODO: Define what else will be inside like payload (decoding etc) export const InputCard: FC = ({ input }) => { + const theme = useMantineTheme(); const statusColor = useRightColorShade(getStatusColor(input.status)); const [viewControl, setViewControl] = useState("payload"); const [decoderType, setDecoderType] = useState("raw"); @@ -54,7 +55,12 @@ export const InputCard: FC = ({ input }) => { -
+
# {input.index} = ({ input }) => { - + {decoderFn(input.decodedData.payload)} @@ -144,7 +146,7 @@ export const InputCard: FC = ({ input }) => { - + = ({ inputs }) => { return ( - + {inputs.map((input) => ( ))} diff --git a/apps/dave/src/components/layout/PageTitle.tsx b/apps/dave/src/components/layout/PageTitle.tsx index 458b39bd..1602cfb0 100644 --- a/apps/dave/src/components/layout/PageTitle.tsx +++ b/apps/dave/src/components/layout/PageTitle.tsx @@ -10,8 +10,8 @@ const PageTitle: FC = ({ title, Icon }) => { const theme = useMantineTheme(); return ( - - {title} + + {title} ); }; diff --git a/apps/dave/src/components/navigation/Hierarchy.tsx b/apps/dave/src/components/navigation/Hierarchy.tsx index cfebbd7d..b211bb88 100644 --- a/apps/dave/src/components/navigation/Hierarchy.tsx +++ b/apps/dave/src/components/navigation/Hierarchy.tsx @@ -124,7 +124,7 @@ export const Hierarchy: FC = ({ px={0} w={"100%"} pos="sticky" - top="calc(var(--app-shell-header-height) - 3px)" + top="calc(var(--app-shell-header-height))" style={{ zIndex: theme.other.zIndexXS }} > {showShortForm ? ( diff --git a/apps/dave/src/components/output/OutputList.tsx b/apps/dave/src/components/output/OutputList.tsx index 869bd333..252f2704 100644 --- a/apps/dave/src/components/output/OutputList.tsx +++ b/apps/dave/src/components/output/OutputList.tsx @@ -1,52 +1,29 @@ -import type { - Output as OutputReturn, - Pagination as QueryPagination, -} from "@cartesi/viem"; -import { Group, Pagination, Stack } from "@mantine/core"; -import { Activity, type FC } from "react"; +import type { Output, Pagination } from "@cartesi/viem"; +import { Stack } from "@mantine/core"; +import { type FC } from "react"; +import { QueryPagination } from "../QueryPagination"; import type { DecoderType } from "../types"; import { OutputView } from "./OutputView"; type OutputListProps = { decoderType?: DecoderType; - outputs: OutputReturn[]; - pagination: QueryPagination; + outputs: Output[]; + pagination: Pagination; onPaginationChange?: (newOffset: number) => void; }; -const getActivePage = (offset: number, limit: number) => { - const safeLimit = limit === 0 ? 1 : limit; - return offset / safeLimit + 1; -}; - export const OutputList: FC = ({ outputs, pagination, decoderType = "raw", onPaginationChange, }) => { - const totalPages = Math.ceil(pagination.totalCount / pagination.limit); - const activePage = getActivePage(pagination.offset, pagination.limit); - const hasMoreThanOnePage = totalPages > 1; - return ( - - - { - if (newPageNumber !== activePage) { - onPaginationChange?.( - newPageNumber * pagination.limit - - pagination.limit, - ); - } - }} - /> - - + {outputs.map((output) => ( = ({ decodedData, decoderType }) => { return (
- + {decoderFn(decodedData.payload)} diff --git a/apps/dave/src/components/report/ReportView.tsx b/apps/dave/src/components/report/ReportView.tsx index 147743f0..59367596 100644 --- a/apps/dave/src/components/report/ReportView.tsx +++ b/apps/dave/src/components/report/ReportView.tsx @@ -24,7 +24,7 @@ export const ReportView: FC = ({ return (
- + {decoderFn(report.rawData)} diff --git a/apps/dave/src/components/tournament/TournamentView.stories.ts b/apps/dave/src/components/tournament/TournamentView.stories.ts index 43b538d4..6ddde9ab 100644 --- a/apps/dave/src/components/tournament/TournamentView.stories.ts +++ b/apps/dave/src/components/tournament/TournamentView.stories.ts @@ -195,7 +195,52 @@ export const Finalized: Story = { export const MidLevelDispute: Story = { args: { - commitments: [], + commitments: [ + { + blockNumber: 1n, + commitment: claim(7).hash, + createdAt: new Date(timestamp), + epochIndex, + tournamentAddress, + finalStateHash: zeroHash, + submitterAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + txHash: "0x06ad8f0ce427010498fbb2388b432f6d578e4e1ffe5dbf20869629b09dcf0d70", + updatedAt: new Date(timestamp), + }, + { + blockNumber: 1n, + commitment: claim(8).hash, + createdAt: new Date(timestamp), + epochIndex, + tournamentAddress, + finalStateHash: zeroHash, + submitterAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + txHash: "0x06ad8f0ce427010498fbb2388b432f6d578e4e1ffe5dbf20869629b09dcf0d70", + updatedAt: new Date(timestamp), + }, + { + blockNumber: 1n, + commitment: claim(9).hash, + createdAt: new Date(timestamp), + epochIndex, + tournamentAddress, + finalStateHash: zeroHash, + submitterAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + txHash: "0x06ad8f0ce427010498fbb2388b432f6d578e4e1ffe5dbf20869629b09dcf0d70", + updatedAt: new Date(timestamp), + }, + { + blockNumber: 1n, + commitment: claim(10).hash, + createdAt: new Date(timestamp), + epochIndex, + tournamentAddress, + finalStateHash: zeroHash, + submitterAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + txHash: "0x06ad8f0ce427010498fbb2388b432f6d578e4e1ffe5dbf20869629b09dcf0d70", + updatedAt: new Date(timestamp), + }, + ], tournament: { address: "0x61bcab9d0d8b554009824292d2d6855dfa3aab86", createdAt: new Date(timestamp), diff --git a/apps/dave/src/containers/ApplicationSummaryContainer.tsx b/apps/dave/src/containers/ApplicationSummaryContainer.tsx new file mode 100644 index 00000000..47c8307c --- /dev/null +++ b/apps/dave/src/containers/ApplicationSummaryContainer.tsx @@ -0,0 +1,102 @@ +"use client"; +import { + useEpochs, + useInputs, + useOutputs, + useReports, + useTournaments, +} from "@cartesi/wagmi"; +import type { FC } from "react"; +import { + Hierarchy, + type HierarchyConfig, +} from "../components/navigation/Hierarchy"; +import { ApplicationSummaryPage } from "../page/ApplicationSummaryPage"; +import { pathBuilder } from "../routes/routePathBuilder"; +import ContainerStack from "./ContainerStack"; + +interface ApplicationSummaryContainerProps { + application: string; +} + +const defaultParams = { + limit: 2, + descending: true, +}; + +export const ApplicationSummaryContainer: FC< + ApplicationSummaryContainerProps +> = (props) => { + const epochsResult = useEpochs({ + application: props.application, + ...defaultParams, + }); + const reportsResult = useReports({ + application: props.application, + ...defaultParams, + }); + const outputsResult = useOutputs({ + application: props.application, + ...defaultParams, + }); + + const inputsResult = useInputs({ + application: props.application, + ...defaultParams, + }); + + const tournamentsResult = useTournaments({ + application: props.application, + level: 0n, + ...defaultParams, + }); + + const epochs = { + data: epochsResult.data?.data ?? [], + totalCount: epochsResult.data?.pagination.totalCount ?? 0, + isLoading: epochsResult.isLoading, + }; + + const reports = { + totalCount: reportsResult.data?.pagination.totalCount ?? 0, + isLoading: reportsResult.isLoading, + }; + + const outputs = { + totalCount: outputsResult.data?.pagination.totalCount ?? 0, + isLoading: outputsResult.isLoading, + }; + + const inputs = { + data: inputsResult.data?.data ?? [], + totalCount: inputsResult.data?.pagination.totalCount ?? 0, + isLoading: inputsResult.isLoading, + }; + const tournaments = { + data: tournamentsResult.data?.data ?? [], + totalCount: tournamentsResult.data?.pagination.totalCount ?? 0, + isLoading: tournamentsResult.isLoading, + }; + + const hierarchyConfig: HierarchyConfig[] = [ + { title: "Home", href: "/" }, + { + title: props.application, + href: pathBuilder.application({ application: props.application }), + }, + ]; + + return ( + + + + + ); +}; diff --git a/apps/dave/src/containers/ApplicationsContainer.tsx b/apps/dave/src/containers/ApplicationsContainer.tsx index e36ad5fc..49bbbce3 100644 --- a/apps/dave/src/containers/ApplicationsContainer.tsx +++ b/apps/dave/src/containers/ApplicationsContainer.tsx @@ -1,7 +1,6 @@ "use client"; import { useApplications } from "@cartesi/wagmi"; -import { Stack } from "@mantine/core"; import type { FC } from "react"; import { Hierarchy, @@ -9,6 +8,7 @@ import { } from "../components/navigation/Hierarchy"; import { ApplicationsPage } from "../page/ApplicationsPage"; import { ContainerSkeleton } from "./ContainerSkeleton"; +import ContainerStack from "./ContainerStack"; export type HomeContainerProps = { descending?: boolean; @@ -22,7 +22,7 @@ export const ApplicationsContainer: FC = (props) => { const applications = data?.data ?? []; return ( - + {isLoading ? ( @@ -33,6 +33,6 @@ export const ApplicationsContainer: FC = (props) => { pagination={data?.pagination} /> )} - + ); }; diff --git a/apps/dave/src/containers/ContainerSkeleton.tsx b/apps/dave/src/containers/ContainerSkeleton.tsx index 0c7aa353..435808a6 100644 --- a/apps/dave/src/containers/ContainerSkeleton.tsx +++ b/apps/dave/src/containers/ContainerSkeleton.tsx @@ -4,7 +4,7 @@ export const ContainerSkeleton = () => { const repeat = Array.from({ length: 4 }); return ( - + diff --git a/apps/dave/src/containers/ContainerStack.tsx b/apps/dave/src/containers/ContainerStack.tsx new file mode 100644 index 00000000..660cc1b8 --- /dev/null +++ b/apps/dave/src/containers/ContainerStack.tsx @@ -0,0 +1,18 @@ +import { Stack, type StackProps } from "@mantine/core"; +import type { FC } from "react"; + +/** + * All container has a stack but the padding. margin and gaps were slightly different + * showing a bit of difference between page navigation. the goal is to all containers + * to use this Stack, just a still configurable Stack with sensible defaults for containers. + * @param props + */ +const ContainerStack: FC = (props) => { + return ( + + {props.children} + + ); +}; + +export default ContainerStack; diff --git a/apps/dave/src/containers/EpochContainer.tsx b/apps/dave/src/containers/EpochContainer.tsx index 2c972732..b0eb53c9 100644 --- a/apps/dave/src/containers/EpochContainer.tsx +++ b/apps/dave/src/containers/EpochContainer.tsx @@ -1,6 +1,5 @@ "use client"; import { useEpoch, useInputs } from "@cartesi/wagmi"; -import { Stack } from "@mantine/core"; import { notFound } from "next/navigation"; import { type FC } from "react"; import { @@ -10,6 +9,7 @@ import { import { EpochPage } from "../page/EpochPage"; import { pathBuilder } from "../routes/routePathBuilder"; import { ContainerSkeleton } from "./ContainerSkeleton"; +import ContainerStack from "./ContainerStack"; export type EpochContainerProps = { application: string; @@ -29,6 +29,10 @@ export const EpochContainer: FC = (props) => { { title: "Home", href: "/" }, { title: props.application, + href: pathBuilder.application(props), + }, + { + title: "epochs", href: pathBuilder.epochs(props), }, { @@ -42,7 +46,7 @@ export const EpochContainer: FC = (props) => { } return ( - + {isLoading && } {!!epoch && ( @@ -52,6 +56,6 @@ export const EpochContainer: FC = (props) => { pagination={inputs?.pagination} /> )} - + ); }; diff --git a/apps/dave/src/containers/EpochsContainer.tsx b/apps/dave/src/containers/EpochsContainer.tsx index 8d7a3e39..267bae44 100644 --- a/apps/dave/src/containers/EpochsContainer.tsx +++ b/apps/dave/src/containers/EpochsContainer.tsx @@ -1,13 +1,14 @@ "use client"; import { useEpochs } from "@cartesi/wagmi"; -import { Stack } from "@mantine/core"; import type { FC } from "react"; import { Hierarchy, type HierarchyConfig, } from "../components/navigation/Hierarchy"; import { EpochsPage } from "../page/EpochsPage"; +import { pathBuilder } from "../routes/routePathBuilder"; import { ContainerSkeleton } from "./ContainerSkeleton"; +import ContainerStack from "./ContainerStack"; export type EpochsContainerProps = { application: string; @@ -22,19 +23,23 @@ export const EpochsContainer: FC = (props) => { { title: "Home", href: "/" }, { title: props.application, - href: `apps/${props.application}/epochs`, + href: pathBuilder.application(props), + }, + { + title: "epochs", + href: pathBuilder.epochs(props), }, ]; const epochs = data?.data ?? []; return ( - + {isLoading && } {props.application && ( )} - + ); }; diff --git a/apps/dave/src/containers/MatchContainer.tsx b/apps/dave/src/containers/MatchContainer.tsx index 586a7a83..5f56fdef 100644 --- a/apps/dave/src/containers/MatchContainer.tsx +++ b/apps/dave/src/containers/MatchContainer.tsx @@ -5,7 +5,6 @@ import { useTournament, useTournaments, } from "@cartesi/wagmi"; -import { Stack } from "@mantine/core"; import { notFound } from "next/navigation"; import type { FC } from "react"; import { @@ -18,6 +17,7 @@ import { useTournamentHierarchy } from "../hooks/useTournamentHierarchy"; import { MatchPage } from "../page/MatchPage"; import { pathBuilder, type MatchParams } from "../routes/routePathBuilder"; import { ContainerSkeleton } from "./ContainerSkeleton"; +import ContainerStack from "./ContainerStack"; export const MatchContainer: FC = (params) => { const now = Date.now(); @@ -55,6 +55,10 @@ export const MatchContainer: FC = (params) => { { title: "Home", href: "/" }, { title: params.application, + href: pathBuilder.application(params), + }, + { + title: "epochs", href: pathBuilder.epochs(params), }, { @@ -112,7 +116,7 @@ export const MatchContainer: FC = (params) => { } return ( - + {isLoading && } {tournament !== null && match !== null && ( @@ -124,6 +128,6 @@ export const MatchContainer: FC = (params) => { now={now} /> )} - + ); }; diff --git a/apps/dave/src/containers/TournamentContainer.tsx b/apps/dave/src/containers/TournamentContainer.tsx index 7ef71373..674f5dad 100644 --- a/apps/dave/src/containers/TournamentContainer.tsx +++ b/apps/dave/src/containers/TournamentContainer.tsx @@ -1,6 +1,5 @@ "use client"; import { useCommitments, useMatches, useTournament } from "@cartesi/wagmi"; -import { Stack } from "@mantine/core"; import { notFound } from "next/navigation"; import type { FC } from "react"; import { @@ -13,6 +12,7 @@ import { useTournamentHierarchy } from "../hooks/useTournamentHierarchy"; import { TournamentPage } from "../page/TournamentPage"; import { pathBuilder, type TournamentParams } from "../routes/routePathBuilder"; import { ContainerSkeleton } from "./ContainerSkeleton"; +import ContainerStack from "./ContainerStack"; export const TournamentContainer: FC = (params) => { const { data: tournament, isLoading } = useTournament({ @@ -69,7 +69,11 @@ export const TournamentContainer: FC = (params) => { { title: "Home", href: "/" }, { title: params.application, - href: pathBuilder.epochs({ application: params.application }), + href: pathBuilder.application(params), + }, + { + title: "epochs", + href: pathBuilder.epochs(params), }, { title: `Epoch #${params.epochIndex}`, @@ -99,7 +103,7 @@ export const TournamentContainer: FC = (params) => { } return ( - + {isLoading && } {!!tournament && ( @@ -109,6 +113,6 @@ export const TournamentContainer: FC = (params) => { tournament={tournament} /> )} - + ); }; diff --git a/apps/dave/src/page/ApplicationSummaryPage.stories.tsx b/apps/dave/src/page/ApplicationSummaryPage.stories.tsx new file mode 100644 index 00000000..302a2e20 --- /dev/null +++ b/apps/dave/src/page/ApplicationSummaryPage.stories.tsx @@ -0,0 +1,147 @@ +import type { Meta, StoryObj } from "@storybook/nextjs"; +import { ApplicationSummaryPage } from "./ApplicationSummaryPage"; + +const meta = { + title: "Pages/Application/summary", + component: ApplicationSummaryPage, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const date = new Date(); + +const params: Parameters[0] = { + application: "aeron", + inputs: { + data: [ + { + epochIndex: 36n, + index: 0n, + blockNumber: 10830n, + rawData: + "0x415bf3630000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000040a7c73a6a592d8697be97254e273e6f3fb46000000000000000000000000000a632c5c05812c6a6149b7af5c56117d1d26038280000000000000000000000000000000000000000000000000000000000002a4e000000000000000000000000000000000000000000000000000000006984d54b46f0d3ef2cf902a71ed5d4f180ff2aa8cd21851eb9cff5425199d023c522f0e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000034a074683b5be015f053b5dceb064c41fc9d11b6e50000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000", + decodedData: { + chainId: 31337n, + applicationContract: + "0x40a7C73a6a592D8697bE97254E273E6f3FB46000", + sender: "0xA632c5c05812c6a6149B7af5C56117d1D2603828", + blockNumber: 10830n, + blockTimestamp: 1770313035n, + prevRandao: + 32087405413018063438212660768862326033482889634384336760477959572643178016995n, + index: 0n, + payload: + "0xa074683b5be015f053b5dceb064c41fc9d11b6e50000000000000000000000000000000000000000000000001bc16d674ec80000", + }, + status: "ACCEPTED", + machineHash: + "0x227d685f612568ed2d5b34fb8e3c19eef80097430498fd2b4a60e73c59e75e8a", + outputsHash: + "0x97a8872d473a093269f65e4e14170fcf5d1383cd105d7215688f5e8c55f00553", + transactionReference: + "0x75e1a936e92309ce05d3592905fb08637e64dab4131440c59ff053880ffde098", + createdAt: date, + updatedAt: date, + }, + ], + totalCount: 1, + isLoading: false, + }, + outputs: { + totalCount: 2, + isLoading: false, + }, + reports: { + totalCount: 0, + isLoading: false, + }, + tournaments: { + data: [ + { + epochIndex: 51n, + address: "0xBFCCffb1AE21227f49009540C5ac45BA45d96149", + parentTournamentAddress: null, + parentMatchIdHash: null, + maxLevel: 3n, + level: 0n, + log2step: 44n, + height: 48n, + winnerCommitment: null, + finalStateHash: null, + finishedAtBlock: 0n, + createdAt: date, + updatedAt: date, + }, + { + epochIndex: 50n, + address: "0x5267E8d41d9c6C1386DBfee95e00B8c6C5503Ba0", + parentTournamentAddress: null, + parentMatchIdHash: null, + maxLevel: 3n, + level: 0n, + log2step: 44n, + height: 48n, + winnerCommitment: + "0x725e9d3febbdd79841345f187aacf343ee497214277f3cb330aca90319cbdd92", + finalStateHash: + "0x227d685f612568ed2d5b34fb8e3c19eef80097430498fd2b4a60e73c59e75e8a", + finishedAtBlock: 15390n, + createdAt: date, + updatedAt: date, + }, + ], + totalCount: 52, + isLoading: false, + }, + + epochs: { + data: [ + { + index: 52n, + firstBlock: 15391n, + lastBlock: 15498n, + inputIndexLowerBound: 1n, + inputIndexUpperBound: 1n, + tournamentAddress: null, + machineHash: null, + commitment: null, + claimTransactionHash: null, + status: "OPEN", + virtualIndex: 52n, + createdAt: date, + updatedAt: date, + commitmentProof: null, + outputsMerkleProof: null, + outputsMerkleRoot: null, + }, + { + index: 51n, + firstBlock: 15090n, + lastBlock: 15391n, + inputIndexLowerBound: 1n, + inputIndexUpperBound: 1n, + tournamentAddress: "0xBFCCffb1AE21227f49009540C5ac45BA45d96149", + machineHash: + "0x227d685f612568ed2d5b34fb8e3c19eef80097430498fd2b4a60e73c59e75e8a", + commitment: + "0x725e9d3febbdd79841345f187aacf343ee497214277f3cb330aca90319cbdd92", + claimTransactionHash: null, + status: "CLAIM_COMPUTED", + virtualIndex: 51n, + createdAt: date, + updatedAt: date, + commitmentProof: null, + outputsMerkleProof: null, + outputsMerkleRoot: null, + }, + ], + totalCount: 53, + isLoading: false, + }, +}; + +export const Default: Story = { + args: params, +}; diff --git a/apps/dave/src/page/ApplicationSummaryPage.tsx b/apps/dave/src/page/ApplicationSummaryPage.tsx new file mode 100644 index 00000000..85b9ac6b --- /dev/null +++ b/apps/dave/src/page/ApplicationSummaryPage.tsx @@ -0,0 +1,180 @@ +import type { + GetEpochReturnType, + GetInputReturnType, + GetTournamentReturnType, +} from "@cartesi/viem"; +import { Anchor, Card, Grid, Group, Stack, Text, Title } from "@mantine/core"; +import Link from "next/link"; +import { head } from "ramda"; +import { isNilOrEmpty } from "ramda-adjunct"; +import type { FC } from "react"; +import { + TbClock, + TbInbox, + TbMail, + TbMessageReport, + TbStack2, + TbTrophy, +} from "react-icons/tb"; +import Address from "../components/Address"; +import CenteredText from "../components/CenteredText"; +import { EpochList } from "../components/epoch/EpochList"; +import { InputList } from "../components/input/InputList"; +import PageTitle from "../components/layout/PageTitle"; +import { SummaryCard } from "../components/SummaryCard"; +import { pathBuilder } from "../routes/routePathBuilder"; + +type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] }; + +type Meta = OmitNever<{ + totalCount: number; + data: T extends never ? never : T; + isLoading: boolean; +}>; + +interface Props { + application: string; + inputs: Meta; + epochs: Meta; + outputs: Meta; + reports: Meta; + tournaments: Meta; +} + +const gridSpan = { base: 12, xs: 6, sm: 4 }; + +export const ApplicationSummaryPage: FC = ({ + application, + epochs, + inputs, + outputs, + reports, + tournaments, +}) => { + const latestTournament = head(tournaments.data); + const epochsUrls = pathBuilder.epochs({ application }); + + const tournamentUrl = + latestTournament !== undefined + ? pathBuilder.tournament({ + application, + epochIndex: latestTournament.epochIndex, + tournamentAddress: latestTournament.address, + }) + : null; + return ( + + + + + + + + + + + + + + + + + + + + + + + {tournamentUrl && ( + + + Active Tournament + + + +
+ Epoch #{latestTournament?.epochIndex} + + + + )} + + + + + Latest Epochs + + + + view all epochs + + + + {epochs.isLoading ? ( + + ) : isNilOrEmpty(epochs.data) ? ( + + ) : ( + + )} + + + + + Latest Inputs + + + {inputs.isLoading ? ( + + ) : isNilOrEmpty(inputs.data) ? ( + + ) : ( + + )} + + + ); +}; diff --git a/apps/dave/src/providers/theme.ts b/apps/dave/src/providers/theme.ts index f0f9db2a..ad6c8f46 100644 --- a/apps/dave/src/providers/theme.ts +++ b/apps/dave/src/providers/theme.ts @@ -1,4 +1,4 @@ -import { createTheme, virtualColor } from "@mantine/core"; +import { createTheme, Spoiler, virtualColor } from "@mantine/core"; const theme = createTheme({ colors: { @@ -55,6 +55,13 @@ const theme = createTheme({ withBorder: true, }, }, + Spoiler: Spoiler.extend({ + defaultProps: { + hideLabel: "Show less", + showLabel: "Show more", + maxHeight: 80, + }, + }), }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f14bfb41..f5f823e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: '@raugfer/jazzicon': specifier: ^1.0.6 version: 1.0.6 + '@react-spring/web': + specifier: ^10.0.1 + version: 10.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tabler/icons-react': specifier: ^3.35.0 version: 3.35.0(react@19.2.3) @@ -9960,9 +9963,6 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-solc@0.5.1: - resolution: {integrity: sha512-Z/hBplZq1+4i4bYeIeD9N3vP1BLUBXpSDa4h0Ipm2Z2cHv7x7DtZ2zFb0E1L1VZo4BF+OJGVtGlT+nTUXGgncQ==} - web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} @@ -14864,7 +14864,7 @@ snapshots: '@usecannon/web-solc': 0.5.1 acorn: 8.15.0 axios: 1.10.0(debug@4.4.3) - axios-retry: 4.5.0(axios@1.10.0(debug@4.4.3)) + axios-retry: 4.5.0(axios@1.10.0) buffer: 6.0.3 chalk: 4.1.2 debug: 4.4.3 @@ -14922,9 +14922,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@usecannon/web-solc@0.5.1': - dependencies: - web-solc: 0.5.1 + '@usecannon/web-solc@0.5.1': {} '@vanilla-extract/css@1.17.3': dependencies: @@ -15113,7 +15111,7 @@ snapshots: - bufferutil - utf-8-validate - '@wagmi/connectors@6.1.0(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@6.1.0(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': dependencies: '@base-org/account': 1.1.1(@types/react@19.1.13)(bufferutil@4.0.8)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.13)(bufferutil@4.0.8)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@3.25.76) @@ -15121,10 +15119,10 @@ snapshots: '@metamask/sdk': 0.33.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.19(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + porto: 0.2.19(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) viem: 2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.9.3 @@ -15226,7 +15224,6 @@ snapshots: - immer - react - use-sync-external-store - optional: true '@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.2.7)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': dependencies: @@ -16454,7 +16451,7 @@ snapshots: axe-core@4.11.0: {} - axios-retry@4.5.0(axios@1.10.0(debug@4.4.3)): + axios-retry@4.5.0(axios@1.10.0): dependencies: axios: 1.10.0(debug@4.4.3) is-retry-allowed: 2.2.0 @@ -20263,9 +20260,9 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.19(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + porto@0.2.19(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) hono: 4.10.2 idb-keyval: 6.2.1 mipd: 0.0.7(typescript@5.9.3) @@ -22567,7 +22564,7 @@ snapshots: wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): dependencies: '@tanstack/react-query': 5.90.12(react@19.2.3) - '@wagmi/connectors': 6.1.0(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) + '@wagmi/connectors': 6.1.0(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) react: 19.2.3 use-sync-external-store: 1.4.0(react@19.2.3) @@ -22641,10 +22638,6 @@ snapshots: dependencies: defaults: 1.0.4 - web-solc@0.5.1: - dependencies: - semver: 7.7.3 - web-streams-polyfill@3.2.1: {} webcrypto-core@1.7.7: @@ -22979,7 +22972,6 @@ snapshots: '@types/react': 19.1.13 react: 19.2.3 use-sync-external-store: 1.6.0(react@19.2.3) - optional: true zustand@5.0.0(@types/react@19.2.7)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)): optionalDependencies: