From 3e5ea7cfb3e4435d44548a4e6064178426ed5dae Mon Sep 17 00:00:00 2001 From: iamonuwa Date: Thu, 2 Oct 2025 20:14:08 +0100 Subject: [PATCH 01/17] chore: add wagmi configuration and providers for Status Hub --- .npmrc | 2 +- apps/hub/src/app/layout.tsx | 6 +- apps/hub/src/app/providers.tsx | 24 + apps/hub/src/wagmi.ts | 96 +++ package.json | 5 + pnpm-lock.yaml | 1319 ++++++++++++++++++++++++++------ 6 files changed, 1218 insertions(+), 234 deletions(-) create mode 100644 apps/hub/src/app/providers.tsx create mode 100644 apps/hub/src/wagmi.ts diff --git a/.npmrc b/.npmrc index 798eb5648..f54ec269c 100644 --- a/.npmrc +++ b/.npmrc @@ -17,4 +17,4 @@ engine-strict=true ignore-workspace-root-check=true ; https://pnpm.io/npmrc#node-options ; why: https://github.com/vercel/next.js/discussions/70423 -node-options=--network-family-autoselection-attempt-timeout=500 \ No newline at end of file +# node-options=--network-family-autoselection-attempt-timeout=500 \ No newline at end of file diff --git a/apps/hub/src/app/layout.tsx b/apps/hub/src/app/layout.tsx index 1064253d0..290319ef3 100644 --- a/apps/hub/src/app/layout.tsx +++ b/apps/hub/src/app/layout.tsx @@ -2,6 +2,8 @@ import './globals.css' import { Inter } from 'next/font/google' +import { Providers } from './providers' + import type { Metadata } from 'next' const inter = Inter({ @@ -22,7 +24,9 @@ export default function RootLayout({ }) { return ( - {children} + + {children} + ) } diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx new file mode 100644 index 000000000..90155736b --- /dev/null +++ b/apps/hub/src/app/providers.tsx @@ -0,0 +1,24 @@ +'use client' + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ConnectKitProvider } from 'connectkit' +import { WagmiProvider } from 'wagmi' +import { mainnet } from 'wagmi/chains' + +import { defineWagmiConfig } from '../wagmi' + +const config = defineWagmiConfig({ + chains: [mainnet], +}) + +const queryClient = new QueryClient() + +export const Providers = ({ children }: { children: React.ReactNode }) => { + return ( + + + {children} + + + ) +} diff --git a/apps/hub/src/wagmi.ts b/apps/hub/src/wagmi.ts new file mode 100644 index 000000000..2ee534d06 --- /dev/null +++ b/apps/hub/src/wagmi.ts @@ -0,0 +1,96 @@ +import { createConfig, http } from 'wagmi' + +import type { Config, CreateConfigParameters, Transport } from 'wagmi' +import type { Chain } from 'wagmi/chains' + +// Todo: move this to shared packages + +/** + * Configuration options for Status Hub wagmi config + */ +export interface DefineWagmiConfigOptions { + /** + * Array of chains to support + */ + chains: [Chain, ...Chain[]] + + /** + * Enable server-side rendering support + * @default false + */ + ssr?: boolean + + /** + * Custom RPC URLs per chain (optional) + * Maps chain IDs to RPC URLs + */ + rpcUrls?: Record + + /** + * Batch JSON-RPC requests + * @default undefined + */ + batch?: CreateConfigParameters['batch'] + + /** + * Polling interval in milliseconds + * @default undefined + */ + pollingInterval?: number + + /** + * WalletConnect project ID + * @default undefined + */ + walletConnectProjectId?: string +} + +/** + * Creates a wagmi configuration for Status Hub with the specified chains and options + * + * @param options - Configuration options + * @returns Wagmi config instance + * + * @example + * ```ts + * import { mainnet, optimism } from 'wagmi/chains' + * + * const config = defineWagmiConfig({ + * chains: [mainnet, optimism], + * ssr: true, + * walletConnectProjectId: 'your-project-id', + * rpcUrls: { + * [mainnet.id]: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY' + * } + * }) + * ``` + */ +export const defineWagmiConfig = ( + options: DefineWagmiConfigOptions +): Config => { + const { chains, ssr = false, rpcUrls, batch, pollingInterval } = options + + const transports = chains.reduce( + (acc, chain) => { + const rpcUrl = rpcUrls?.[chain.id] + acc[chain.id] = http(rpcUrl, { + batch: batch ? true : undefined, + ...(pollingInterval && { pollingInterval }), + }) + return acc + }, + {} as Record + ) + + return createConfig({ + chains, + ssr, + transports, + batch, + }) +} + +/** + * Type helper to infer the config type from defineWagmiConfig + */ +export type InferWagmiConfig = ReturnType diff --git a/package.json b/package.json index 7fd9d7b7b..a75c5eb11 100644 --- a/package.json +++ b/package.json @@ -82,5 +82,10 @@ "oslo@1.0.1": "patches/oslo@1.0.1.patch", "@trpc/server@11.1.0": "patches/@trpc__server@11.1.0.patch" } + }, + "dependencies": { + "@tanstack/react-query": "^5.90.2", + "connectkit": "^1.9.0", + "wagmi": "^2.12.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b37dba14..506e23e1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,16 @@ patchedDependencies: importers: .: + dependencies: + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.2(react@18.3.1) + connectkit: + specifier: ^1.9.0 + version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.90.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + wagmi: + specifier: ^2.12.8 + version: 2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) devDependencies: '@changesets/cli': specifier: ^2.26.2 @@ -113,7 +123,7 @@ importers: version: 10.45.2(@trpc/server@10.45.2) '@trpc/next': specifier: 10.45.2 - version: 10.45.2(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@11.1.0(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@trpc/server@10.45.2)(next@15.3.0(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 10.45.2(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@11.1.0(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@trpc/server@10.45.2)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@trpc/server': specifier: 10.45.2 version: 10.45.2 @@ -265,7 +275,7 @@ importers: version: link:../../packages/status-network '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) + version: 1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) cva: specifier: 1.0.0-beta.1 version: 1.0.0-beta.1(typescript@5.8.3) @@ -274,7 +284,7 @@ importers: version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: specifier: 15.1.6 - version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + version: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-mdx-remote: specifier: ^5.0.0 version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) @@ -500,10 +510,10 @@ importers: version: 0.6.1 connectkit: specifier: ^1.9.0 - version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) + version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -560,10 +570,10 @@ importers: version: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -635,10 +645,10 @@ importers: version: 5.0.1 viem: specifier: ^2.21.1 - version: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + version: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) wagmi: specifier: ^2.12.8 - version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) + version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) zod: specifier: 3.23.8 version: 3.23.8 @@ -648,10 +658,10 @@ importers: version: 2.0.0(prettier@3.3.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -660,7 +670,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -774,7 +784,7 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.19.12) + version: 9.2.1(esbuild@0.25.4) nanoid: specifier: ^5.0.8 version: 5.1.5 @@ -1047,13 +1057,13 @@ importers: version: 3.4.3 '@vercel/analytics': specifier: ^1.2.2 - version: 1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) + version: 1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) '@vercel/postgres': specifier: ^0.8.0 version: 0.8.0 '@vercel/toolbar': specifier: ^0.1.5 - version: 0.1.36(@vercel/analytics@1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1)) + version: 0.1.36(@vercel/analytics@1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1)) '@visx/axis': specifier: ^3.0.0 version: 3.12.0(react@19.1.0) @@ -1101,7 +1111,7 @@ importers: version: 0.6.1 contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -1158,13 +1168,13 @@ importers: version: 6.3.0 next: specifier: 15.2.4 - version: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + version: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -1240,10 +1250,10 @@ importers: version: 2.0.0(prettier@3.5.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -1252,7 +1262,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -1366,13 +1376,13 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.25.4) + version: 9.2.1(esbuild@0.19.12) nanoid: specifier: ^5.0.8 version: 5.1.5 next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4)) + version: 4.2.3(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4)) postcss: specifier: ^8.4.21 version: 8.5.3 @@ -1549,7 +1559,7 @@ importers: version: link:../../packages/icons '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) + version: 1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) cva: specifier: 1.0.0-beta.1 version: 1.0.0-beta.1(typescript@5.8.3) @@ -1558,7 +1568,7 @@ importers: version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.0))(react@19.1.0) next: specifier: 15.1.6 - version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) + version: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-mdx-remote: specifier: ^5.0.0 version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) @@ -2139,7 +2149,7 @@ importers: version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.0))(react@19.1.0) next: specifier: 15.1.6 - version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) + version: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) ts-pattern: specifier: ^5.3.1 version: 5.7.1 @@ -2254,7 +2264,7 @@ importers: version: link:../icons '@trpc/react-query': specifier: 10.45.2 - version: 10.45.2(@tanstack/react-query@5.75.5(react@18.3.1))(@trpc/client@11.1.0(@trpc/server@10.45.2)(typescript@5.8.3))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 10.45.2(@tanstack/react-query@5.90.2(react@18.3.1))(@trpc/client@11.1.0(@trpc/server@10.45.2)(typescript@5.8.3))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trpc/server': specifier: 10.45.2 version: 10.45.2 @@ -2317,7 +2327,7 @@ importers: version: 6.15.0(bufferutil@4.0.9)(utf-8-validate@6.0.3) next: specifier: 15.1.6 - version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.4) + version: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.4) qrcode.react: specifier: ^3.1.0 version: 3.2.0(react@18.3.1) @@ -2537,10 +2547,6 @@ packages: resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} @@ -8548,6 +8554,9 @@ packages: '@tanstack/query-core@5.75.5': resolution: {integrity: sha512-kPDOxtoMn2Ycycb76Givx2fi+2pzo98F9ifHL/NFiahEDpDwSVW6o12PRuQ0lQnBOunhRG5etatAhQij91M3MQ==} + '@tanstack/query-core@5.90.2': + resolution: {integrity: sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==} + '@tanstack/query-devtools@5.28.10': resolution: {integrity: sha512-5UN629fKa5/1K/2Pd26gaU7epxRrYiT1gy+V+pW5K6hnf1DeUKK3pANSb2eHKlecjIKIhTwyF7k9XdyE2gREvQ==} @@ -8576,6 +8585,11 @@ packages: peerDependencies: react: ^18 || ^19 + '@tanstack/react-query@5.90.2': + resolution: {integrity: sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-router-devtools@1.120.2': resolution: {integrity: sha512-89qY5pKdIN6r5G0pHP92mLvorzd7rUlHjvCkjNYOyYduxGQ8a703Y7Fp/RqXibwhjvZcet6BR2IrEhIqg9Yjhw==} engines: {node: '>=12'} @@ -19820,13 +19834,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.24.7(supports-color@5.5.0)': - dependencies: - '@babel/traverse': 7.27.1(supports-color@5.5.0) - '@babel/types': 7.27.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.27.1(supports-color@5.5.0)': dependencies: '@babel/traverse': 7.27.1(supports-color@5.5.0) @@ -21547,7 +21554,7 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3)': + '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10)': dependencies: '@babel/generator': 7.27.1 '@babel/template': 7.27.2 @@ -21562,8 +21569,8 @@ snapshots: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) - '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -21571,7 +21578,7 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.11.0 - graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) inquirer: 8.2.6 is-glob: 4.0.3 jiti: 1.21.6 @@ -21871,16 +21878,16 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': + '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': dependencies: '@graphql-tools/executor-common': 0.0.4(graphql@16.11.0) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/disposablestack': 0.0.6 graphql: 16.11.0 - graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@fastify/websocket' - bufferutil @@ -21914,14 +21921,14 @@ snapshots: - bufferutil - utf-8-validate - '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': + '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/ws': 8.5.8 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -22051,9 +22058,9 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': + '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': dependencies: - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.6 @@ -22127,21 +22134,21 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': + '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': dependencies: - '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-tools/executor-http': 1.3.3(@types/node@22.7.5)(graphql@16.11.0) - '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@graphql-tools/wrap': 10.0.35(graphql@16.11.0) '@types/ws': 8.5.8 '@whatwg-node/fetch': 0.10.6 '@whatwg-node/promise-helpers': 1.3.1 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) sync-fetch: 0.6.0-2 tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -23054,7 +23061,7 @@ snapshots: '@metamask/safe-event-emitter@3.1.2': {} - '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3))': + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: bufferutil: 4.0.9 cross-fetch: 4.1.0 @@ -23063,7 +23070,7 @@ snapshots: eciesjs: 0.4.14 eventemitter2: 6.4.9 readable-stream: 3.6.2 - socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) utf-8-validate: 5.0.10 uuid: 8.3.2 transitivePeerDependencies: @@ -23073,12 +23080,12 @@ snapshots: dependencies: '@paulmillr/qr': 0.2.1 - '@metamask/sdk@0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.27.1 '@metamask/onboarding': 1.0.1 '@metamask/providers': 16.1.0 - '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@metamask/sdk-install-modal-web': 0.32.0 '@paulmillr/qr': 0.2.1 bowser: 2.11.0 @@ -23090,7 +23097,7 @@ snapshots: obj-multiplex: 1.0.0 pump: 3.0.2 readable-stream: 3.6.2 - socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) tslib: 2.8.1 util: 0.12.5 uuid: 8.3.2 @@ -26723,7 +26730,7 @@ snapshots: '@swc/helpers': 0.5.15 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - use-sync-external-store: 1.2.0(react@18.3.1) + use-sync-external-store: 1.5.0(react@18.3.1) '@react-aria/color@3.0.0-rc.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -27664,24 +27671,69 @@ snapshots: '@react-types/shared': 3.24.1(react@18.3.1) react: 18.3.1 - '@reown/appkit-common@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27713,13 +27765,49 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + lit: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -27749,11 +27837,45 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + lit: 3.1.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -27783,16 +27905,53 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-polyfills': 1.7.3 + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27820,9 +27979,9 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 '@walletconnect/logger': 2.1.2 zod: 3.23.8 @@ -27831,20 +27990,72 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@walletconnect/logger': 2.1.2 + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-polyfills': 1.7.3 + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + bs58: 6.0.0 + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-polyfills': 1.7.3 + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.19.2 + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27962,9 +28173,9 @@ snapshots: '@rushstack/eslint-patch@1.11.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -27972,10 +28183,30 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript @@ -28625,6 +28856,8 @@ snapshots: '@tanstack/query-core@5.75.5': {} + '@tanstack/query-core@5.90.2': {} + '@tanstack/query-devtools@5.28.10': {} '@tanstack/query-devtools@5.74.7': {} @@ -28646,14 +28879,19 @@ snapshots: '@tanstack/query-core': 5.29.0 react: 19.1.0 - '@tanstack/react-query@5.75.5(react@18.3.1)': + '@tanstack/react-query@5.75.5(react@19.1.0)': dependencies: '@tanstack/query-core': 5.75.5 + react: 19.1.0 + + '@tanstack/react-query@5.90.2(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.90.2 react: 18.3.1 - '@tanstack/react-query@5.75.5(react@19.1.0)': + '@tanstack/react-query@5.90.2(react@19.1.0)': dependencies: - '@tanstack/query-core': 5.75.5 + '@tanstack/query-core': 5.90.2 react: 19.1.0 '@tanstack/react-router-devtools@1.120.2(@tanstack/react-router@1.120.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.119.0)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)': @@ -28889,19 +29127,19 @@ snapshots: '@trpc/server': 11.1.0(patch_hash=j772m5gatolhdil2x65xum5qqi)(typescript@5.8.3) typescript: 5.8.3 - '@trpc/next@10.45.2(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@11.1.0(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@trpc/server@10.45.2)(next@15.3.0(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@trpc/next@10.45.2(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@11.1.0(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@trpc/server@10.45.2)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@tanstack/react-query': 5.75.5(react@19.1.0) + '@tanstack/react-query': 5.90.2(react@19.1.0) '@trpc/client': 10.45.2(@trpc/server@10.45.2) - '@trpc/react-query': 11.1.0(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + '@trpc/react-query': 11.1.0(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) '@trpc/server': 10.45.2 next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@trpc/react-query@10.45.2(@tanstack/react-query@5.75.5(react@18.3.1))(@trpc/client@11.1.0(@trpc/server@10.45.2)(typescript@5.8.3))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@trpc/react-query@10.45.2(@tanstack/react-query@5.90.2(react@18.3.1))(@trpc/client@11.1.0(@trpc/server@10.45.2)(typescript@5.8.3))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/react-query': 5.75.5(react@18.3.1) + '@tanstack/react-query': 5.90.2(react@18.3.1) '@trpc/client': 11.1.0(@trpc/server@10.45.2)(typescript@5.8.3) '@trpc/server': 10.45.2 react: 18.3.1 @@ -28916,9 +29154,9 @@ snapshots: react-dom: 19.1.0(react@19.1.0) typescript: 5.8.3 - '@trpc/react-query@11.1.0(@tanstack/react-query@5.75.5(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)': + '@trpc/react-query@11.1.0(@tanstack/react-query@5.90.2(react@19.1.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)': dependencies: - '@tanstack/react-query': 5.75.5(react@19.1.0) + '@tanstack/react-query': 5.90.2(react@19.1.0) '@trpc/client': 10.45.2(@trpc/server@10.45.2) '@trpc/server': 10.45.2 react: 19.1.0 @@ -29549,23 +29787,23 @@ snapshots: mdast-util-to-string: 3.2.0 unist-util-visit: 4.1.2 - '@vercel/analytics@1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': + '@vercel/analytics@1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': optionalDependencies: - next: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 svelte: 4.2.2 vue: 3.3.4 - '@vercel/analytics@1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': + '@vercel/analytics@1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': optionalDependencies: - next: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 svelte: 4.2.2 vue: 3.3.4 - '@vercel/analytics@1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': + '@vercel/analytics@1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': optionalDependencies: - next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 svelte: 4.2.2 vue: 3.3.4 @@ -29577,7 +29815,7 @@ snapshots: svelte: 4.2.2 vue: 3.3.4 - '@vercel/microfrontends@1.1.0(@vercel/analytics@1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1))': + '@vercel/microfrontends@1.1.0(@vercel/analytics@1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1))': dependencies: ajv: 8.17.1 commander: 12.1.0 @@ -29588,8 +29826,8 @@ snapshots: nanoid: 3.3.11 path-to-regexp: 6.2.1 optionalDependencies: - '@vercel/analytics': 1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) - next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + '@vercel/analytics': 1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) vite: 6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1) @@ -29622,10 +29860,10 @@ snapshots: utf-8-validate: 6.0.3 ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) - '@vercel/toolbar@0.1.36(@vercel/analytics@1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1))': + '@vercel/toolbar@0.1.36(@vercel/analytics@1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1))': dependencies: '@tinyhttp/app': 1.3.0 - '@vercel/microfrontends': 1.1.0(@vercel/analytics@1.5.0(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1)) + '@vercel/microfrontends': 1.1.0(@vercel/analytics@1.5.0(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4))(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite@6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1)) chokidar: 3.6.0 execa: 5.1.1 fast-glob: 3.3.2 @@ -29634,7 +29872,7 @@ snapshots: jsonc-parser: 3.3.1 strip-ansi: 6.0.1 optionalDependencies: - next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 vite: 6.3.5(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1) transitivePeerDependencies: @@ -30272,16 +30510,16 @@ snapshots: '@vue/shared@3.3.4': {} - '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)': + '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': dependencies: '@coinbase/wallet-sdk': 4.3.0 - '@metamask/sdk': 0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) - '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -30311,11 +30549,50 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))': + '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.90.2)(@types/react@19.1.0)(react@18.3.1)(typescript@5.6.2)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': + dependencies: + '@coinbase/wallet-sdk': 4.3.0 + '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.90.2)(@types/react@19.1.0)(react@18.3.1)(typescript@5.6.2)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - supports-color + - uploadthing + - utf-8-validate + - zod + + '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.8.3) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) zustand: 5.0.0(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.29.0 @@ -30326,6 +30603,21 @@ snapshots: - react - use-sync-external-store + '@wagmi/core@2.17.1(@tanstack/query-core@5.90.2)(@types/react@19.1.0)(react@18.3.1)(typescript@5.6.2)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.6.2) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + zustand: 5.0.0(@types/react@19.1.0)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/query-core': 5.90.2 + typescript: 5.6.2 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + '@waku/core@0.0.26(@multiformats/multiaddr@12.2.0)(libp2p@0.46.18)': dependencies: '@noble/hashes': 1.8.0 @@ -30458,13 +30750,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@walletconnect/core@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30472,7 +30764,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30501,13 +30793,56 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.19.2 + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30515,7 +30850,50 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.20.0 + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30548,18 +30926,58 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/types': 2.20.0 + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30625,12 +31043,12 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 tslib: 1.14.1 - '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/safe-json': 1.0.2 events: 3.3.0 - ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -30680,16 +31098,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30715,16 +31133,86 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.19.2 + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.20.0 + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30810,7 +31298,46 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/types': 2.19.2 + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30819,9 +31346,48 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/types': 2.20.0 + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -30849,7 +31415,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30858,9 +31424,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -30888,7 +31454,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -30906,7 +31472,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -30931,7 +31497,50 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.19.2 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -30949,7 +31558,50 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.20.0 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31065,6 +31717,11 @@ snapshots: abbrev@2.0.0: {} + abitype@1.0.8(typescript@5.6.2)(zod@3.23.8): + optionalDependencies: + typescript: 5.6.2 + zod: 3.23.8 + abitype@1.0.8(typescript@5.8.3)(zod@3.23.8): optionalDependencies: typescript: 5.8.3 @@ -31460,6 +32117,18 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.8 + babel-plugin-styled-components@2.1.4(@babel/core@7.27.1)(styled-components@5.3.11(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1))(supports-color@5.5.0): + dependencies: + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) + lodash: 4.17.21 + picomatch: 2.3.1 + styled-components: 5.3.11(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - supports-color + babel-plugin-styled-components@2.1.4(@babel/core@7.27.1)(styled-components@5.3.11(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0))(supports-color@5.5.0): dependencies: '@babel/helper-annotate-as-pure': 7.27.1 @@ -32335,12 +33004,12 @@ snapshots: graceful-fs: 4.2.11 xdg-basedir: 5.1.0 - connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): + connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) buffer: 6.0.3 detect-browser: 5.3.0 - family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) + family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) framer-motion: 6.5.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) qrcode: 1.5.4 react: 19.1.0 @@ -32349,8 +33018,28 @@ snapshots: react-use-measure: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) resize-observer-polyfill: 1.5.1 styled-components: 5.3.11(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + transitivePeerDependencies: + - '@babel/core' + - react-is + + connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.90.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + dependencies: + '@tanstack/react-query': 5.90.2(react@18.3.1) + buffer: 6.0.3 + detect-browser: 5.3.0 + family: 0.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + framer-motion: 6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + qrcode: 1.5.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-state: 1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + resize-observer-polyfill: 1.5.1 + styled-components: 5.3.11(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) transitivePeerDependencies: - '@babel/core' - react-is @@ -32888,6 +33577,10 @@ snapshots: dequal@2.0.3: {} + derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1)): + dependencies: + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0)): dependencies: valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) @@ -33127,12 +33820,12 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3): + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7(supports-color@5.5.0) engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -33611,7 +34304,7 @@ snapshots: debug: 4.3.7(supports-color@5.5.0) enhanced-resolve: 5.17.1 eslint: 9.14.0(jiti@2.4.2) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -33630,7 +34323,7 @@ snapshots: debug: 4.3.7(supports-color@5.5.0) enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -33649,7 +34342,7 @@ snapshots: debug: 4.3.7(supports-color@5.5.0) enhanced-resolve: 5.17.1 eslint: 9.14.0(jiti@2.4.2) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -33682,7 +34375,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -33693,7 +34386,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -33704,7 +34397,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -33732,7 +34425,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.14.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -33761,7 +34454,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -33790,7 +34483,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.14.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.14.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -34379,7 +35072,7 @@ snapshots: extension-port-stream@3.0.0: dependencies: readable-stream: 3.6.2 - webextension-polyfill: 0.10.0 + webextension-polyfill: 0.12.0 external-editor@3.1.0: dependencies: @@ -34397,12 +35090,19 @@ snapshots: transitivePeerDependencies: - supports-color - family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): + family@0.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + + family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) fast-check@3.16.0: dependencies: @@ -34666,6 +35366,19 @@ snapshots: react: 19.1.0 react-dom: 19.1.1(react@19.1.0) + framer-motion@6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@motionone/dom': 10.12.0 + framesync: 6.0.1 + hey-listen: 1.0.8 + popmotion: 11.0.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + style-value-types: 5.0.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + framer-motion@6.5.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@motionone/dom': 10.12.0 @@ -35096,13 +35809,13 @@ snapshots: - uWebSockets.js - utf-8-validate - graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3): + graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10): dependencies: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) '@graphql-tools/merge': 9.0.24(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) cosmiconfig: 8.1.3 graphql: 16.11.0 @@ -35140,11 +35853,11 @@ snapshots: optionalDependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): + graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: graphql: 16.11.0 optionalDependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) graphql@15.9.0: {} @@ -36099,17 +36812,17 @@ snapshots: dependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): + isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - isows@1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - isows@1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + isows@1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) it-all@3.0.4: {} @@ -38086,6 +38799,10 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mipd@0.0.7(typescript@5.6.2): + optionalDependencies: + typescript: 5.6.2 + mipd@0.0.7(typescript@5.8.3): optionalDependencies: typescript: 5.8.3 @@ -38202,12 +38919,12 @@ snapshots: netmask@2.0.2: {} - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.19.12) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.19.12) - next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -38216,12 +38933,12 @@ snapshots: - markdown-wasm - supports-color - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.25.4) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.25.4) - next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -38258,13 +38975,13 @@ snapshots: - acorn - supports-color - next-sitemap@4.2.3(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4)): + next-sitemap@4.2.3(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.2 minimist: 1.2.8 - next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-sitemap@4.2.3(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4)): dependencies: @@ -38274,7 +38991,7 @@ snapshots: minimist: 1.2.8 next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) - next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.4): + next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.4): dependencies: '@next/env': 15.1.6 '@swc/counter': 0.1.3 @@ -38284,7 +39001,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.27.1)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 15.1.6 '@next/swc-darwin-x64': 15.1.6 @@ -38301,7 +39018,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): + next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): dependencies: '@next/env': 15.1.6 '@swc/counter': 0.1.3 @@ -38328,7 +39045,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4): + next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4): dependencies: '@next/env': 15.1.6 '@swc/counter': 0.1.3 @@ -38355,7 +39072,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): + next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): dependencies: '@next/env': 15.2.4 '@swc/counter': 0.1.3 @@ -38787,6 +39504,20 @@ snapshots: outdent@0.5.0: {} + ox@0.6.7(typescript@5.6.2)(zod@3.23.8): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - zod + ox@0.6.7(typescript@5.8.3)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.10.1 @@ -38801,6 +39532,20 @@ snapshots: transitivePeerDependencies: - zod + ox@0.6.9(typescript@5.6.2)(zod@3.23.8): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - zod + ox@0.6.9(typescript@5.8.3)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.10.1 @@ -40072,6 +40817,11 @@ snapshots: dependencies: react: 19.1.0 + react-transition-state@1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-state@1.1.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -41920,11 +42670,11 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7(supports-color@5.5.0) - engine.io-client: 6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) + engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -42245,9 +42995,27 @@ snapshots: hey-listen: 1.0.8 tslib: 2.8.1 + styled-components@5.3.11(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1): + dependencies: + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/traverse': 7.27.1(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.1)(styled-components@5.3.11(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1))(supports-color@5.5.0) + css-to-react-native: 3.2.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.1.0 + shallowequal: 1.1.0 + supports-color: 5.5.0 + transitivePeerDependencies: + - '@babel/core' + styled-components@5.3.11(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0): dependencies: - '@babel/helper-module-imports': 7.24.7(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) '@babel/traverse': 7.27.1(supports-color@5.5.0) '@emotion/is-prop-valid': 1.3.1 '@emotion/stylis': 0.8.5 @@ -42263,17 +43031,19 @@ snapshots: transitivePeerDependencies: - '@babel/core' - styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.27.1)(react@18.3.1): dependencies: client-only: 0.0.1 - react: 19.1.0 + react: 18.3.1 optionalDependencies: '@babel/core': 7.27.1 - styled-jsx@5.1.6(react@18.3.1): + styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.0): dependencies: client-only: 0.0.1 - react: 18.3.1 + react: 19.1.0 + optionalDependencies: + '@babel/core': 7.27.1 stylis@4.2.0: {} @@ -43338,10 +44108,18 @@ snapshots: dependencies: react: 19.1.0 + use-sync-external-store@1.4.0(react@18.3.1): + dependencies: + react: 18.3.1 + use-sync-external-store@1.4.0(react@19.1.0): dependencies: react: 19.1.0 + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + use-sync-external-store@1.5.0(react@19.1.0): dependencies: react: 19.1.0 @@ -43394,6 +44172,15 @@ snapshots: spdx-correct: 3.1.1 spdx-expression-parse: 3.0.1 + valtio@1.13.2(@types/react@19.1.0)(react@18.3.1): + dependencies: + derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1)) + proxy-compare: 2.6.0 + use-sync-external-store: 1.2.0(react@18.3.1) + optionalDependencies: + '@types/react': 19.1.0 + react: 18.3.1 + valtio@1.13.2(@types/react@19.1.0)(react@19.1.0): dependencies: derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0)) @@ -43467,16 +44254,33 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - viem@2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): + viem@2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.6.7(typescript@5.6.2)(zod@3.23.8) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) - isows: 1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) ox: 0.6.7(typescript@5.8.3)(zod@3.23.8) - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -43484,16 +44288,33 @@ snapshots: - utf-8-validate - zod - viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): + viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + dependencies: + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.6.9(typescript@5.6.2)(zod@3.23.8) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.2 '@noble/hashes': 1.7.2 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) - isows: 1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) - ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -43632,14 +44453,14 @@ snapshots: '@vue/server-renderer': 3.3.4(vue@3.3.4) '@vue/shared': 3.3.4 - wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8): + wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) - '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) + '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -43670,6 +44491,44 @@ snapshots: - utf-8-validate - zod + wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): + dependencies: + '@tanstack/react-query': 5.90.2(react@18.3.1) + '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.90.2)(@types/react@19.1.0)(react@18.3.1)(typescript@5.6.2)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.90.2)(@types/react@19.1.0)(react@18.3.1)(typescript@5.6.2)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)) + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - immer + - ioredis + - supports-color + - uploadthing + - utf-8-validate + - zod + walk-up-path@3.0.1: {} watchpack@2.4.2: @@ -43897,11 +44756,6 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -43912,11 +44766,6 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 6.0.3 - ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 - ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -43927,10 +44776,10 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 6.0.3 - ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: @@ -44129,6 +44978,12 @@ snapshots: optionalDependencies: react: 18.3.1 + zustand@5.0.0(@types/react@19.1.0)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 19.1.0 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + zustand@5.0.0(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)): optionalDependencies: '@types/react': 19.1.0 From 10aa6ae4d6bbc0c3f4a018116320ccd4b46f2d14 Mon Sep 17 00:00:00 2001 From: iamonuwa Date: Thu, 2 Oct 2025 20:42:37 +0100 Subject: [PATCH 02/17] feat: add new icons, promo modal for staking page, integrate connectkit to stake form --- apps/hub/public/modal/bg-gradient.png | Bin 0 -> 319640 bytes apps/hub/public/modal/connector-1.png | Bin 0 -> 26746 bytes apps/hub/public/modal/connector-2.png | Bin 0 -> 39386 bytes apps/hub/public/modal/dragon.png | Bin 0 -> 46929 bytes apps/hub/public/piggy-bank.png | Bin 0 -> 14264 bytes apps/hub/src/app/_components/icons/index.ts | 1 + .../src/app/_components/icons/snt-icon.tsx | 22 ++ .../src/app/_components/stake/promo-modal.tsx | 122 ++++++ apps/hub/src/app/stake/page.tsx | 350 ++++++++++++++---- .../src/components/button/index.tsx | 4 +- 10 files changed, 421 insertions(+), 78 deletions(-) create mode 100644 apps/hub/public/modal/bg-gradient.png create mode 100644 apps/hub/public/modal/connector-1.png create mode 100644 apps/hub/public/modal/connector-2.png create mode 100644 apps/hub/public/modal/dragon.png create mode 100644 apps/hub/public/piggy-bank.png create mode 100644 apps/hub/src/app/_components/icons/snt-icon.tsx create mode 100644 apps/hub/src/app/_components/stake/promo-modal.tsx diff --git a/apps/hub/public/modal/bg-gradient.png b/apps/hub/public/modal/bg-gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..f00944338c89921b19254ad448510329b2eaed71 GIT binary patch literal 319640 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∈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 z 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 literal 0 HcmV?d00001 diff --git a/apps/hub/public/modal/connector-1.png b/apps/hub/public/modal/connector-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c3b181104d50c28d3a0235e79b30f78307c77dd3 GIT binary patch literal 26746 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Μ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

    FlS$9#;d7xe3jX;A~k;QS1zaMQS$Im4G* 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 IM6N<$g44|0>i_@% literal 0 HcmV?d00001 diff --git a/apps/hub/public/modal/dragon.png b/apps/hub/public/modal/dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..d168ed17827c80aa4a4e777ccb6691b3f091e7ef GIT binary patch literal 46929 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

    2a5 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 OB`c*QSu1WF`u_m-c~v$5 literal 0 HcmV?d00001 diff --git a/apps/hub/public/piggy-bank.png b/apps/hub/public/piggy-bank.png new file mode 100644 index 0000000000000000000000000000000000000000..68bd09bf58a96ddb6e502b31fc56f43f05750289 GIT binary patch literal 14264 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 ay7B)WMu5;%smRU%0000 + SNT + Karma + + ) +} diff --git a/apps/hub/src/app/_components/stake/promo-modal.tsx b/apps/hub/src/app/_components/stake/promo-modal.tsx new file mode 100644 index 000000000..7e2c1a85b --- /dev/null +++ b/apps/hub/src/app/_components/stake/promo-modal.tsx @@ -0,0 +1,122 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import * as Dialog from '@radix-ui/react-dialog' +import { Tag } from '@status-im/components' +import { CloseIcon, ExternalIcon } from '@status-im/icons/20' +import { Button, ButtonLink } from '@status-im/status-network/components' +import Image from 'next/image' + +type Props = { + open: boolean + onClose: () => void + children: React.ReactNode +} + +const PromoModal = (props: Props) => { + const { open, onClose, children } = props + + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) { + onClose() + } + } + + return ( + + {children} + + + + +

    + + + +
    +
    +
    + +
    +
    + + +
    + +

    + Connect to +
    + Status Network +

    +
    + + +

    + Use Status L2 features in Chrome with the safety and + control of Status. +

    +
    +
    + +
    + + Install Status Wallet Connector + + + + {/* TODO: Implement connect wallet flow through provider */} + {/* @ts-expect-error - TODO: fix this */} + +
    +
    + +
    + Status Wallet Connector + Status Wallet Connector + Dragon +
    +
    +
    + + + + ) +} + +export { PromoModal } diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 51cfa71fe..db4ea0f0b 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -1,105 +1,301 @@ +/* eslint-disable import/no-unresolved */ 'use client' +import { useEffect, useState } from 'react' + +import { Tooltip } from '@status-im/components' +import { + DropdownIcon, + ExternalIcon, + InfoIcon, + PlaceholderIcon, +} from '@status-im/icons/20' +import { Button, ButtonLink } from '@status-im/status-network/components' +import { ConnectKitButton } from 'connectkit' +import Image from 'next/image' +import { match } from 'ts-pattern' +import { useAccount } from 'wagmi' + import { HubLayout } from '~components/hub-layout' +import { LaunchIcon, SNTIcon } from '../_components/icons' +import { PromoModal } from '../_components/stake/promo-modal' + export default function StakePage() { + const [status, setStatus] = useState< + 'unninstalled' | 'disconnected' | 'connected' + >('unninstalled') + const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) + const { isConnected } = useAccount() + + useEffect(() => { + if (isConnected) { + setStatus('connected') + } else { + setStatus('disconnected') + } + }, [isConnected]) + return (
    - {/* Hero Section */} -
    -

    - Staking is good for karma -

    -

    - Stake SNT to increase your Karma and unlock gasless transactions -

    -
    - - {/* Main Content */}
    -
    -
    -
    - {/* Amount to Stake */} -
    - -
    -
    -
    - +
    +

    Stake SNT, receive good Karma

    +

    + Stake SNT to increase your Karma, unlock more gasless transactions + and increase your power over the network +

    +
    + +
    +
    +
    +
    +
    + Piggy Bank +
    + +
    +
    +

    + Free Testnet SNT faucet +

    +
    + +
    +
    +

    Daily limit

    +

    10,000 SNT

    -
    -
    -
    - - S - -
    -
    -
    -
    -
    - - SNT - +
    +

    Used today

    +

    0 SNT

    +
    +
    +

    Available

    +

    10,000 SNT

    - {/* Select Vault */} -
    - -
    -
    - New vault - + + Claim testnet SNT + +
    + +
    +
    +
    +
    'space-y-2') + .otherwise(() => 'space-y-2 opacity-[40%]')} + > + + {match(status) + .with('connected', () => ( +
    +
    + +
    + + + SNT + +
    +
    +
    +
    + 0 + +
    +
    + )) + .otherwise(() => ( +
    +
    + +
    + + + SNT + +
    +
    +
    + ))} +
    + +
    'space-y-2') + .otherwise(() => 'space-y-2 opacity-[40%]')} + > + + {match(status) + .with('connected', () => ( + + )) + .otherwise(() => ( +
    +
    + New vault + +
    +
    + ))}
    + {match(status) + .with('unninstalled', () => ( + { + setIsPromoModalOpen(false) + setStatus('disconnected') + }} + > + {/* @ts-expect-error - TODO: fix this */} + + + )) + .with('disconnected', () => ( + + {({ isConnected, show }) => ( + // @ts-expect-error - TODO: fix this + + )} + + )) + .with('connected', () => ( + // @ts-expect-error - TODO: fix this + + )) + .exhaustive()}
    - {/* Action Button */} -
    - +
    +
    +
    +

    + Total staked +

    +
    +
    + + 0 SNT +
    +

    + Next unlock in 356 days +

    +
    + +
    +
    +

    + Weighted aggregated boost +

    + +
    +
    + + + x0 +
    +

    + No points are ready to compound +

    +
    -
    +
    ) } + +const InfoTooltip = () => ( + +

    + The longer SNT is staked or locked in vaults, the higher this + multiplier goes. This rewards long term believers. The maximum + multiplier is x9. +

    + + {/* TODO: change link */} + + Learn more + + +
    + } + > + + +) diff --git a/packages/status-network/src/components/button/index.tsx b/packages/status-network/src/components/button/index.tsx index 21a8de0df..9e9d623c6 100644 --- a/packages/status-network/src/components/button/index.tsx +++ b/packages/status-network/src/components/button/index.tsx @@ -3,7 +3,7 @@ import { forwardRef } from 'react' import { cva, cx } from 'cva' type Props = { - variant?: 'primary' | 'secondary' | 'white' + variant?: 'primary' | 'secondary' | 'white' | 'outline' backdropFilter?: boolean children?: React.ReactNode active?: boolean @@ -22,6 +22,8 @@ const buttonStyles = cva({ 'border-white-10 bg-white-5 text-white-100 hover:border-white-20 hover:bg-white-10', white: 'border-neutral-30 bg-white-100 text-dark-100 hover:border-neutral-40 hover:bg-white-80', + outline: + 'pressed:border-neutral-50 border border-neutral-30 text-neutral-100 hover:border-neutral-40 disabled:border-neutral-20', }, withIcon: { true: '', From e0bb149cafc6ca42937eb3f8adf32996b0e15f49 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Thu, 2 Oct 2025 21:49:13 +0100 Subject: [PATCH 03/17] feat: implement progress dialog and state management for vault operations --- apps/hub/src/app/_components/icons/index.ts | 1 + .../app/_components/icons/new-action-icon.tsx | 32 +++ .../app/_components/stake/progress-dialog.tsx | 72 +++++++ .../stake/use-progress-dialog-content.tsx | 123 +++++++++++ .../src/app/_hooks/use-vault-state-machine.ts | 194 ++++++++++++++++++ apps/hub/src/app/stake/page.tsx | 76 ++++--- 6 files changed, 475 insertions(+), 23 deletions(-) create mode 100644 apps/hub/src/app/_components/icons/new-action-icon.tsx create mode 100644 apps/hub/src/app/_components/stake/progress-dialog.tsx create mode 100644 apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx create mode 100644 apps/hub/src/app/_hooks/use-vault-state-machine.ts diff --git a/apps/hub/src/app/_components/icons/index.ts b/apps/hub/src/app/_components/icons/index.ts index a02ea5745..f547e07ec 100644 --- a/apps/hub/src/app/_components/icons/index.ts +++ b/apps/hub/src/app/_components/icons/index.ts @@ -10,6 +10,7 @@ export { default as HomeIcon } from './home-icon' export { default as KarmaIcon } from './karma-icon' export { default as LaunchIcon } from './launch-icon' export { default as MintIcon } from './mint-icon' +export { default as NewActionIcon } from './new-action-icon' export { default as PercentIcon } from './percent-icon' export { default as PlusIcon } from './plus-icon' export { default as SettingsIcon } from './settings-icon' diff --git a/apps/hub/src/app/_components/icons/new-action-icon.tsx b/apps/hub/src/app/_components/icons/new-action-icon.tsx new file mode 100644 index 000000000..7c6382e8c --- /dev/null +++ b/apps/hub/src/app/_components/icons/new-action-icon.tsx @@ -0,0 +1,32 @@ +import type { SVGProps } from 'react' + +export default function NewActionIcon({ + className, + ...props +}: SVGProps) { + return ( + + + + + ) +} diff --git a/apps/hub/src/app/_components/stake/progress-dialog.tsx b/apps/hub/src/app/_components/stake/progress-dialog.tsx new file mode 100644 index 000000000..8de0ea326 --- /dev/null +++ b/apps/hub/src/app/_components/stake/progress-dialog.tsx @@ -0,0 +1,72 @@ +/* eslint-disable import/no-unresolved */ +import * as Dialog from '@radix-ui/react-dialog' +import { CloseIcon } from '@status-im/icons/20' +import { match } from 'ts-pattern' + +import { NewActionIcon } from '../icons' + +import type { ProgressDialogState } from './use-progress-dialog-content' + +type Props = { + open: boolean + onClose: () => void + title: string + description: string + children?: React.ReactNode + state?: ProgressDialogState +} + +const ProgressDialog = (props: Props) => { + const { open, onClose, title, description, children, state = 'new' } = props + + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) { + onClose() + } + } + + const mapIconToState = (state: ProgressDialogState) => { + return match(state) + .with('siwe', () => ) + .with('new', () => ) + .with('in-progress', () => ) + .with('failed', () => ) + .otherwise(() => null) + } + + return ( + + {children} + + + + +
    +
    + + + +
    + {mapIconToState(state)} + +

    + {title} +

    +
    + + {description} + +
    +
    + + + + ) +} + +export { ProgressDialog } diff --git a/apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx b/apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx new file mode 100644 index 000000000..4fd050e5e --- /dev/null +++ b/apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx @@ -0,0 +1,123 @@ +import { match } from 'ts-pattern' + +import type { VaultState } from '../../_hooks/use-vault-state-machine' + +export type ProgressDialogState = + | 'siwe' + | 'new' + | 'in-progress' + | 'failed' + | 'success' + +export type ProgressDialogContent = { + title: string + description: string + state: ProgressDialogState + showCloseButton: boolean +} + +export function useProgressDialogContent( + state: VaultState +): ProgressDialogContent | null { + return ( + match(state) + // SIWE flow + .with({ type: 'siwe', step: 'initialize' }, () => ({ + title: 'Sign in', + description: 'Please sign the message in your wallet.', + state: 'new', + showCloseButton: true, + })) + .with({ type: 'siwe', step: 'processing' }, () => ({ + title: 'Signing in', + description: 'Wait a moment.', + state: 'in-progress', + showCloseButton: true, + })) + .with({ type: 'siwe', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'failed', + showCloseButton: true, + })) + + // Create Vault flow + .with({ type: 'createVault', step: 'initialize' }, () => ({ + title: 'Ready to create new vault', + description: 'Please sign the message in your wallet.', + state: 'new', + showCloseButton: true, + })) + .with({ type: 'createVault', step: 'processing' }, () => ({ + title: 'Creating new vault', + description: 'Wait a moment.', + state: 'in-progress', + showCloseButton: true, + })) + .with({ type: 'createVault', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'failed', + showCloseButton: true, + })) + + // Increase Allowance flow + .with({ type: 'increaseAllowance', step: 'initialize' }, () => ({ + title: 'Increase token allowance', + description: 'Please sign the message in your wallet.', + state: 'new', + showCloseButton: true, + })) + .with({ type: 'increaseAllowance', step: 'processing' }, () => ({ + title: 'Increasing token allowance', + description: 'Wait a moment.', + state: 'in-progress', + showCloseButton: false, + })) + .with({ type: 'increaseAllowance', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'failed', + showCloseButton: true, + })) + + // Staking flow + .with( + { + type: 'staking', + step: 'initialize', + }, + state => ({ + title: `Ready to stake ${state.amount || '0'} SNT`, + description: 'Please sign the message in your wallet.', + state: 'new', + showCloseButton: true, + }) + ) + + .with({ type: 'staking', step: 'processing' }, state => ({ + title: `Staking ${state.amount} SNT`, + description: 'Wait a moment...', + state: 'in-progress', + showCloseButton: false, + })) + .with({ type: 'staking', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'failed', + showCloseButton: true, + })) + + // Success + .with({ type: 'success' }, () => ({ + title: 'Success!', + description: 'Your transaction completed successfully', + state: 'success', + showCloseButton: true, + })) + + // Idle - no dialog + .with({ type: 'idle' }, () => null) + .exhaustive() + ) +} diff --git a/apps/hub/src/app/_hooks/use-vault-state-machine.ts b/apps/hub/src/app/_hooks/use-vault-state-machine.ts new file mode 100644 index 000000000..133064c46 --- /dev/null +++ b/apps/hub/src/app/_hooks/use-vault-state-machine.ts @@ -0,0 +1,194 @@ +import { useCallback, useState } from 'react' + +import { match } from 'ts-pattern' + +// State definitions +export type VaultState = + | { type: 'idle' } + | { type: 'siwe'; step: 'initialize' | 'processing' | 'rejected' } + | { type: 'createVault'; step: 'initialize' | 'processing' | 'rejected' } + | { + type: 'increaseAllowance' + step: 'initialize' | 'processing' | 'rejected' + amount?: string + } + | { + type: 'staking' + step: 'initialize' | 'processing' | 'rejected' + amount?: string + } + | { type: 'success' } + +// Event definitions +export type VaultEvent = + | { type: 'START_SIWE' } + | { type: 'START_CREATE_VAULT' } + | { type: 'START_INCREASE_ALLOWANCE'; amount?: string } + | { type: 'SIGN' } + | { type: 'REJECT' } + | { type: 'PROCESS' } + | { type: 'COMPLETE'; amount?: string } + | { type: 'READY_TO_STAKE' } + | { type: 'START_STAKING'; amount?: string } + | { type: 'RESET' } + +// Transition function +function transition(state: VaultState, event: VaultEvent): VaultState { + return ( + match<[VaultState, VaultEvent], VaultState>([state, event]) + .with([{ type: 'idle' }, { type: 'START_SIWE' }], () => ({ + type: 'siwe', + step: 'initialize', + })) + .with([{ type: 'idle' }, { type: 'START_CREATE_VAULT' }], () => ({ + type: 'createVault', + step: 'initialize', + })) + .with( + [{ type: 'idle' }, { type: 'START_INCREASE_ALLOWANCE' }], + ([, event]) => ({ + type: 'increaseAllowance', + step: 'initialize', + amount: event.amount, + }) + ) + + // SIWE flow + .with([{ type: 'siwe', step: 'initialize' }, { type: 'SIGN' }], () => ({ + type: 'siwe', + step: 'processing', + })) + .with( + [{ type: 'siwe', step: 'processing' }, { type: 'COMPLETE' }], + () => ({ + type: 'success', + }) + ) + .with([{ type: 'siwe', step: 'processing' }, { type: 'REJECT' }], () => ({ + type: 'siwe', + step: 'rejected', + })) + .with([{ type: 'siwe', step: 'rejected' }, { type: 'RESET' }], () => ({ + type: 'idle', + })) + + // Create Vault flow + .with( + [{ type: 'createVault', step: 'initialize' }, { type: 'SIGN' }], + () => ({ + type: 'createVault', + step: 'processing', + }) + ) + .with( + [{ type: 'createVault', step: 'processing' }, { type: 'COMPLETE' }], + () => ({ type: 'success' }) + ) + .with( + [{ type: 'createVault', step: 'processing' }, { type: 'REJECT' }], + () => ({ + type: 'createVault', + step: 'rejected', + }) + ) + .with( + [{ type: 'createVault', step: 'rejected' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) + + // Increase Allowance flow + .with( + [{ type: 'increaseAllowance', step: 'initialize' }, { type: 'SIGN' }], + ([state]) => ({ + type: 'increaseAllowance', + step: 'processing', + amount: state.amount, + }) + ) + .with( + [ + { type: 'increaseAllowance', step: 'processing' }, + { type: 'COMPLETE' }, + ], + ([state, event]) => ({ + type: 'staking', + step: 'initialize', + amount: event.amount || state.amount || '0', + }) + ) + .with( + [{ type: 'increaseAllowance', step: 'processing' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'increaseAllowance', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [ + { type: 'increaseAllowance', step: 'rejected' }, + { type: 'START_INCREASE_ALLOWANCE' }, + ], + ([state, event]) => ({ + type: 'increaseAllowance', + step: 'initialize', + amount: event.amount || state.amount, + }) + ) + + // Staking flow + .with( + [{ type: 'staking', step: 'initialize' }, { type: 'SIGN' }], + ([state]) => ({ + type: 'staking', + step: 'processing', + amount: state.amount, + }) + ) + .with( + [{ type: 'staking', step: 'processing' }, { type: 'COMPLETE' }], + () => ({ type: 'success' }) + ) + .with( + [{ type: 'staking', step: 'processing' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'staking', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [{ type: 'staking', step: 'rejected' }, { type: 'START_STAKING' }], + ([state, event]) => ({ + type: 'staking', + step: 'initialize', + amount: event.amount || state.amount, + }) + ) + + // Reset from success + .with([{ type: 'success' }, { type: 'RESET' }], () => ({ type: 'idle' })) + + // Fallback + .otherwise(() => state) + ) +} + +// Hook +export function useVaultStateMachine() { + const [state, setState] = useState({ type: 'idle' }) + + const send = useCallback((event: VaultEvent) => { + setState(currentState => transition(currentState, event)) + }, []) + + const reset = useCallback(() => { + send({ type: 'RESET' }) + }, [send]) + + return { + state, + send, + reset, + } +} diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index db4ea0f0b..89ab1b10d 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useEffect, useState } from 'react' +import { useMemo, useState } from 'react' import { Tooltip } from '@status-im/components' import { @@ -19,22 +19,43 @@ import { useAccount } from 'wagmi' import { HubLayout } from '~components/hub-layout' import { LaunchIcon, SNTIcon } from '../_components/icons' +import { ProgressDialog } from '../_components/stake/progress-dialog' import { PromoModal } from '../_components/stake/promo-modal' +import { useProgressDialogContent } from '../_components/stake/use-progress-dialog-content' +import { useVaultStateMachine } from '../_hooks/use-vault-state-machine' + +type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' export default function StakePage() { - const [status, setStatus] = useState< - 'unninstalled' | 'disconnected' | 'connected' - >('unninstalled') - const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) const { isConnected } = useAccount() + const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) + + // State machine for vault operations + const { + state: vaultState, + send: sendVaultEvent, + reset: resetVault, + } = useVaultStateMachine() + const dialogContent = useProgressDialogContent(vaultState) + + const status: ConnectionStatus = useMemo(() => { + if (isConnected) return 'connected' + return isPromoModalOpen ? 'uninstalled' : 'disconnected' + }, [isConnected, isPromoModalOpen]) - useEffect(() => { + const handleCreateVault = () => { if (isConnected) { - setStatus('connected') - } else { - setStatus('disconnected') + sendVaultEvent({ type: 'START_CREATE_VAULT' }) } - }, [isConnected]) + } + + const handleCloseProgressDialog = () => { + resetVault() + } + + const handleClosePromoModal = () => { + setIsPromoModalOpen(false) + } return ( @@ -192,13 +213,10 @@ export default function StakePage() {
    {match(status) - .with('unninstalled', () => ( + .with('uninstalled', () => ( { - setIsPromoModalOpen(false) - setStatus('disconnected') - }} + onClose={handleClosePromoModal} > {/* @ts-expect-error - TODO: fix this */} )} )) .with('connected', () => ( - // @ts-expect-error - TODO: fix this - + <> + {/* @ts-expect-error - TODO: fix this */} + + {dialogContent && ( + + )} + )) .exhaustive()}
    From 3243473a1037bc976c1c3d6b4b2f877e6e876241 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Thu, 2 Oct 2025 22:02:17 +0100 Subject: [PATCH 04/17] chore: rename progress dialog to action status component and where it is used --- .../src/app/_components/icons/close-icon.tsx | 20 ++++++++ apps/hub/src/app/_components/icons/index.ts | 2 + .../app/_components/icons/processing-icon.tsx | 34 ++++++++++++++ ...ss-dialog.tsx => action-status-dialog.tsx} | 29 +++++++----- ...tent.tsx => use-action-status-content.tsx} | 47 +++++++++---------- apps/hub/src/app/stake/page.tsx | 8 ++-- 6 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 apps/hub/src/app/_components/icons/close-icon.tsx create mode 100644 apps/hub/src/app/_components/icons/processing-icon.tsx rename apps/hub/src/app/_components/stake/{progress-dialog.tsx => action-status-dialog.tsx} (78%) rename apps/hub/src/app/_components/stake/{use-progress-dialog-content.tsx => use-action-status-content.tsx} (79%) diff --git a/apps/hub/src/app/_components/icons/close-icon.tsx b/apps/hub/src/app/_components/icons/close-icon.tsx new file mode 100644 index 000000000..bfc2c617a --- /dev/null +++ b/apps/hub/src/app/_components/icons/close-icon.tsx @@ -0,0 +1,20 @@ +import type { SVGProps } from 'react' + +const CloseIcon = (props: SVGProps) => ( + + + +) +export default CloseIcon diff --git a/apps/hub/src/app/_components/icons/index.ts b/apps/hub/src/app/_components/icons/index.ts index f547e07ec..e721b54aa 100644 --- a/apps/hub/src/app/_components/icons/index.ts +++ b/apps/hub/src/app/_components/icons/index.ts @@ -1,5 +1,6 @@ // Custom Figma icons exported directly from design export { default as BridgeIcon } from './bridge-icon' +export { default as CloseIcon } from './close-icon' export { default as DepositIcon } from './deposit-icon' export { default as DiscoverIcon } from './discover-icon' export { default as DocsIcon } from './docs-icon' @@ -13,6 +14,7 @@ export { default as MintIcon } from './mint-icon' export { default as NewActionIcon } from './new-action-icon' export { default as PercentIcon } from './percent-icon' export { default as PlusIcon } from './plus-icon' +export { default as ProcessingIcon } from './processing-icon' export { default as SettingsIcon } from './settings-icon' export { default as SNTIcon } from './snt-icon' export { default as StakeIcon } from './stake-icon' diff --git a/apps/hub/src/app/_components/icons/processing-icon.tsx b/apps/hub/src/app/_components/icons/processing-icon.tsx new file mode 100644 index 000000000..660432a8b --- /dev/null +++ b/apps/hub/src/app/_components/icons/processing-icon.tsx @@ -0,0 +1,34 @@ +import type { SVGProps } from 'react' + +const ProcessingIcon = (props: SVGProps) => ( + + + + + +) +export default ProcessingIcon diff --git a/apps/hub/src/app/_components/stake/progress-dialog.tsx b/apps/hub/src/app/_components/stake/action-status-dialog.tsx similarity index 78% rename from apps/hub/src/app/_components/stake/progress-dialog.tsx rename to apps/hub/src/app/_components/stake/action-status-dialog.tsx index 8de0ea326..a00b6dc02 100644 --- a/apps/hub/src/app/_components/stake/progress-dialog.tsx +++ b/apps/hub/src/app/_components/stake/action-status-dialog.tsx @@ -3,9 +3,9 @@ import * as Dialog from '@radix-ui/react-dialog' import { CloseIcon } from '@status-im/icons/20' import { match } from 'ts-pattern' -import { NewActionIcon } from '../icons' +import { NewActionIcon, ProcessingIcon } from '../icons' -import type { ProgressDialogState } from './use-progress-dialog-content' +import type { ActionStatusState } from './use-action-status-content' type Props = { open: boolean @@ -13,11 +13,18 @@ type Props = { title: string description: string children?: React.ReactNode - state?: ProgressDialogState + state?: ActionStatusState } -const ProgressDialog = (props: Props) => { - const { open, onClose, title, description, children, state = 'new' } = props +const ActionStatusDialog = (props: Props) => { + const { + open, + onClose, + title, + description, + children, + state = 'pending', + } = props const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen) { @@ -25,12 +32,12 @@ const ProgressDialog = (props: Props) => { } } - const mapIconToState = (state: ProgressDialogState) => { + const mapIconToState = (state: ActionStatusState) => { return match(state) - .with('siwe', () => ) - .with('new', () => ) - .with('in-progress', () => ) - .with('failed', () => ) + .with('pending', () => ) + .with('processing', () => ) + .with('error', () => ) + .with('success', () => ) .otherwise(() => null) } @@ -69,4 +76,4 @@ const ProgressDialog = (props: Props) => { ) } -export { ProgressDialog } +export { ActionStatusDialog } diff --git a/apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx b/apps/hub/src/app/_components/stake/use-action-status-content.tsx similarity index 79% rename from apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx rename to apps/hub/src/app/_components/stake/use-action-status-content.tsx index 4fd050e5e..15e4da193 100644 --- a/apps/hub/src/app/_components/stake/use-progress-dialog-content.tsx +++ b/apps/hub/src/app/_components/stake/use-action-status-content.tsx @@ -2,42 +2,41 @@ import { match } from 'ts-pattern' import type { VaultState } from '../../_hooks/use-vault-state-machine' -export type ProgressDialogState = - | 'siwe' - | 'new' - | 'in-progress' - | 'failed' - | 'success' +export type ActionStatusState = + | 'pending' // Waiting for user action (sign, approve) + | 'processing' // Transaction in progress + | 'error' // Failed/rejected + | 'success' // Completed successfully -export type ProgressDialogContent = { +export type ActionStatusContent = { title: string description: string - state: ProgressDialogState + state: ActionStatusState showCloseButton: boolean } -export function useProgressDialogContent( +export function useActionStatusContent( state: VaultState -): ProgressDialogContent | null { +): ActionStatusContent | null { return ( - match(state) + match(state) // SIWE flow .with({ type: 'siwe', step: 'initialize' }, () => ({ title: 'Sign in', description: 'Please sign the message in your wallet.', - state: 'new', + state: 'pending', showCloseButton: true, })) .with({ type: 'siwe', step: 'processing' }, () => ({ title: 'Signing in', description: 'Wait a moment.', - state: 'in-progress', + state: 'processing', showCloseButton: true, })) .with({ type: 'siwe', step: 'rejected' }, () => ({ title: 'Request was rejected', description: 'Request was rejected by user', - state: 'failed', + state: 'error', showCloseButton: true, })) @@ -45,19 +44,19 @@ export function useProgressDialogContent( .with({ type: 'createVault', step: 'initialize' }, () => ({ title: 'Ready to create new vault', description: 'Please sign the message in your wallet.', - state: 'new', + state: 'pending', showCloseButton: true, })) .with({ type: 'createVault', step: 'processing' }, () => ({ title: 'Creating new vault', description: 'Wait a moment.', - state: 'in-progress', + state: 'processing', showCloseButton: true, })) .with({ type: 'createVault', step: 'rejected' }, () => ({ title: 'Request was rejected', description: 'Request was rejected by user', - state: 'failed', + state: 'error', showCloseButton: true, })) @@ -65,19 +64,19 @@ export function useProgressDialogContent( .with({ type: 'increaseAllowance', step: 'initialize' }, () => ({ title: 'Increase token allowance', description: 'Please sign the message in your wallet.', - state: 'new', + state: 'pending', showCloseButton: true, })) .with({ type: 'increaseAllowance', step: 'processing' }, () => ({ title: 'Increasing token allowance', description: 'Wait a moment.', - state: 'in-progress', + state: 'processing', showCloseButton: false, })) .with({ type: 'increaseAllowance', step: 'rejected' }, () => ({ title: 'Request was rejected', description: 'Request was rejected by user', - state: 'failed', + state: 'error', showCloseButton: true, })) @@ -90,21 +89,21 @@ export function useProgressDialogContent( state => ({ title: `Ready to stake ${state.amount || '0'} SNT`, description: 'Please sign the message in your wallet.', - state: 'new', + state: 'pending', showCloseButton: true, }) ) .with({ type: 'staking', step: 'processing' }, state => ({ - title: `Staking ${state.amount} SNT`, + title: `Staking ${state.amount || '0'} SNT`, description: 'Wait a moment...', - state: 'in-progress', + state: 'processing', showCloseButton: false, })) .with({ type: 'staking', step: 'rejected' }, () => ({ title: 'Request was rejected', description: 'Request was rejected by user', - state: 'failed', + state: 'error', showCloseButton: true, })) diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 89ab1b10d..05354eab2 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -19,9 +19,9 @@ import { useAccount } from 'wagmi' import { HubLayout } from '~components/hub-layout' import { LaunchIcon, SNTIcon } from '../_components/icons' -import { ProgressDialog } from '../_components/stake/progress-dialog' +import { ActionStatusDialog } from '../_components/stake/action-status-dialog' import { PromoModal } from '../_components/stake/promo-modal' -import { useProgressDialogContent } from '../_components/stake/use-progress-dialog-content' +import { useActionStatusContent } from '../_components/stake/use-action-status-content' import { useVaultStateMachine } from '../_hooks/use-vault-state-machine' type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' @@ -36,7 +36,7 @@ export default function StakePage() { send: sendVaultEvent, reset: resetVault, } = useVaultStateMachine() - const dialogContent = useProgressDialogContent(vaultState) + const dialogContent = useActionStatusContent(vaultState) const status: ConnectionStatus = useMemo(() => { if (isConnected) return 'connected' @@ -250,7 +250,7 @@ export default function StakePage() { Create new vault {dialogContent && ( - Date: Fri, 3 Oct 2025 13:17:49 +0100 Subject: [PATCH 05/17] feat: integrate SIWE for wallet authentication and enhance vault state management --- apps/hub/package.json | 5 +- .../stake/action-status-dialog.tsx | 22 ++-- .../src/app/_components/stake/promo-modal.tsx | 21 ++-- .../_components/stake/vault-lock-modal.tsx | 62 ++++++++++ apps/hub/src/app/_hooks/use-siwe.ts | 50 ++++++++ .../src/app/_hooks/use-vault-state-machine.ts | 7 ++ apps/hub/src/app/providers.tsx | 1 + apps/hub/src/app/stake/page.tsx | 115 ++++++++++++++---- pnpm-lock.yaml | 61 ++++++++++ 9 files changed, 301 insertions(+), 43 deletions(-) create mode 100644 apps/hub/src/app/_components/stake/vault-lock-modal.tsx create mode 100644 apps/hub/src/app/_hooks/use-siwe.ts diff --git a/apps/hub/package.json b/apps/hub/package.json index bffe62278..864e24606 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -15,8 +15,8 @@ }, "dependencies": { "@status-im/colors": "workspace:*", - "@status-im/icons": "workspace:*", "@status-im/components": "workspace:*", + "@status-im/icons": "workspace:*", "@status-im/status-network": "workspace:*", "@vercel/analytics": "^1.5.0", "cva": "1.0.0-beta.1", @@ -26,13 +26,14 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "rehype-slug": "^6.0.0", + "siwe": "^2.3.2", "ts-pattern": "^5.6.2", "zod": "^3.24.1" }, "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@next/eslint-plugin-next": "15.3.0", "@status-im/eslint-config": "workspace:*", - "@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/apps/hub/src/app/_components/stake/action-status-dialog.tsx b/apps/hub/src/app/_components/stake/action-status-dialog.tsx index a00b6dc02..f3fd877ac 100644 --- a/apps/hub/src/app/_components/stake/action-status-dialog.tsx +++ b/apps/hub/src/app/_components/stake/action-status-dialog.tsx @@ -14,6 +14,7 @@ type Props = { description: string children?: React.ReactNode state?: ActionStatusState + showCloseButton?: boolean } const ActionStatusDialog = (props: Props) => { @@ -24,10 +25,11 @@ const ActionStatusDialog = (props: Props) => { description, children, state = 'pending', + showCloseButton = true, } = props const handleOpenChange = (nextOpen: boolean) => { - if (!nextOpen) { + if (!nextOpen && showCloseButton) { onClose() } } @@ -50,14 +52,16 @@ const ActionStatusDialog = (props: Props) => {
    - - - + {showCloseButton && ( + + + + )}
    {mapIconToState(state)} diff --git a/apps/hub/src/app/_components/stake/promo-modal.tsx b/apps/hub/src/app/_components/stake/promo-modal.tsx index 7e2c1a85b..afff47aaf 100644 --- a/apps/hub/src/app/_components/stake/promo-modal.tsx +++ b/apps/hub/src/app/_components/stake/promo-modal.tsx @@ -5,6 +5,7 @@ import * as Dialog from '@radix-ui/react-dialog' import { Tag } from '@status-im/components' import { CloseIcon, ExternalIcon } from '@status-im/icons/20' import { Button, ButtonLink } from '@status-im/status-network/components' +import { ConnectKitButton } from 'connectkit' import Image from 'next/image' type Props = { @@ -76,15 +77,17 @@ const PromoModal = (props: Props) => { - {/* TODO: Implement connect wallet flow through provider */} - {/* @ts-expect-error - TODO: fix this */} - + + {({ show, isConnected }) => ( + // @ts-expect-error - TODO: fix this + + )} +
    diff --git a/apps/hub/src/app/_components/stake/vault-lock-modal.tsx b/apps/hub/src/app/_components/stake/vault-lock-modal.tsx new file mode 100644 index 000000000..53714f554 --- /dev/null +++ b/apps/hub/src/app/_components/stake/vault-lock-modal.tsx @@ -0,0 +1,62 @@ +/* eslint-disable import/no-unresolved */ +import * as Dialog from '@radix-ui/react-dialog' +import { CloseIcon } from '@status-im/icons/20' +import { Button } from '@status-im/status-network/components' + +type Props = { + open: boolean + onClose: () => void + children?: React.ReactNode +} + +const VaultLockModal = (props: Props) => { + const { open, onClose, children } = props + + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) { + onClose() + } + } + + return ( + + {children} + + + +
    +
    + + + +
    + +

    + Extend lock time +

    +
    + + + Extending lock time increasing Karma boost + + +
    +
    +
    + {/* @ts-expect-error - TODO: fix this */} + + {/* @ts-expect-error - TODO: fix this */} + +
    + + + + ) +} + +export { VaultLockModal } diff --git a/apps/hub/src/app/_hooks/use-siwe.ts b/apps/hub/src/app/_hooks/use-siwe.ts new file mode 100644 index 000000000..f97e816c2 --- /dev/null +++ b/apps/hub/src/app/_hooks/use-siwe.ts @@ -0,0 +1,50 @@ +import { useCallback } from 'react' + +import { SiweMessage } from 'siwe' +import { useAccount, useSignMessage } from 'wagmi' + +export function useSiwe() { + const { address, chainId } = useAccount() + const { signMessageAsync } = useSignMessage() + + const signIn = useCallback(async () => { + if (!address || !chainId) { + throw new Error('Wallet not connected') + } + + // Create SIWE message + const message = new SiweMessage({ + domain: window.location.host, + address, + statement: 'Sign in with Ethereum to the app.', + uri: window.location.origin, + version: '1', + chainId, + nonce: await generateNonce(), + }) + + const preparedMessage = message.prepareMessage() + + // Sign the message + const signature = await signMessageAsync({ + message: preparedMessage, + }) + + // Verify the signature (you would typically do this on the backend) + // For now, we just return the signature + return { + message: preparedMessage, + signature, + } + }, [address, chainId, signMessageAsync]) + + return { + signIn, + } +} + +// Helper function to generate a nonce +async function generateNonce(): Promise { + const nonce = Math.random().toString(36).substring(2, 15) + return nonce +} diff --git a/apps/hub/src/app/_hooks/use-vault-state-machine.ts b/apps/hub/src/app/_hooks/use-vault-state-machine.ts index 133064c46..aca4eeb2a 100644 --- a/apps/hub/src/app/_hooks/use-vault-state-machine.ts +++ b/apps/hub/src/app/_hooks/use-vault-state-machine.ts @@ -58,6 +58,10 @@ function transition(state: VaultState, event: VaultEvent): VaultState { type: 'siwe', step: 'processing', })) + .with([{ type: 'siwe', step: 'initialize' }, { type: 'REJECT' }], () => ({ + type: 'siwe', + step: 'rejected', + })) .with( [{ type: 'siwe', step: 'processing' }, { type: 'COMPLETE' }], () => ({ @@ -68,6 +72,9 @@ function transition(state: VaultState, event: VaultEvent): VaultState { type: 'siwe', step: 'rejected', })) + .with([{ type: 'siwe', step: 'processing' }, { type: 'RESET' }], () => ({ + type: 'idle', + })) .with([{ type: 'siwe', step: 'rejected' }, { type: 'RESET' }], () => ({ type: 'idle', })) diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index 90155736b..56e8bcb90 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -9,6 +9,7 @@ import { defineWagmiConfig } from '../wagmi' const config = defineWagmiConfig({ chains: [mainnet], + ssr: false, }) const queryClient = new QueryClient() diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 05354eab2..b076f8efe 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { Tooltip } from '@status-im/components' import { @@ -22,6 +22,7 @@ import { LaunchIcon, SNTIcon } from '../_components/icons' import { ActionStatusDialog } from '../_components/stake/action-status-dialog' import { PromoModal } from '../_components/stake/promo-modal' import { useActionStatusContent } from '../_components/stake/use-action-status-content' +import { useSiwe } from '../_hooks/use-siwe' import { useVaultStateMachine } from '../_hooks/use-vault-state-machine' type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' @@ -29,6 +30,9 @@ type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' export default function StakePage() { const { isConnected } = useAccount() const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) + const siweHook = useSiwe() + const siweRef = useRef(siweHook) + siweRef.current = siweHook // State machine for vault operations const { @@ -43,12 +47,72 @@ export default function StakePage() { return isPromoModalOpen ? 'uninstalled' : 'disconnected' }, [isConnected, isPromoModalOpen]) - const handleCreateVault = () => { + // Track previous connection state to detect new connections + const prevConnectedRef = useRef(isConnected) + + useEffect(() => { + // Only trigger SIWE when transitioning from disconnected to connected + if ( + isConnected && + !prevConnectedRef.current && + vaultState.type === 'idle' + ) { + sendVaultEvent({ type: 'START_SIWE' }) + } + prevConnectedRef.current = isConnected + }, [isConnected, vaultState.type, sendVaultEvent]) + + const handleCreateVault = async (e: React.MouseEvent) => { + e.preventDefault() if (isConnected) { sendVaultEvent({ type: 'START_CREATE_VAULT' }) } } + useEffect(() => { + const handleWalletInteraction = async () => { + // Handle SIWE flow - initialize + if (vaultState.type === 'siwe' && vaultState.step === 'initialize') { + try { + // Sign in with wallet (this shows wallet UI for signing) + await siweRef.current.signIn() + + // Move to processing state after user signs + sendVaultEvent({ type: 'SIGN' }) + + // Give user visual feedback that it's processing + await new Promise(resolve => setTimeout(resolve, 500)) + + // Close dialog on success + resetVault() + } catch { + sendVaultEvent({ type: 'REJECT' }) + } + } + + // Handle Create Vault flow + if ( + vaultState.type === 'createVault' && + vaultState.step === 'initialize' + ) { + try { + // TODO: Replace with actual wallet signing logic + await new Promise(resolve => setTimeout(resolve, 1000)) + sendVaultEvent({ type: 'SIGN' }) + + // Simulate processing + await new Promise(resolve => setTimeout(resolve, 2000)) + sendVaultEvent({ type: 'COMPLETE' }) + } catch (error) { + console.error(error) + sendVaultEvent({ type: 'REJECT' }) + } + } + } + + handleWalletInteraction() + }, [vaultState, sendVaultEvent, resetVault]) + const handleCloseProgressDialog = () => { resetVault() } @@ -115,7 +179,7 @@ export default function StakePage() {
    -
    +
    + ) => { + e.preventDefault() + show?.() + }} > Connect Wallet @@ -241,27 +310,27 @@ export default function StakePage() { )) .with('connected', () => ( - <> - {/* @ts-expect-error - TODO: fix this */} - - {dialogContent && ( - - )} - + // @ts-expect-error - TODO: fix this + )) .exhaustive()} -
    + + + {dialogContent && ( + + )}
    diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 506e23e1b..093958e15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -297,6 +297,9 @@ importers: rehype-slug: specifier: ^6.0.0 version: 6.0.0 + siwe: + specifier: ^2.3.2 + version: 2.3.2(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@6.0.3)) ts-pattern: specifier: ^5.6.2 version: 5.7.1 @@ -8031,6 +8034,21 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@spruceid/siwe-parser@2.1.2': + resolution: {integrity: sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==} + + '@stablelib/binary@1.0.1': + resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} + + '@stablelib/int@1.0.1': + resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} + + '@stablelib/random@1.0.2': + resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} + + '@stablelib/wipe@1.0.1': + resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} + '@storybook/addon-actions@8.3.0': resolution: {integrity: sha512-HvAc3fW979JVw8CSKXZMouvgrJ2BNLNWaUB8jNokQb3Us00P6igVKLwg/pBV8GBgDr5Ng4pHYqi/ZH+xzEYFFw==} peerDependencies: @@ -10009,6 +10027,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apg-js@4.4.0: + resolution: {integrity: sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==} + arch@2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} @@ -17727,6 +17748,11 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + siwe@2.3.2: + resolution: {integrity: sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==} + peerDependencies: + ethers: ^5.6.8 || ^6.0.8 + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -19015,6 +19041,9 @@ packages: engines: {node: '>=8'} hasBin: true + valid-url@1.0.9: + resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -28273,6 +28302,26 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} + '@spruceid/siwe-parser@2.1.2': + dependencies: + '@noble/hashes': 1.8.0 + apg-js: 4.4.0 + uri-js: 4.4.1 + valid-url: 1.0.9 + + '@stablelib/binary@1.0.1': + dependencies: + '@stablelib/int': 1.0.1 + + '@stablelib/int@1.0.1': {} + + '@stablelib/random@1.0.2': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/wipe@1.0.1': {} + '@storybook/addon-actions@8.3.0(storybook@8.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.3))': dependencies: '@storybook/global': 5.0.0 @@ -31839,6 +31888,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + apg-js@4.4.0: {} + arch@2.2.0: {} arctic@1.2.0(patch_hash=np2mbzzdnalh3n27syyoxx3zu4): @@ -42630,6 +42681,14 @@ snapshots: sisteransi@1.0.5: {} + siwe@2.3.2(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@6.0.3)): + dependencies: + '@spruceid/siwe-parser': 2.1.2 + '@stablelib/random': 1.0.2 + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@6.0.3) + uri-js: 4.4.1 + valid-url: 1.0.9 + slash@3.0.0: {} slice-ansi@3.0.0: @@ -44167,6 +44226,8 @@ snapshots: kleur: 4.1.5 sade: 1.8.1 + valid-url@1.0.9: {} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.1.1 From c8b482b7d697d3e47d600c7579d993316dad3b01 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Fri, 3 Oct 2025 18:53:59 +0100 Subject: [PATCH 06/17] feat: update vault management with new table and modal components, integrate react-query and react-table --- apps/hub/package.json | 2 + .../lock-modal.tsx} | 0 .../app/_components/vaults/table-columns.tsx | 200 +++++++++ apps/hub/src/app/_components/vaults/table.tsx | 137 ++++++ apps/hub/src/app/_components/vaults/types.ts | 16 + apps/hub/src/app/providers.tsx | 17 + apps/hub/src/app/stake/page.tsx | 418 +++++++++--------- apps/hub/src/utils/currency.ts | 232 ++++++++++ package.json | 1 - pnpm-lock.yaml | 40 +- 10 files changed, 829 insertions(+), 234 deletions(-) rename apps/hub/src/app/_components/{stake/vault-lock-modal.tsx => vaults/lock-modal.tsx} (100%) create mode 100644 apps/hub/src/app/_components/vaults/table-columns.tsx create mode 100644 apps/hub/src/app/_components/vaults/table.tsx create mode 100644 apps/hub/src/app/_components/vaults/types.ts create mode 100644 apps/hub/src/utils/currency.ts diff --git a/apps/hub/package.json b/apps/hub/package.json index 864e24606..3ed17271e 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -18,6 +18,8 @@ "@status-im/components": "workspace:*", "@status-im/icons": "workspace:*", "@status-im/status-network": "workspace:*", + "@tanstack/react-query": "^5.90.2", + "@tanstack/react-table": "^8.21.3", "@vercel/analytics": "^1.5.0", "cva": "1.0.0-beta.1", "framer-motion": "^12.0.6", diff --git a/apps/hub/src/app/_components/stake/vault-lock-modal.tsx b/apps/hub/src/app/_components/vaults/lock-modal.tsx similarity index 100% rename from apps/hub/src/app/_components/stake/vault-lock-modal.tsx rename to apps/hub/src/app/_components/vaults/lock-modal.tsx diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx new file mode 100644 index 000000000..ea97f1e46 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -0,0 +1,200 @@ +/* eslint-disable import/no-unresolved */ +import { OptionsIcon, TimeIcon } from '@status-im/icons/12' +import { LockedIcon, UnlockedIcon } from '@status-im/icons/20' +import { Button } from '@status-im/status-network/components' +import { createColumnHelper } from '@tanstack/react-table' + +import { formatKarma, formatSNT } from '../../../utils/currency' + +import type { Vault } from './types' + +export const createVaultTableColumns = (data: Vault[]) => { + const totalStaked = data.reduce((acc, vault) => acc + vault.staked, BigInt(0)) + const totalKarma = data.reduce((acc, vault) => acc + vault.karma, 0) + const columnHelper = createColumnHelper() + + return [ + columnHelper.accessor('id', { + id: 'vault', + header: 'Vault', + cell: ({ row }) => { + return ( + + #{row.original.id} + + ) + }, + footer: () => { + return ( + Total + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('address', { + header: 'Address', + cell: ({ row }) => { + return ( + + {row.original.address.slice(0, 6)}... + {row.original.address.slice(-4)} + + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('staked', { + header: 'Staked', + cell: ({ row }) => { + return ( +
    + + {formatSNT(row.original.staked)} + SNT + +
    + ) + }, + footer: () => { + return ( + + {formatSNT(totalStaked)} + SNT + + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('unlocksIn', { + header: 'Unlocks in', + cell: ({ row }) => { + if (row.original.unlocksIn === null) { + return null + } + return ( +
    + + {row.original.unlocksIn} + d + +
    + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('boost', { + header: 'Boost', + cell: ({ row }) => { + if (!row.original.boost) { + return null + } + return ( +
    + + x{row.original.boost.toFixed(2)} + + {row.original.potentialBoost && ( + + x{row.original.potentialBoost.toFixed(2)} if locked + + )} +
    + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('karma', { + header: 'Karma', + cell: ({ row }) => { + return ( +
    + + {formatKarma(row.original.karma)} + KARMA + +
    + ) + }, + footer: () => { + return ( + + {formatKarma(totalKarma)} + KARMA + + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.accessor('locked', { + id: 'state', + header: 'State', + cell: ({ row }) => { + return ( +
    + {row.original.locked ? ( + + ) : ( + + )} + + {row.original.locked ? 'Locked' : 'Open'} + +
    + ) + }, + meta: { + headerClassName: 'text-left', + cellClassName: 'text-left', + }, + }), + columnHelper.display({ + id: 'actions', + header: 'Actions', + cell: ({ row }) => { + return ( +
    + {row.original.locked ? ( + // @ts-expect-error - Button component is not typed + + ) : ( + // @ts-expect-error - Button component is not typed + + )} + {/* @ts-expect-error - Button component is not typed */} + +
    + ) + }, + meta: { + headerClassName: 'text-center', + cellClassName: 'text-right', + }, + }), + ] +} diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/table.tsx new file mode 100644 index 000000000..3caa3c195 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/table.tsx @@ -0,0 +1,137 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import { useMemo } from 'react' + +import { AddCircleIcon } from '@status-im/icons/12' +import { Button } from '@status-im/status-network/components' +import { + flexRender, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table' + +import { useVaultStateMachine } from '../../_hooks/use-vault-state-machine' +import { createVaultTableColumns } from './table-columns' + +import type { Vault, VaultColumnMeta } from './types' + +const vaults: Vault[] = [ + { + id: '1', + address: '0x9A8A9958ac1B70c49ccE9693CCb0230f13F63505', + staked: 100000000n, + unlocksIn: 356, + boost: 0.03, + karma: 99.69, + locked: true, + }, + { + id: '2', + address: '0x9A8A9958ac1B70c49ccE9693CCb0230f13F63505', + staked: 100000000n, + unlocksIn: null, + boost: 0.02, + potentialBoost: 0.29, + karma: 69.69, + locked: false, + }, +] + +export const VaultsTable = () => { + const columns = useMemo(() => createVaultTableColumns(vaults), []) + const data = useMemo(() => vaults ?? [], []) + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }) + + const { send: sendVaultEvent } = useVaultStateMachine() + + const handleAddVaultModal = () => { + sendVaultEvent({ type: 'START_CREATE_VAULT' }) + } + + return ( +
    +
    +

    + My vaults +

    + {/* @ts-expect-error - Button component is not typed */} + +
    + +
    + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + ))} + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + + + {table.getFooterGroups().map(footerGroup => ( + + {footerGroup.headers.map(header => ( + + ))} + + ))} + +
    + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + +
    + {flexRender(cell.column.columnDef.cell, cell.getContext())} +
    + {header.column.columnDef.footer + ? flexRender( + header.column.columnDef.footer, + header.getContext() + ) + : null} +
    +
    +
    + ) +} diff --git a/apps/hub/src/app/_components/vaults/types.ts b/apps/hub/src/app/_components/vaults/types.ts new file mode 100644 index 000000000..e166f9cf6 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/types.ts @@ -0,0 +1,16 @@ +export interface Vault { + id: string + address: `0x${string}` + staked: bigint + unlocksIn: number | null + boost: number + potentialBoost?: number + karma: number + locked: boolean +} + +export type VaultColumnMeta = { + headerClassName?: string + cellClassName?: string + footerClassName?: string +} diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index 56e8bcb90..d162f4ee1 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -6,6 +6,9 @@ import { WagmiProvider } from 'wagmi' import { mainnet } from 'wagmi/chains' import { defineWagmiConfig } from '../wagmi' +import { ActionStatusDialog } from './_components/stake/action-status-dialog' +import { useActionStatusContent } from './_components/stake/use-action-status-content' +import { useVaultStateMachine } from './_hooks/use-vault-state-machine' const config = defineWagmiConfig({ chains: [mainnet], @@ -15,10 +18,24 @@ const config = defineWagmiConfig({ const queryClient = new QueryClient() export const Providers = ({ children }: { children: React.ReactNode }) => { + const { state: vaultState, reset: resetVault } = useVaultStateMachine() + const dialogContent = useActionStatusContent(vaultState) + return ( {children} + + {dialogContent && ( + + )} ) diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index b076f8efe..1d51a58e7 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -19,9 +19,8 @@ import { useAccount } from 'wagmi' import { HubLayout } from '~components/hub-layout' import { LaunchIcon, SNTIcon } from '../_components/icons' -import { ActionStatusDialog } from '../_components/stake/action-status-dialog' import { PromoModal } from '../_components/stake/promo-modal' -import { useActionStatusContent } from '../_components/stake/use-action-status-content' +import { VaultsTable } from '../_components/vaults/table' import { useSiwe } from '../_hooks/use-siwe' import { useVaultStateMachine } from '../_hooks/use-vault-state-machine' @@ -40,7 +39,6 @@ export default function StakePage() { send: sendVaultEvent, reset: resetVault, } = useVaultStateMachine() - const dialogContent = useActionStatusContent(vaultState) const status: ConnectionStatus = useMemo(() => { if (isConnected) return 'connected' @@ -113,262 +111,242 @@ export default function StakePage() { handleWalletInteraction() }, [vaultState, sendVaultEvent, resetVault]) - const handleCloseProgressDialog = () => { - resetVault() - } - const handleClosePromoModal = () => { setIsPromoModalOpen(false) } return ( -
    -
    -
    -

    Stake SNT, receive good Karma

    -

    - Stake SNT to increase your Karma, unlock more gasless transactions - and increase your power over the network -

    -
    +
    +
    +

    Stake SNT, receive good Karma

    +

    + Stake SNT to increase your Karma, unlock more gasless transactions + and increase your power over the network +

    +
    + +
    +
    +
    +
    +
    + Piggy Bank +
    -
    -
    -
    -
    -
    - Piggy Bank +
    +
    +

    Free Testnet SNT faucet

    -
    -
    -

    - Free Testnet SNT faucet -

    +
    +
    +

    Daily limit

    +

    10,000 SNT

    - -
    -
    -

    Daily limit

    -

    10,000 SNT

    -
    -
    -

    Used today

    -

    0 SNT

    -
    -
    -

    Available

    -

    10,000 SNT

    -
    +
    +

    Used today

    +

    0 SNT

    +
    +
    +

    Available

    +

    10,000 SNT

    - - {/* @ts-expect-error - TODO: fix this */} -
    -
    -
    -
    -
    'space-y-2') - .otherwise(() => 'space-y-2 opacity-[40%]')} + {/* @ts-expect-error - TODO: fix this */} + +
    + +
    + +
    +
    'space-y-2') + .otherwise(() => 'space-y-2 opacity-[40%]')} + > +
    -
    +
    +
    +
    ) diff --git a/apps/hub/src/utils/currency.ts b/apps/hub/src/utils/currency.ts new file mode 100644 index 000000000..663c45abd --- /dev/null +++ b/apps/hub/src/utils/currency.ts @@ -0,0 +1,232 @@ +export interface FormatTokenOptions { + /** + * Number of decimal places to display + * @default 2 + */ + decimals?: number + /** + * Minimum number of decimal places + * @default decimals value + */ + minimumFractionDigits?: number + /** + * Maximum number of decimal places + * @default decimals value + */ + maximumFractionDigits?: number + /** + * Whether to include the token symbol + * @default false + */ + includeSymbol?: boolean + /** + * Locale for number formatting + * @default 'en-US' + */ + locale?: string + /** + * Whether to use compact notation (K, M, B) + * @default false + */ + compact?: boolean + /** + * Whether to round down instead of rounding to nearest + * @default false + */ + roundDown?: boolean + /** + * Number of decimals for the token (used for bigint conversion) + * @default 18 + */ + tokenDecimals?: number +} + +/** + * Convert bigint to number with proper decimal handling + */ +function bigIntToNumber(value: bigint, decimals: number = 18): number { + const divisor = BigInt(10 ** decimals) + const whole = value / divisor + const remainder = value % divisor + + // Convert remainder to decimal + const decimalStr = remainder.toString().padStart(decimals, '0') + const decimal = Number(`0.${decimalStr}`) + + return Number(whole) + decimal +} + +/** + * Format a token amount with proper decimal handling + */ +export function formatTokenAmount( + amount: number | bigint | string, + token: string, + options: FormatTokenOptions = {} +): string { + const { + decimals = 2, + minimumFractionDigits = decimals, + maximumFractionDigits = decimals, + includeSymbol = false, + locale = 'en-US', + compact = false, + roundDown = false, + tokenDecimals = 18, + } = options + + // Convert to number + let numericAmount: number + if (typeof amount === 'bigint') { + numericAmount = bigIntToNumber(amount, tokenDecimals) + } else if (typeof amount === 'string') { + numericAmount = Number(amount) + } else { + numericAmount = amount + } + + // Round down if requested + if (roundDown) { + const factor = Math.pow(10, maximumFractionDigits) + numericAmount = Math.floor(numericAmount * factor) / factor + } + + const formatter = new Intl.NumberFormat(locale, { + minimumFractionDigits, + maximumFractionDigits, + notation: compact ? 'compact' : 'standard', + compactDisplay: compact ? 'short' : undefined, + }) + + const formatted = formatter.format(numericAmount) + + return includeSymbol ? `${formatted} ${token}` : formatted +} + +/** + * Format SNT token amount + */ +export function formatSNT( + amount: number | bigint | string, + options: Omit & { + includeSymbol?: boolean + } = {} +): string { + return formatTokenAmount(amount, 'SNT', { + ...options, + // Don't apply decimal conversion for SNT - values are already in display units + tokenDecimals: 0, + }) +} + +/** + * Format KARMA token amount + */ +export function formatKarma( + amount: number | bigint | string, + options: Omit & { + includeSymbol?: boolean + } = {} +): string { + return formatTokenAmount(amount, 'KARMA', { + ...options, + // Don't apply decimal conversion for KARMA - values are already in display units + tokenDecimals: 0, + }) +} + +/** + * Format ETH amount + */ +export function formatETH( + amount: number | bigint | string, + options: Omit & { + includeSymbol?: boolean + } = {} +): string { + return formatTokenAmount(amount, 'ETH', { + ...options, + decimals: options.decimals ?? 4, + }) +} + +/** + * Format stablecoin amount (USDC, USDT, DAI) + */ +export function formatStablecoin( + amount: number | bigint | string, + token: 'USDC' | 'USDT' | 'DAI', + options: Omit & { + includeSymbol?: boolean + } = {} +): string { + return formatTokenAmount(amount, token, options) +} + +/** + * Format a currency value for display in UI + * Automatically handles large numbers with compact notation + */ +export function formatCurrency( + amount: number | bigint | string, + options: FormatTokenOptions & { symbol?: string } = {} +): string { + const { symbol = '', tokenDecimals = 18, ...rest } = options + + let numericAmount: number + if (typeof amount === 'bigint') { + numericAmount = bigIntToNumber(amount, tokenDecimals) + } else if (typeof amount === 'string') { + numericAmount = Number(amount) + } else { + numericAmount = amount + } + + // Auto-enable compact for large numbers + const shouldCompact = + rest.compact !== false && Math.abs(numericAmount) >= 1000000 + + const formatter = new Intl.NumberFormat(rest.locale ?? 'en-US', { + minimumFractionDigits: rest.minimumFractionDigits ?? rest.decimals ?? 2, + maximumFractionDigits: rest.maximumFractionDigits ?? rest.decimals ?? 2, + notation: shouldCompact ? 'compact' : 'standard', + compactDisplay: 'short', + }) + + const formatted = formatter.format(numericAmount) + return symbol ? `${symbol}${formatted}` : formatted +} + +/** + * Parse a formatted token string back to a number + */ +export function parseTokenAmount( + formattedAmount: string, + token?: string +): number { + // Remove token symbol if present + let cleaned = formattedAmount + if (token) { + cleaned = cleaned.replace(token, '').trim() + } + + // Remove commas and other formatting + cleaned = cleaned.replace(/[,\s]/g, '') + + // Handle compact notation (K, M, B) + const multipliers: Record = { + K: 1000, + M: 1000000, + B: 1000000000, + T: 1000000000000, + } + + const match = cleaned.match(/^([\d.]+)([KMBT])$/i) + if (match) { + const [, num, suffix] = match + const multiplier = multipliers[suffix.toUpperCase()] || 1 + return Number(num) * multiplier + } + + return Number(cleaned) +} diff --git a/package.json b/package.json index a75c5eb11..e96ea1107 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ } }, "dependencies": { - "@tanstack/react-query": "^5.90.2", "connectkit": "^1.9.0", "wagmi": "^2.12.8" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 093958e15..15199684a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,9 +42,6 @@ importers: .: dependencies: - '@tanstack/react-query': - specifier: ^5.90.2 - version: 5.90.2(react@18.3.1) connectkit: specifier: ^1.9.0 version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.90.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) @@ -273,6 +270,12 @@ importers: '@status-im/status-network': specifier: workspace:* version: link:../../packages/status-network + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.2(react@19.1.0) + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@vercel/analytics': specifier: ^1.5.0 version: 1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) @@ -8629,6 +8632,13 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + '@tanstack/react-virtual@3.13.8': resolution: {integrity: sha512-meS2AanUg50f3FBSNoAdBSRAh8uS0ue01qm7zrw65KGJtiXB9QXfybqZwkh4uFpRv2iX/eu5tjcH5wqUpwYLPg==} peerDependencies: @@ -8700,6 +8710,10 @@ packages: '@tanstack/store@0.7.0': resolution: {integrity: sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==} + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + '@tanstack/virtual-core@3.13.8': resolution: {integrity: sha512-BT6w89Hqy7YKaWewYzmecXQzcJh6HTBbKYJIIkMaNU49DZ06LoTV3z32DWWEdUsgW6n1xTmwTLs4GtWrZC261w==} @@ -10710,10 +10724,6 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.1: - resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} - engines: {node: '>= 14.16.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -19801,7 +19811,7 @@ snapshots: '@babel/traverse': 7.27.1(supports-color@5.5.0) '@babel/types': 7.27.1 convert-source-map: 2.0.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -28973,6 +28983,12 @@ snapshots: react-dom: 19.1.0(react@19.1.0) use-sync-external-store: 1.5.0(react@19.1.0) + '@tanstack/react-table@8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@tanstack/react-virtual@3.13.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/virtual-core': 3.13.8 @@ -29052,6 +29068,8 @@ snapshots: '@tanstack/store@0.7.0': {} + '@tanstack/table-core@8.21.3': {} + '@tanstack/virtual-core@3.13.8': {} '@tanstack/virtual-file-routes@1.115.0': {} @@ -32805,10 +32823,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.1: - dependencies: - readdirp: 4.0.2 - chokidar@4.0.3: dependencies: readdirp: 4.0.2 @@ -42420,7 +42434,7 @@ snapshots: sass@1.80.4: dependencies: '@parcel/watcher': 2.4.1 - chokidar: 4.0.1 + chokidar: 4.0.3 immutable: 4.3.7 source-map-js: 1.2.1 From 9ddf2a5ad2137d70b550dd53f114ac2b0929f0c8 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Fri, 3 Oct 2025 19:06:37 +0100 Subject: [PATCH 07/17] feat: integrate @tanstack/react-query for improved data fetching and state management in vault components --- apps/hub/package.json | 1 - apps/hub/src/app/_components/vaults/table.tsx | 4 +- .../src/app/_hooks/vault-state-context.tsx | 39 ++ apps/hub/src/app/providers.tsx | 42 +- apps/hub/src/app/stake/page.tsx | 4 +- package.json | 1 + pnpm-lock.yaml | 542 +++++++++++------- 7 files changed, 391 insertions(+), 242 deletions(-) create mode 100644 apps/hub/src/app/_hooks/vault-state-context.tsx diff --git a/apps/hub/package.json b/apps/hub/package.json index 3ed17271e..8af32bea0 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -18,7 +18,6 @@ "@status-im/components": "workspace:*", "@status-im/icons": "workspace:*", "@status-im/status-network": "workspace:*", - "@tanstack/react-query": "^5.90.2", "@tanstack/react-table": "^8.21.3", "@vercel/analytics": "^1.5.0", "cva": "1.0.0-beta.1", diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/table.tsx index 3caa3c195..5549ac6e0 100644 --- a/apps/hub/src/app/_components/vaults/table.tsx +++ b/apps/hub/src/app/_components/vaults/table.tsx @@ -11,7 +11,7 @@ import { useReactTable, } from '@tanstack/react-table' -import { useVaultStateMachine } from '../../_hooks/use-vault-state-machine' +import { useVaultStateContext } from '../../_hooks/vault-state-context' import { createVaultTableColumns } from './table-columns' import type { Vault, VaultColumnMeta } from './types' @@ -48,7 +48,7 @@ export const VaultsTable = () => { getCoreRowModel: getCoreRowModel(), }) - const { send: sendVaultEvent } = useVaultStateMachine() + const { send: sendVaultEvent } = useVaultStateContext() const handleAddVaultModal = () => { sendVaultEvent({ type: 'START_CREATE_VAULT' }) diff --git a/apps/hub/src/app/_hooks/vault-state-context.tsx b/apps/hub/src/app/_hooks/vault-state-context.tsx new file mode 100644 index 000000000..c3ae4d8e0 --- /dev/null +++ b/apps/hub/src/app/_hooks/vault-state-context.tsx @@ -0,0 +1,39 @@ +'use client' + +import { createContext, useContext } from 'react' + +import { useVaultStateMachine } from './use-vault-state-machine' + +import type { VaultEvent, VaultState } from './use-vault-state-machine' + +type VaultStateContextType = { + state: VaultState + send: (event: VaultEvent) => void + reset: () => void +} + +const VaultStateContext = createContext(null) + +export const VaultStateProvider = ({ + children, +}: { + children: React.ReactNode +}) => { + const stateMachine = useVaultStateMachine() + + return ( + + {children} + + ) +} + +export const useVaultStateContext = () => { + const context = useContext(VaultStateContext) + if (!context) { + throw new Error( + 'useVaultStateContext must be used within VaultStateProvider' + ) + } + return context +} diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index d162f4ee1..9a3a84ae9 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -8,7 +8,10 @@ import { mainnet } from 'wagmi/chains' import { defineWagmiConfig } from '../wagmi' import { ActionStatusDialog } from './_components/stake/action-status-dialog' import { useActionStatusContent } from './_components/stake/use-action-status-content' -import { useVaultStateMachine } from './_hooks/use-vault-state-machine' +import { + useVaultStateContext, + VaultStateProvider, +} from './_hooks/vault-state-context' const config = defineWagmiConfig({ chains: [mainnet], @@ -17,25 +20,34 @@ const config = defineWagmiConfig({ const queryClient = new QueryClient() -export const Providers = ({ children }: { children: React.ReactNode }) => { - const { state: vaultState, reset: resetVault } = useVaultStateMachine() +const ProvidersContent = ({ children }: { children: React.ReactNode }) => { + const { state: vaultState, reset: resetVault } = useVaultStateContext() const dialogContent = useActionStatusContent(vaultState) + return ( + <> + {children} + + {dialogContent && ( + + )} + + ) +} + +export const Providers = ({ children }: { children: React.ReactNode }) => { return ( - {children} - - {dialogContent && ( - - )} + + + {children} + + ) diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 1d51a58e7..c20d162e7 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -22,7 +22,7 @@ import { LaunchIcon, SNTIcon } from '../_components/icons' import { PromoModal } from '../_components/stake/promo-modal' import { VaultsTable } from '../_components/vaults/table' import { useSiwe } from '../_hooks/use-siwe' -import { useVaultStateMachine } from '../_hooks/use-vault-state-machine' +import { useVaultStateContext } from '../_hooks/vault-state-context' type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' @@ -38,7 +38,7 @@ export default function StakePage() { state: vaultState, send: sendVaultEvent, reset: resetVault, - } = useVaultStateMachine() + } = useVaultStateContext() const status: ConnectionStatus = useMemo(() => { if (isConnected) return 'connected' diff --git a/package.json b/package.json index e96ea1107..a75c5eb11 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ } }, "dependencies": { + "@tanstack/react-query": "^5.90.2", "connectkit": "^1.9.0", "wagmi": "^2.12.8" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15199684a..71799cc17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: .: dependencies: + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.2(react@18.3.1) connectkit: specifier: ^1.9.0 version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.90.2(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.1.0)(react@18.3.1)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) @@ -270,9 +273,6 @@ importers: '@status-im/status-network': specifier: workspace:* version: link:../../packages/status-network - '@tanstack/react-query': - specifier: ^5.90.2 - version: 5.90.2(react@19.1.0) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -516,10 +516,10 @@ importers: version: 0.6.1 connectkit: specifier: ^1.9.0 - version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -576,10 +576,10 @@ importers: version: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -651,10 +651,10 @@ importers: version: 5.0.1 viem: specifier: ^2.21.1 - version: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + version: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) wagmi: specifier: ^2.12.8 - version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) zod: specifier: 3.23.8 version: 3.23.8 @@ -664,10 +664,10 @@ importers: version: 2.0.0(prettier@3.3.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -676,7 +676,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -790,7 +790,7 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.25.4) + version: 9.2.1(esbuild@0.19.12) nanoid: specifier: ^5.0.8 version: 5.1.5 @@ -1117,7 +1117,7 @@ importers: version: 0.6.1 contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -1177,10 +1177,10 @@ importers: version: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -1256,10 +1256,10 @@ importers: version: 2.0.0(prettier@3.5.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -1268,7 +1268,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -1382,7 +1382,7 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.19.12) + version: 9.2.1(esbuild@0.25.4) nanoid: specifier: ^5.0.8 version: 5.1.5 @@ -21593,7 +21593,7 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@babel/generator': 7.27.1 '@babel/template': 7.27.2 @@ -21608,8 +21608,8 @@ snapshots: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) - '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -21617,7 +21617,7 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.11.0 - graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) inquirer: 8.2.6 is-glob: 4.0.3 jiti: 1.21.6 @@ -21917,16 +21917,16 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@graphql-tools/executor-common': 0.0.4(graphql@16.11.0) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/disposablestack': 0.0.6 graphql: 16.11.0 - graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - '@fastify/websocket' - bufferutil @@ -21960,14 +21960,14 @@ snapshots: - bufferutil - utf-8-validate - '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/ws': 8.5.8 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -22097,9 +22097,9 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.6 @@ -22173,21 +22173,21 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: - '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/executor-http': 1.3.3(@types/node@22.7.5)(graphql@16.11.0) - '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@graphql-tools/wrap': 10.0.35(graphql@16.11.0) '@types/ws': 8.5.8 '@whatwg-node/fetch': 0.10.6 '@whatwg-node/promise-helpers': 1.3.1 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) sync-fetch: 0.6.0-2 tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -23100,6 +23100,21 @@ snapshots: '@metamask/safe-event-emitter@3.1.2': {} + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3))': + dependencies: + bufferutil: 4.0.9 + cross-fetch: 4.1.0 + date-fns: 2.30.0 + debug: 4.4.0 + eciesjs: 0.4.14 + eventemitter2: 6.4.9 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + utf-8-validate: 5.0.10 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: bufferutil: 4.0.9 @@ -23119,6 +23134,33 @@ snapshots: dependencies: '@paulmillr/qr': 0.2.1 + '@metamask/sdk@0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + dependencies: + '@babel/runtime': 7.27.1 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + '@metamask/sdk-install-modal-web': 0.32.0 + '@paulmillr/qr': 0.2.1 + bowser: 2.11.0 + cross-fetch: 4.1.0 + debug: 4.4.0 + eciesjs: 0.4.14 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.2 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.27.1 @@ -27710,35 +27752,35 @@ snapshots: '@react-types/shared': 3.24.1(react@18.3.1) react: 18.3.1 - '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27766,13 +27808,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27804,13 +27846,13 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -27840,13 +27882,13 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -27876,11 +27918,11 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -27910,11 +27952,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -27944,16 +27986,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27981,16 +28023,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28018,9 +28060,9 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 '@walletconnect/logger': 2.1.2 zod: 3.23.8 @@ -28029,9 +28071,9 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 '@walletconnect/logger': 2.1.2 zod: 3.23.8 @@ -28040,20 +28082,20 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28081,20 +28123,20 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28212,9 +28254,9 @@ snapshots: '@rushstack/eslint-patch@1.11.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -28222,9 +28264,9 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -28232,20 +28274,20 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript @@ -30577,16 +30619,16 @@ snapshots: '@vue/shared@3.3.4': {} - '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': + '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)': dependencies: '@coinbase/wallet-sdk': 4.3.0 - '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) - '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@metamask/sdk': 0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) + '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -30655,11 +30697,11 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))': + '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.8.3) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) zustand: 5.0.0(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.29.0 @@ -30817,13 +30859,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30831,7 +30873,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30860,7 +30902,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -30874,7 +30916,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30903,13 +30945,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30917,7 +30959,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30946,7 +30988,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -30960,7 +31002,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30993,18 +31035,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31033,18 +31075,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31110,6 +31152,16 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 tslib: 1.14.1 + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 @@ -31165,16 +31217,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31200,16 +31252,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31235,16 +31287,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31270,16 +31322,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31365,7 +31417,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31374,9 +31426,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31404,7 +31456,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31413,9 +31465,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31443,7 +31495,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31452,9 +31504,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31482,7 +31534,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31491,9 +31543,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31521,7 +31573,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31539,7 +31591,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31564,7 +31616,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31582,7 +31634,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31607,7 +31659,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31625,7 +31677,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31650,7 +31702,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31668,7 +31720,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -33069,12 +33121,12 @@ snapshots: graceful-fs: 4.2.11 xdg-basedir: 5.1.0 - connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) buffer: 6.0.3 detect-browser: 5.3.0 - family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) framer-motion: 6.5.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) qrcode: 1.5.4 react: 19.1.0 @@ -33083,8 +33135,8 @@ snapshots: react-use-measure: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) resize-observer-polyfill: 1.5.1 styled-components: 5.3.11(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) transitivePeerDependencies: - '@babel/core' - react-is @@ -33885,6 +33937,18 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 @@ -35162,12 +35226,12 @@ snapshots: viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) wagmi: 2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) - family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) fast-check@3.16.0: dependencies: @@ -35874,13 +35938,13 @@ snapshots: - uWebSockets.js - utf-8-validate - graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10): + graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3): dependencies: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) '@graphql-tools/merge': 9.0.24(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) cosmiconfig: 8.1.3 graphql: 16.11.0 @@ -35918,11 +35982,11 @@ snapshots: optionalDependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): dependencies: graphql: 16.11.0 optionalDependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) graphql@15.9.0: {} @@ -36877,14 +36941,22 @@ snapshots: dependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): dependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + + isows@1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + dependencies: + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + dependencies: + ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + isows@1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -38984,12 +39056,12 @@ snapshots: netmask@2.0.2: {} - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.19.12) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.19.12) - next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -38998,12 +39070,12 @@ snapshots: - markdown-wasm - supports-color - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.25.4) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.25.4) - next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -42743,6 +42815,17 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 + socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-client: 6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 @@ -44329,69 +44412,69 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - viem@2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) - isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.6.2)(zod@3.23.8) - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + ox: 0.6.7(typescript@5.8.3)(zod@3.23.8) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.8.3)(zod@3.23.8) + ox: 0.6.7(typescript@5.6.2)(zod@3.23.8) ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.8.3 + typescript: 5.6.2 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): dependencies: '@noble/curves': 1.8.2 '@noble/hashes': 1.7.2 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) - isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.9(typescript@5.6.2)(zod@3.23.8) - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) + ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.2 '@noble/hashes': 1.7.2 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) + ox: 0.6.9(typescript@5.6.2)(zod@3.23.8) ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.8.3 + typescript: 5.6.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -44528,14 +44611,14 @@ snapshots: '@vue/server-renderer': 3.3.4(vue@3.3.4) '@vue/shared': 3.3.4 - wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): + wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) - '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -44831,6 +44914,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -44841,6 +44929,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 6.0.3 + ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -44851,6 +44944,11 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 6.0.3 + ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 From 4e9e17e6586d0265746497bb336f4512dde01ad0 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Fri, 3 Oct 2025 21:21:45 +0100 Subject: [PATCH 08/17] feat: enhance vault management with new compound modal and lock configuration, update table actions for improved user experience --- apps/hub/package.json | 3 +- .../app/_components/vaults/compound-modal.tsx | 315 +++++++++ .../src/app/_components/vaults/lock-modal.tsx | 239 ++++++- .../app/_components/vaults/table-columns.tsx | 104 ++- apps/hub/src/app/_components/vaults/table.tsx | 16 +- packages/components/src/button/button.tsx | 2 +- packages/components/src/button/index.tsx | 1 + pnpm-lock.yaml | 636 ++++++++---------- 8 files changed, 905 insertions(+), 411 deletions(-) create mode 100644 apps/hub/src/app/_components/vaults/compound-modal.tsx diff --git a/apps/hub/package.json b/apps/hub/package.json index 8af32bea0..f3d329a5e 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -28,8 +28,7 @@ "react-dom": "^19.0.0", "rehype-slug": "^6.0.0", "siwe": "^2.3.2", - "ts-pattern": "^5.6.2", - "zod": "^3.24.1" + "ts-pattern": "^5.6.2" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.3.1", diff --git a/apps/hub/src/app/_components/vaults/compound-modal.tsx b/apps/hub/src/app/_components/vaults/compound-modal.tsx new file mode 100644 index 000000000..d07be1cc6 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/compound-modal.tsx @@ -0,0 +1,315 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import * as Dialog from '@radix-ui/react-dialog' +import { InfoIcon } from '@status-im/icons/16' +import { CloseIcon } from '@status-im/icons/20' +import Image from 'next/image' + +const img = + 'http://localhost:3845/assets/545aa67a6eecd0a49090b45f7d6a973e4872182a.svg' +const img1 = + 'http://localhost:3845/assets/43c3f8200f12a3f272bcfe34fd5a4fc09d6aaf18.svg' +const img2 = + 'http://localhost:3845/assets/eafc1691ee0ee47b7e6dd412dc2ba15302ee831e.svg' +const img3 = + 'http://localhost:3845/assets/7624c6c65a9f8dc84102de40794499658fa2d785.svg' + +type Props = { + open: boolean + onClose: () => void + children?: React.ReactNode + points?: number + totalCompounded?: number + earnRate?: number + boost?: number + equivalentRate?: number + equivalentBoost?: number +} + +const VaultCompoundModal = (props: Props) => { + const { + open, + onClose, + children, + points = 124, + totalCompounded = 123345, + earnRate = 83, + boost = 2.035, + equivalentRate = 39, + equivalentBoost = 0.0, + } = props + + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) { + onClose() + } + } + + return ( + + {children} + + + +
    + {/* Close button */} + + + + + {/* Icon section */} +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + + {/* Text Combinations */} +
    + {/* Title */} +
    + +
    +

    + Ready to compound {points} points +

    +
    +
    + +
    +

    + Please sign the message in your wallet. +

    +
    +
    +
    + + {/* Stats */} +
    + {/* Total compounded */} +
    +
    +

    Total compounded

    +
    +
    +

    + {totalCompounded.toLocaleString()} points +

    +
    +
    + + {/* Your earn rate */} +
    +
    +

    + Your earn rate at x{boost} boost +

    +
    +
    +

    {earnRate} Karma / day

    +
    +
    + + {/* Equivalent */} +
    +
    +

    + Equivalent at x{equivalentBoost.toFixed(2)} boost +

    +
    +
    +

    + {equivalentRate} Karma / day +

    +
    +
    +
    + + {/* Information Box */} +
    +
    +
    + +
    +
    +
    +

    + Boost the rate at which you receive Karma. More points you + compound, the higher your rate. The longer you lock your + vault, the higher your boost, and the faster you accumulate + Karma. +

    +
    +
    +
    +
    +
    +
    +
    + ) +} + +export { VaultCompoundModal } diff --git a/apps/hub/src/app/_components/vaults/lock-modal.tsx b/apps/hub/src/app/_components/vaults/lock-modal.tsx index 53714f554..172e78633 100644 --- a/apps/hub/src/app/_components/vaults/lock-modal.tsx +++ b/apps/hub/src/app/_components/vaults/lock-modal.tsx @@ -1,57 +1,252 @@ /* eslint-disable import/no-unresolved */ +'use client' + +import { useState } from 'react' + import * as Dialog from '@radix-ui/react-dialog' +import { ContextTag, Input } from '@status-im/components' +import { InfoIcon } from '@status-im/icons/16' import { CloseIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' +import type { HTMLAttributes } from 'react' + +type ActionButton = HTMLAttributes & { + label: string + disabled?: boolean +} + type Props = { - open: boolean + open?: boolean + onOpenChange?: (open: boolean) => void onClose: () => void + title: string + description: string children?: React.ReactNode + // Slider configuration + sliderConfig?: { + minLabel: string + maxLabel: string + minDays: number + maxDays: number + initialPosition?: number // 0-100 percentage + } + // Initial values + initialYears?: string + initialDays?: string + // Boost and unlock info + boost?: string + unlockDate?: string + // Info box content + infoMessage?: string + // Error validation + errorMessage?: string | null + onValidate?: (years: string, days: string) => string | null + // Footer actions (left to right) + actions: [ActionButton, ActionButton] } -const VaultLockModal = (props: Props) => { - const { open, onClose, children } = props +const VaultLockConfigModal = (props: Props) => { + const { + open, + onOpenChange, + onClose, + children, + title, + description, + sliderConfig = { + minLabel: '1 year', + maxLabel: '4 years', + minDays: 365, + maxDays: 1460, + initialPosition: 0, + }, + initialYears = '1', + initialDays = '365', + boost = 'x2.5', + unlockDate = '30/01/2026', + infoMessage = 'Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks.', + errorMessage: externalErrorMessage, + onValidate, + actions, + } = props + + const [years, setYears] = useState(initialYears) + const [days, setDays] = useState(initialDays) + const [errorMessage, setErrorMessage] = useState(null) const handleOpenChange = (nextOpen: boolean) => { + if (onOpenChange) { + onOpenChange(nextOpen) + } if (!nextOpen) { onClose() } } + const handleYearsChange = (value: string) => { + setYears(value) + if (onValidate) { + const error = onValidate(value, days) + setErrorMessage(error) + } + } + + const handleDaysChange = (value: string) => { + setDays(value) + if (onValidate) { + const error = onValidate(years, value) + setErrorMessage(error) + } + } + + const displayError = externalErrorMessage || errorMessage + const hasError = Boolean(displayError) + return ( {children} - -
    -
    + +
    -
    + +
    -

    - Extend lock time -

    +
    +

    + {title} +

    +
    - - Extending lock time increasing Karma boost - +
    +

    {description}

    +
    -
    -
    - {/* @ts-expect-error - TODO: fix this */} - - {/* @ts-expect-error - TODO: fix this */} - + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    {sliderConfig.minLabel}

    +

    {sliderConfig.maxLabel}

    +
    +
    + +
    +
    + +
    +

    + or +

    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    {displayError || '\u00A0'}

    +
    +
    +
    + +
    +
    +
    +

    Boost:

    +
    + + {boost} + +
    +
    +
    +

    Unlock:

    +
    + + {unlockDate} + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    {infoMessage}

    +
    +
    +
    + +
    +
    + {/* @ts-expect-error - Button component is not typed */} + + {/* @ts-expect-error - Button component is not typed */} + +
    +
    @@ -59,4 +254,6 @@ const VaultLockModal = (props: Props) => { ) } -export { VaultLockModal } +export { VaultLockConfigModal } +// Backward compatibility +export { VaultLockConfigModal as VaultLockModal } diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index ea97f1e46..79c4e1213 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -1,14 +1,26 @@ /* eslint-disable import/no-unresolved */ + import { OptionsIcon, TimeIcon } from '@status-im/icons/12' import { LockedIcon, UnlockedIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' import { createColumnHelper } from '@tanstack/react-table' import { formatKarma, formatSNT } from '../../../utils/currency' +import { VaultLockConfigModal } from './lock-modal' import type { Vault } from './types' -export const createVaultTableColumns = (data: Vault[]) => { +interface TableColumnsProps { + data: Vault[] + openModalVaultId: string | null + setOpenModalVaultId: (vaultId: string | null) => void +} + +export const createVaultTableColumns = ({ + data, + openModalVaultId, + setOpenModalVaultId, +}: TableColumnsProps) => { const totalStaked = data.reduce((acc, vault) => acc + vault.staked, BigInt(0)) const totalKarma = data.reduce((acc, vault) => acc + vault.karma, 0) const columnHelper = createColumnHelper() @@ -169,20 +181,90 @@ export const createVaultTableColumns = (data: Vault[]) => { id: 'actions', header: 'Actions', cell: ({ row }) => { + const isModalOpen = openModalVaultId === row.original.id + return (
    {row.original.locked ? ( - // @ts-expect-error - Button component is not typed - + + setOpenModalVaultId(open ? row.original.id : null) + } + title="Extend lock time" + description="Extending lock time increasing Karma boost" + actions={[ + { + label: 'Cancel', + onClick: () => setOpenModalVaultId(null), + }, + { + label: 'Extend lock', + onClick: () => { + // Handle extend lock logic + setOpenModalVaultId(null) + }, + }, + ]} + onClose={() => setOpenModalVaultId(null)} + boost="x2.5" + unlockDate="30/01/2026" + infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." + > + {/* @ts-expect-error - Button component is not typed */} + + ) : ( - // @ts-expect-error - Button component is not typed - + + setOpenModalVaultId(open ? row.original.id : null) + } + title="Do you want to lock the vault?" + description="Lock this vault to receive more Karma" + sliderConfig={{ + minLabel: '90 days', + maxLabel: '4 years', + minDays: 90, + maxDays: 1460, + initialPosition: 50, + }} + initialYears="2" + initialDays="732" + actions={[ + { + label: "Don't lock", + onClick: () => setOpenModalVaultId(null), + }, + { + label: 'Lock', + onClick: () => { + // Handle lock logic + setOpenModalVaultId(null) + }, + }, + ]} + onClose={() => setOpenModalVaultId(null)} + boost="x2.5" + unlockDate="30/01/2025" + infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." + onValidate={(years, days) => { + const totalDays = + parseInt(years || '0') * 365 + parseInt(days || '0') + return totalDays > 1460 + ? 'Maximum lock time is 4 years' + : null + }} + > + {/* @ts-expect-error - Button component is not typed */} + + )} {/* @ts-expect-error - Button component is not typed */} - - - {/* Icon section */} -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - - {/* Text Combinations */} -
    - {/* Title */} -
    - -
    -

    - Ready to compound {points} points -

    -
    -
    - -
    -

    - Please sign the message in your wallet. -

    -
    -
    -
    - - {/* Stats */} -
    - {/* Total compounded */} -
    -
    -

    Total compounded

    -
    -
    -

    - {totalCompounded.toLocaleString()} points -

    -
    -
    - - {/* Your earn rate */} -
    -
    -

    - Your earn rate at x{boost} boost -

    -
    -
    -

    {earnRate} Karma / day

    -
    -
    - - {/* Equivalent */} -
    -
    -

    - Equivalent at x{equivalentBoost.toFixed(2)} boost -

    -
    -
    -

    - {equivalentRate} Karma / day -

    -
    -
    -
    - - {/* Information Box */} -
    -
    -
    - -
    -
    -
    -

    - Boost the rate at which you receive Karma. More points you - compound, the higher your rate. The longer you lock your - vault, the higher your boost, and the faster you accumulate - Karma. -

    -
    -
    -
    -
    - - - - ) -} - -export { VaultCompoundModal } diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index 79c4e1213..15a694538 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -1,12 +1,13 @@ /* eslint-disable import/no-unresolved */ -import { OptionsIcon, TimeIcon } from '@status-im/icons/12' +import { AlertIcon, OptionsIcon, TimeIcon } from '@status-im/icons/12' import { LockedIcon, UnlockedIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' import { createColumnHelper } from '@tanstack/react-table' import { formatKarma, formatSNT } from '../../../utils/currency' -import { VaultLockConfigModal } from './lock-modal' +import { VaultLockConfigModal } from './vault-lock-modal' +import { VaultWithdrawModal } from './withdraw-modal' import type { Vault } from './types' @@ -181,15 +182,40 @@ export const createVaultTableColumns = ({ id: 'actions', header: 'Actions', cell: ({ row }) => { - const isModalOpen = openModalVaultId === row.original.id + const withdrawModalId = `withdraw-${row.original.id}` + const lockModalId = `lock-${row.original.id}` + const isWithdrawModalOpen = openModalVaultId === withdrawModalId + const isLockModalOpen = openModalVaultId === lockModalId return (
    + + setOpenModalVaultId(open ? withdrawModalId : null) + } + onClose={() => setOpenModalVaultId(null)} + onWithdraw={() => { + // Handle withdraw logic + console.log('Withdrawing from vault:', row.original.id) + }} + withdrawAddress={row.original.address} + > + {/* @ts-expect-error - Button component is not typed */} + + {row.original.locked ? ( - setOpenModalVaultId(open ? row.original.id : null) + setOpenModalVaultId(open ? lockModalId : null) } title="Extend lock time" description="Extending lock time increasing Karma boost" @@ -219,9 +245,9 @@ export const createVaultTableColumns = ({ ) : ( - setOpenModalVaultId(open ? row.original.id : null) + setOpenModalVaultId(open ? lockModalId : null) } title="Do you want to lock the vault?" description="Lock this vault to receive more Karma" diff --git a/apps/hub/src/app/_components/vaults/lock-modal.tsx b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx similarity index 98% rename from apps/hub/src/app/_components/vaults/lock-modal.tsx rename to apps/hub/src/app/_components/vaults/vault-lock-modal.tsx index 172e78633..ea1cd6503 100644 --- a/apps/hub/src/app/_components/vaults/lock-modal.tsx +++ b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx @@ -6,7 +6,7 @@ import { useState } from 'react' import * as Dialog from '@radix-ui/react-dialog' import { ContextTag, Input } from '@status-im/components' import { InfoIcon } from '@status-im/icons/16' -import { CloseIcon } from '@status-im/icons/20' +import { CloseIcon, IncorrectIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' import type { HTMLAttributes } from 'react' @@ -182,7 +182,7 @@ const VaultLockConfigModal = (props: Props) => {
    - +
    diff --git a/apps/hub/src/app/_components/vaults/withdraw-modal.tsx b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx new file mode 100644 index 000000000..ce41b1955 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx @@ -0,0 +1,195 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import * as Dialog from '@radix-ui/react-dialog' +import { InfoIcon } from '@status-im/icons/12' +import { CloseIcon } from '@status-im/icons/20' +import { Button } from '@status-im/status-network/components' + +type Props = { + open?: boolean + onOpenChange?: (open: boolean) => void + onClose: () => void + onWithdraw: () => void + withdrawAddress?: string + children?: React.ReactNode +} + +const VaultWithdrawModal = (props: Props) => { + const { + open, + onOpenChange, + onClose, + onWithdraw, + withdrawAddress = '0x014b7f0Ba4C4530735616e1Ee7ff5FbCB726f64ba', + children, + } = props + + const handleOpenChange = (nextOpen: boolean) => { + if (onOpenChange) { + onOpenChange(nextOpen) + } + if (!nextOpen) { + onClose() + } + } + + const handleWithdraw = () => { + onWithdraw() + onClose() + } + + return ( + + {children} + + + +
    + {/* Close button */} + + + + + {/* Header */} +
    +
    +
    + +
    +

    + Emergency withdrawal +

    +
    +
    + +
    +

    + In the event of a hack or contract compromise, you can + use this feature to immediately withdraw your funds from + the vault. +

    +
    +
    +
    +
    +
    + + {/* Withdraw address input */} +
    +
    +
    +
    +

    + Withdraw to +

    +
    +
    +
    +

    + {withdrawAddress} +

    +
    +
    +
    +
    +
    + + {/* Info box */} +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    + Your funds will sent directly to your connected wallet. +

    +
    + {/* @ts-expect-error - Button component is not typed */} + +
    +
    +
    +
    + + {/* Footer actions */} +
    +
    + {/* @ts-expect-error - Button component is not typed */} + + {/* @ts-expect-error - Button component is not typed */} + +
    +
    +
    +
    +
    +
    + ) +} + +export { VaultWithdrawModal } From a2109bdf29379d6a6ccc966c325088faf4ff09de Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Sun, 5 Oct 2025 15:54:44 +0100 Subject: [PATCH 10/17] feat: enhance vault management with new hooks for account vaults and faucet integration, update UI components for improved user experience --- apps/hub/package.json | 3 +- .../stake/use-action-status-content.tsx | 2 +- .../app/_components/vaults/table-columns.tsx | 44 +- apps/hub/src/app/_components/vaults/table.tsx | 236 +++--- .../_components/vaults/vault-lock-modal.tsx | 2 +- .../app/_components/vaults/withdraw-modal.tsx | 97 +-- apps/hub/src/app/_hooks/useAccountVaults.ts | 234 ++++++ apps/hub/src/app/_hooks/useFaucet.ts | 224 +++++ .../app/_hooks/{use-siwe.ts => useSIWE.ts} | 0 ...ate-machine.ts => useVaultStateMachine.ts} | 8 +- apps/hub/src/app/_hooks/useWeightedBoost.ts | 173 ++++ .../src/app/_hooks/vault-state-context.tsx | 4 +- apps/hub/src/app/contracts/faucet.ts | 50 ++ apps/hub/src/app/contracts/index.ts | 5 + .../src/app/contracts/stakingManagerAbi.ts | 771 ++++++++++++++++++ apps/hub/src/app/contracts/tokenAbi.ts | 60 ++ apps/hub/src/app/contracts/vaultAbi.ts | 229 ++++++ apps/hub/src/app/contracts/vaultFactoryAbi.ts | 11 + apps/hub/src/app/providers.tsx | 4 +- apps/hub/src/app/stake/page.tsx | 205 +++-- apps/hub/src/config/address.ts | 20 + apps/hub/src/config/chain.ts | 25 + apps/hub/src/config/index.ts | 1 + apps/hub/src/utils/currency.ts | 300 +++++-- apps/hub/src/utils/vault.ts | 60 ++ pnpm-lock.yaml | 71 +- 26 files changed, 2497 insertions(+), 342 deletions(-) create mode 100644 apps/hub/src/app/_hooks/useAccountVaults.ts create mode 100644 apps/hub/src/app/_hooks/useFaucet.ts rename apps/hub/src/app/_hooks/{use-siwe.ts => useSIWE.ts} (100%) rename apps/hub/src/app/_hooks/{use-vault-state-machine.ts => useVaultStateMachine.ts} (97%) create mode 100644 apps/hub/src/app/_hooks/useWeightedBoost.ts create mode 100644 apps/hub/src/app/contracts/faucet.ts create mode 100644 apps/hub/src/app/contracts/index.ts create mode 100644 apps/hub/src/app/contracts/stakingManagerAbi.ts create mode 100644 apps/hub/src/app/contracts/tokenAbi.ts create mode 100644 apps/hub/src/app/contracts/vaultAbi.ts create mode 100644 apps/hub/src/app/contracts/vaultFactoryAbi.ts create mode 100644 apps/hub/src/config/address.ts create mode 100644 apps/hub/src/config/chain.ts create mode 100644 apps/hub/src/config/index.ts create mode 100644 apps/hub/src/utils/vault.ts diff --git a/apps/hub/package.json b/apps/hub/package.json index f3d329a5e..083f305e8 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -28,7 +28,8 @@ "react-dom": "^19.0.0", "rehype-slug": "^6.0.0", "siwe": "^2.3.2", - "ts-pattern": "^5.6.2" + "ts-pattern": "^5.6.2", + "viem": "^2.21.1" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.3.1", diff --git a/apps/hub/src/app/_components/stake/use-action-status-content.tsx b/apps/hub/src/app/_components/stake/use-action-status-content.tsx index 15e4da193..9f07ff22a 100644 --- a/apps/hub/src/app/_components/stake/use-action-status-content.tsx +++ b/apps/hub/src/app/_components/stake/use-action-status-content.tsx @@ -1,6 +1,6 @@ import { match } from 'ts-pattern' -import type { VaultState } from '../../_hooks/use-vault-state-machine' +import type { VaultState } from '../../_hooks/useVaultStateMachine' export type ActionStatusState = | 'pending' // Waiting for user action (sign, approve) diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index 15a694538..8c37abd6f 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -33,7 +33,7 @@ export const createVaultTableColumns = ({ cell: ({ row }) => { return ( - #{row.original.id} + #{Number(row.original.id) + 1} ) }, @@ -188,27 +188,26 @@ export const createVaultTableColumns = ({ const isLockModalOpen = openModalVaultId === lockModalId return ( -
    +
    setOpenModalVaultId(open ? withdrawModalId : null) } onClose={() => setOpenModalVaultId(null)} - onWithdraw={() => { - // Handle withdraw logic - console.log('Withdrawing from vault:', row.original.id) - }} - withdrawAddress={row.original.address} + vaultAdress={row.original.address} > {/* @ts-expect-error - Button component is not typed */} {row.original.locked ? ( @@ -238,9 +237,16 @@ export const createVaultTableColumns = ({ infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." > {/* @ts-expect-error - Button component is not typed */} - ) : ( @@ -286,14 +292,18 @@ export const createVaultTableColumns = ({ }} > {/* @ts-expect-error - Button component is not typed */} - )} {/* @ts-expect-error - Button component is not typed */} -
    diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/table.tsx index 674a97a83..a66c57710 100644 --- a/apps/hub/src/app/_components/vaults/table.tsx +++ b/apps/hub/src/app/_components/vaults/table.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { AddCircleIcon } from '@status-im/icons/12' import { Button } from '@status-im/status-network/components' @@ -11,36 +11,128 @@ import { useReactTable, } from '@tanstack/react-table' +import { transformVaults } from '../../../utils/vault' +import { useAccountVaults } from '../../_hooks/useAccountVaults' import { useVaultStateContext } from '../../_hooks/vault-state-context' import { createVaultTableColumns } from './table-columns' import type { Vault, VaultColumnMeta } from './types' -const vaults: Vault[] = [ - { - id: '1', - address: '0x9A8A9958ac1B70c49ccE9693CCb0230f13F63505', - staked: 100000000n, - unlocksIn: 356, - boost: 0.03, - karma: 99.69, - locked: true, - }, - { - id: '2', - address: '0x9A8A9958ac1B70c49ccE9693CCb0230f13F63505', - staked: 100000000n, - unlocksIn: null, - boost: 0.02, - potentialBoost: 0.29, - karma: 69.69, - locked: false, - }, -] - -export const VaultsTable = () => { +/** + * Gets the appropriate CSS class for table cell/header based on meta + */ +function getCellClassName( + meta: VaultColumnMeta | undefined, + defaultClassName = 'text-left' +): string { + return meta?.cellClassName || defaultClassName +} + +function getHeaderClassName( + meta: VaultColumnMeta | undefined, + defaultClassName = 'text-left' +): string { + return meta?.headerClassName || defaultClassName +} + +// ============================================================================ +// Sub-components +// ============================================================================ + +interface TableHeaderProps { + table: ReturnType> +} + +function TableHeader({ table }: TableHeaderProps) { + return ( + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + + ))} + + ))} + + ) +} + +interface TableBodyProps { + table: ReturnType> +} + +function TableBody({ table }: TableBodyProps) { + return ( + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + ) +} + +interface TableFooterProps { + table: ReturnType> +} + +function TableFooter({ table }: TableFooterProps) { + return ( + + {table.getFooterGroups().map(footerGroup => ( + + {footerGroup.headers.map(header => ( + + {header.column.columnDef.footer + ? flexRender( + header.column.columnDef.footer, + header.getContext() + ) + : null} + + ))} + + ))} + + ) +} + +// ============================================================================ +// Main Component +// ============================================================================ + +export function VaultsTable() { const [openModalVaultId, setOpenModalVaultId] = useState(null) const { send: sendVaultEvent } = useVaultStateContext() + const { data: vaultDataList } = useAccountVaults() + + // Transform vault data from contract format to UI format + const vaults = useMemo( + () => (vaultDataList ? transformVaults(vaultDataList) : []), + [vaultDataList] + ) + const columns = useMemo( () => createVaultTableColumns({ @@ -48,97 +140,45 @@ export const VaultsTable = () => { openModalVaultId, setOpenModalVaultId, }), - [openModalVaultId] + [vaults, openModalVaultId] ) - const data = useMemo(() => vaults ?? [], []) const table = useReactTable({ - data, + data: vaults, columns, getCoreRowModel: getCoreRowModel(), }) - const handleAddVaultModal = () => { + const handleAddVaultModal = useCallback(() => { sendVaultEvent({ type: 'START_CREATE_VAULT' }) - } + }, [sendVaultEvent]) return ( -
    -
    -

    +
    +
    +

    My vaults

    {/* @ts-expect-error - Button component is not typed */} -
    -
    - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => ( - - ))} - - ))} - - - {table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => ( - - ))} - - ))} - - - {table.getFooterGroups().map(footerGroup => ( - - {footerGroup.headers.map(header => ( - - ))} - - ))} - -
    - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - -
    - {flexRender(cell.column.columnDef.cell, cell.getContext())} -
    - {header.column.columnDef.footer - ? flexRender( - header.column.columnDef.footer, - header.getContext() - ) - : null} -
    +
    +
    + + + + +
    +
    ) diff --git a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx index ea1cd6503..ede184d7c 100644 --- a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx +++ b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx @@ -186,7 +186,7 @@ const VaultLockConfigModal = (props: Props) => {

    -

    {displayError || '\u00A0'}

    +

    {displayError}

    diff --git a/apps/hub/src/app/_components/vaults/withdraw-modal.tsx b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx index ce41b1955..6de7d0804 100644 --- a/apps/hub/src/app/_components/vaults/withdraw-modal.tsx +++ b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx @@ -5,25 +5,32 @@ import * as Dialog from '@radix-ui/react-dialog' import { InfoIcon } from '@status-im/icons/12' import { CloseIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' +import { useAccount, useReadContract, useWriteContract } from 'wagmi' + +import { SNT_TOKEN } from '../../../config' +import { vaultAbi } from '../../contracts' + +import type { Address } from 'viem' type Props = { + onClose: () => void + vaultAdress: Address open?: boolean onOpenChange?: (open: boolean) => void - onClose: () => void - onWithdraw: () => void - withdrawAddress?: string children?: React.ReactNode } const VaultWithdrawModal = (props: Props) => { - const { - open, - onOpenChange, - onClose, - onWithdraw, - withdrawAddress = '0x014b7f0Ba4C4530735616e1Ee7ff5FbCB726f64ba', - children, - } = props + const { onClose, vaultAdress, open, onOpenChange, children } = props + + const { address } = useAccount() + const { writeContract } = useWriteContract() + const { data: availableWithdraw } = useReadContract({ + abi: vaultAbi, + address: vaultAdress, + functionName: 'availableWithdraw', + args: [SNT_TOKEN.address], + }) const handleOpenChange = (nextOpen: boolean) => { if (onOpenChange) { @@ -34,8 +41,13 @@ const VaultWithdrawModal = (props: Props) => { } } - const handleWithdraw = () => { - onWithdraw() + const handleVaultWithdrawal = () => { + writeContract({ + abi: vaultAbi, + address: vaultAdress, + functionName: 'unstake', + args: [availableWithdraw || 0n], + }) onClose() } @@ -46,40 +58,20 @@ const VaultWithdrawModal = (props: Props) => {
    - {/* Close button */} - {/* Header */} -
    -
    -
    +
    +
    +
    -
    +

    Emergency withdrawal

    @@ -98,12 +90,7 @@ const VaultWithdrawModal = (props: Props) => {
    - {/* Withdraw address input */} -
    +
    @@ -114,7 +101,7 @@ const VaultWithdrawModal = (props: Props) => {

    - {withdrawAddress} + {address}

    @@ -122,17 +109,8 @@ const VaultWithdrawModal = (props: Props) => {
    - {/* Info box */} -
    -
    +
    +
    @@ -158,12 +136,7 @@ const VaultWithdrawModal = (props: Props) => {
    - {/* Footer actions */} -
    +
    {/* @ts-expect-error - Button component is not typed */} + * ) + * } + * ``` + */ +export function useFaucetMutation(): UseMutationResult< + Address, + Error, + void, + unknown +> { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + + return useMutation({ + mutationKey: [QUERY_KEY_PREFIX, 'request', address], + mutationFn: async (): Promise
    => { + if (!address) { + throw new Error('Wallet not connected') + } + + const hash = await writeContractAsync({ + address: FAUCET.address, + abi: faucetAbi, + functionName: 'requestTokens', + args: [address], + }) + + return hash + }, + }) +} + +// ============================================================================ +// Query Hook +// ============================================================================ + +/** + * Query hook to fetch faucet data for the connected account + * + * Fetches: + * - Daily token request limit + * - Number of requests made by the account today + * - Reset time for the account's daily limit + * + * @param options - Query configuration options + * @returns Query result with faucet state data + * + * @example + * ```tsx + * function FaucetInfo() { + * const { data, isLoading } = useFaucetQuery() + * + * if (isLoading) return + * if (!data) return null + * + * return ( + *
    + *

    Requests remaining: {data.remainingRequests.toString()}

    + *

    Can request: {data.canRequest ? 'Yes' : 'No'}

    + *
    + * ) + * } + * ``` + */ +export function useFaucetQuery( + options?: UseFaucetQueryOptions +): UseQueryResult { + const config = useConfig() + const { address } = useAccount() + const chainId = useChainId() + + return useQuery({ + queryKey: [QUERY_KEY_PREFIX, address, chainId] as const, + queryFn: async (): Promise => { + if (!address) { + throw new Error('Wallet not connected') + } + + const results = await readContracts(config, { + contracts: [ + { + chainId, + address: FAUCET.address, + abi: faucetAbi, + functionName: 'DAILY_LIMIT', + }, + { + chainId, + address: FAUCET.address, + abi: faucetAbi, + functionName: 'accountDailyRequests', + args: [address], + }, + { + chainId, + address: FAUCET.address, + abi: faucetAbi, + functionName: 'accountResetTime', + args: [address], + }, + ], + }) + + // Extract results with proper error handling + const [dailyLimitResult, requestsResult, resetTimeResult] = results + + if (dailyLimitResult.status === 'failure') { + throw new Error('Failed to fetch daily limit') + } + if (requestsResult.status === 'failure') { + throw new Error('Failed to fetch account requests') + } + if (resetTimeResult.status === 'failure') { + throw new Error('Failed to fetch reset time') + } + + // Type-safe extraction of results + const dailyLimit = dailyLimitResult.result as bigint + const accountDailyRequests = requestsResult.result as bigint + const accountResetTime = resetTimeResult.result as bigint + + // Calculate derived state + const remainingRequests = + dailyLimit > accountDailyRequests + ? dailyLimit - accountDailyRequests + : 0n + const canRequest = remainingRequests > 0n + + const now = Math.floor(Date.now() / 1000) + const resetTimestamp = Number(accountResetTime) + const timeUntilReset = Math.max(0, resetTimestamp - now) + + return { + dailyLimit, + accountDailyRequests, + accountResetTime, + canRequest, + remainingRequests, + timeUntilReset, + } + }, + enabled: options?.enabled ?? !!address, + refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL, + }) +} diff --git a/apps/hub/src/app/_hooks/use-siwe.ts b/apps/hub/src/app/_hooks/useSIWE.ts similarity index 100% rename from apps/hub/src/app/_hooks/use-siwe.ts rename to apps/hub/src/app/_hooks/useSIWE.ts diff --git a/apps/hub/src/app/_hooks/use-vault-state-machine.ts b/apps/hub/src/app/_hooks/useVaultStateMachine.ts similarity index 97% rename from apps/hub/src/app/_hooks/use-vault-state-machine.ts rename to apps/hub/src/app/_hooks/useVaultStateMachine.ts index aca4eeb2a..a0f0da09b 100644 --- a/apps/hub/src/app/_hooks/use-vault-state-machine.ts +++ b/apps/hub/src/app/_hooks/useVaultStateMachine.ts @@ -87,10 +87,6 @@ function transition(state: VaultState, event: VaultEvent): VaultState { step: 'processing', }) ) - .with( - [{ type: 'createVault', step: 'processing' }, { type: 'COMPLETE' }], - () => ({ type: 'success' }) - ) .with( [{ type: 'createVault', step: 'processing' }, { type: 'REJECT' }], () => ({ @@ -98,6 +94,10 @@ function transition(state: VaultState, event: VaultEvent): VaultState { step: 'rejected', }) ) + .with( + [{ type: 'createVault', step: 'processing' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) .with( [{ type: 'createVault', step: 'rejected' }, { type: 'RESET' }], () => ({ type: 'idle' }) diff --git a/apps/hub/src/app/_hooks/useWeightedBoost.ts b/apps/hub/src/app/_hooks/useWeightedBoost.ts new file mode 100644 index 000000000..7468a77e1 --- /dev/null +++ b/apps/hub/src/app/_hooks/useWeightedBoost.ts @@ -0,0 +1,173 @@ +import { useMemo } from 'react' + +import { formatUnits } from 'viem' + +import { SNT_TOKEN } from '../../config' + +import type { VaultWithAddress } from './useAccountVaults' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Weighted boost calculation result + */ +export interface WeightedBoost { + /** Weighted aggregate boost multiplier as a number */ + value: number + /** Formatted boost multiplier (e.g., "1.25") */ + formatted: string + /** Total staked amount across all vaults */ + totalStaked: number + /** Whether any vaults have stake */ + hasStake: boolean +} + +// ============================================================================ +// Constants +// ============================================================================ + +const BASE_BOOST = 1.0 +const DEFAULT_DECIMALS = 2 + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Converts bigint token amount to number using formatUnits + * + * @param value - The bigint value to convert + * @param decimals - Number of decimals for the token + * @returns Numeric representation + */ +function toTokenAmount(value: bigint, decimals: number): number { + return Number(formatUnits(value, decimals)) +} + +/** + * Calculates the boost multiplier for a single vault + * + * Boost formula: (MP / Staked) + 1 + * + * @param stakedBalance - Amount of tokens staked in the vault + * @param mpAccrued - Multiplier points accrued + * @param decimals - Token decimals + * @returns Boost multiplier for the vault + */ +function calculateVaultBoost( + stakedBalance: bigint, + mpAccrued: bigint, + decimals: number +): number { + const staked = toTokenAmount(stakedBalance, decimals) + const mp = toTokenAmount(mpAccrued, decimals) + + if (staked === 0) return BASE_BOOST + + return mp / staked + BASE_BOOST +} + +/** + * Calculates weighted aggregate boost across all vaults + * + * The weighted boost is calculated as: + * Sum(Boost_i * Staked_i) / Sum(Staked_i) + * + * Where: + * - Boost_i = (MP_i / Staked_i) + 1 + * - Staked_i = staked balance in vault i + * + * @param vaults - Array of vaults with their data + * @param decimals - Token decimals + * @returns Weighted boost calculation result + */ +function calculateWeightedBoost( + vaults: VaultWithAddress[], + decimals: number +): WeightedBoost { + let totalWeightedBoost = 0 + let totalStaked = 0 + + // Calculate weighted sum across all vaults + for (const vault of vaults) { + // Skip vaults with no data or no stake + if (!vault.data || vault.data.stakedBalance === 0n) { + continue + } + + const vaultStaked = toTokenAmount(vault.data.stakedBalance, decimals) + const vaultBoost = calculateVaultBoost( + vault.data.stakedBalance, + vault.data.mpAccrued, + decimals + ) + + totalWeightedBoost += vaultBoost * vaultStaked + totalStaked += vaultStaked + } + + // Handle no stake case + if (totalStaked === 0) { + return { + value: BASE_BOOST, + formatted: BASE_BOOST.toFixed(DEFAULT_DECIMALS), + totalStaked: 0, + hasStake: false, + } + } + + // Calculate weighted average + const weightedAverage = totalWeightedBoost / totalStaked + + return { + value: weightedAverage, + formatted: weightedAverage.toFixed(DEFAULT_DECIMALS), + totalStaked, + hasStake: true, + } +} + +// ============================================================================ +// Hook +// ============================================================================ + +/** + * Hook to calculate weighted aggregate boost across all user vaults + * + * The weighted boost rewards long-term staking by calculating a weighted + * average of individual vault boosts based on their staked amounts. + * + * @param vaults - Array of user vaults with their data + * @returns Weighted boost calculation result + * + * @example + * ```tsx + * function BoostDisplay() { + * const { data: vaults } = useAccountVaults() + * const boost = useWeightedBoost(vaults) + * + * return ( + *
    + *

    Weighted Boost: x{boost.formatted}

    + *

    Total Staked: {boost.totalStaked} SNT

    + *
    + * ) + * } + * ``` + */ +export function useWeightedBoost(vaults?: VaultWithAddress[]): WeightedBoost { + return useMemo(() => { + if (!vaults || vaults.length === 0) { + return { + value: BASE_BOOST, + formatted: BASE_BOOST.toFixed(DEFAULT_DECIMALS), + totalStaked: 0, + hasStake: false, + } + } + + return calculateWeightedBoost(vaults, SNT_TOKEN.decimals) + }, [vaults]) +} diff --git a/apps/hub/src/app/_hooks/vault-state-context.tsx b/apps/hub/src/app/_hooks/vault-state-context.tsx index c3ae4d8e0..5f5182ff6 100644 --- a/apps/hub/src/app/_hooks/vault-state-context.tsx +++ b/apps/hub/src/app/_hooks/vault-state-context.tsx @@ -2,9 +2,9 @@ import { createContext, useContext } from 'react' -import { useVaultStateMachine } from './use-vault-state-machine' +import { useVaultStateMachine } from './useVaultStateMachine' -import type { VaultEvent, VaultState } from './use-vault-state-machine' +import type { VaultEvent, VaultState } from './useVaultStateMachine' type VaultStateContextType = { state: VaultState diff --git a/apps/hub/src/app/contracts/faucet.ts b/apps/hub/src/app/contracts/faucet.ts new file mode 100644 index 000000000..43a45328e --- /dev/null +++ b/apps/hub/src/app/contracts/faucet.ts @@ -0,0 +1,50 @@ +import type { Abi } from 'viem' + +export const faucetAbi: Abi = [ + { + inputs: [{ internalType: 'address', name: '_token', type: 'address' }], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { inputs: [], name: 'DailyLimitExceeded', type: 'error' }, + { inputs: [], name: 'InvalidAddress', type: 'error' }, + { inputs: [], name: 'InvalidAmount', type: 'error' }, + { + inputs: [], + name: 'DAILY_LIMIT', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'accountDailyRequests', + outputs: [{ internalType: 'uint256', name: 'amount', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'accountResetTime', + outputs: [{ internalType: 'uint256', name: 'time', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + ], + name: 'requestTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'token', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] diff --git a/apps/hub/src/app/contracts/index.ts b/apps/hub/src/app/contracts/index.ts new file mode 100644 index 000000000..4af6b2a90 --- /dev/null +++ b/apps/hub/src/app/contracts/index.ts @@ -0,0 +1,5 @@ +export { faucetAbi } from './faucet' +export { stakingManagerAbi } from './stakingManagerAbi' +export { tokenAbi } from './tokenAbi' +export { vaultAbi } from './vaultAbi' +export { vaultFactoryAbi } from './vaultFactoryAbi' diff --git a/apps/hub/src/app/contracts/stakingManagerAbi.ts b/apps/hub/src/app/contracts/stakingManagerAbi.ts new file mode 100644 index 000000000..6e6fa25f3 --- /dev/null +++ b/apps/hub/src/app/contracts/stakingManagerAbi.ts @@ -0,0 +1,771 @@ +export const stakingManagerAbi = [ + { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, + { inputs: [], name: 'StakeManager__AmountCannotBeZero', type: 'error' }, + { inputs: [], name: 'StakeManager__DurationCannotBeZero', type: 'error' }, + { inputs: [], name: 'StakeManager__EmergencyModeEnabled', type: 'error' }, + { inputs: [], name: 'StakeManager__InsufficientBalance', type: 'error' }, + { inputs: [], name: 'StakeManager__InvalidVault', type: 'error' }, + { inputs: [], name: 'StakeManager__MigrationTargetHasFunds', type: 'error' }, + { inputs: [], name: 'StakeManager__RewardPeriodNotEnded', type: 'error' }, + { inputs: [], name: 'StakeManager__Unauthorized', type: 'error' }, + { inputs: [], name: 'StakeManager__VaultAlreadyRegistered', type: 'error' }, + { inputs: [], name: 'StakeManager__VaultNotRegistered', type: 'error' }, + { inputs: [], name: 'StakeMath__AbsoluteMaxMPOverflow', type: 'error' }, + { inputs: [], name: 'StakeMath__FundsLocked', type: 'error' }, + { inputs: [], name: 'StakeMath__InsufficientBalance', type: 'error' }, + { inputs: [], name: 'StakeMath__InvalidAmount', type: 'error' }, + { inputs: [], name: 'StakeMath__InvalidLockingPeriod', type: 'error' }, + { + inputs: [], + name: 'TrustedCodehashAccess__UnauthorizedCodehash', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { anonymous: false, inputs: [], name: 'EmergencyModeEnabled', type: 'event' }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: 'uint8', name: 'version', type: 'uint8' }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'lockPeriod', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'lockUntil', + type: 'uint256', + }, + ], + name: 'Locked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'lockTime', + type: 'uint256', + }, + ], + name: 'StakeMathTest', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'lockPeriod', + type: 'uint256', + }, + ], + name: 'Staked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'codehash', + type: 'bytes32', + }, + { indexed: false, internalType: 'bool', name: 'trusted', type: 'bool' }, + ], + name: 'TrustedCodehashUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'Unstaked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + ], + name: 'VaultLeft', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + ], + name: 'VaultMigrated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'VaultRegistered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'rewardsAccrued', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'mpAccrued', + type: 'uint256', + }, + ], + name: 'VaultUpdated', + type: 'event', + }, + { + inputs: [], + name: 'ACCRUE_RATE', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_BALANCE', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_LOCKUP_PERIOD', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_MULTIPLIER', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MIN_BALANCE', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MIN_LOCKUP_PERIOD', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MP_APY', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MP_MPY', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MP_MPY_ABSOLUTE', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'SCALE_FACTOR', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'STAKING_TOKEN', + outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'YEAR', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_initialOwner', type: 'address' }, + ], + name: '__TrustedCodehashAccess_init', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'emergencyModeEnabled', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'enableEmergencyMode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'getAccountTotalMaxMP', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'getAccountTotalStakedBalance', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'getAccountVaults', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'getVault', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'stakedBalance', type: 'uint256' }, + { internalType: 'uint256', name: 'rewardIndex', type: 'uint256' }, + { internalType: 'uint256', name: 'mpAccrued', type: 'uint256' }, + { internalType: 'uint256', name: 'maxMP', type: 'uint256' }, + { + internalType: 'uint256', + name: 'lastMPUpdateTime', + type: 'uint256', + }, + { internalType: 'uint256', name: 'lockUntil', type: 'uint256' }, + { internalType: 'uint256', name: 'rewardsAccrued', type: 'uint256' }, + ], + internalType: 'struct StakeManager.VaultData', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_owner', type: 'address' }, + { internalType: 'address', name: '_stakingToken', type: 'address' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: '_codehash', type: 'bytes32' }], + name: 'isTrustedCodehash', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'lastMPUpdatedTime', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'lastRewardIndex', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'lastRewardTime', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'leave', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'lockPeriod', type: 'uint256' }], + name: 'lock', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'migrateTo', type: 'address' }], + name: 'migrateToVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'mpAccruedOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'mpBalanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'mpBalanceOfAccount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'registerVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'rewardAmount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rewardEndTime', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rewardStartTime', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'rewardsBalanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'rewardsBalanceOfAccount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rewardsSupplier', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'duration', type: 'uint256' }, + ], + name: 'setReward', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_rewardsSupplier', type: 'address' }, + ], + name: 'setRewardsSupplier', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: '_codehash', type: 'bytes32' }, + { internalType: 'bool', name: '_trusted', type: 'bool' }, + ], + name: 'setTrustedCodehash', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'lockPeriod', type: 'uint256' }, + ], + name: 'stake', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'stakedBalanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalMP', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalMPAccrued', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalMPStaked', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalMaxMP', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalRewardsAccrued', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalRewardsSupply', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalStaked', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'amount', type: 'uint256' }], + name: 'unstake', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'updateAccount', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'updateGlobalState', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'updateVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newImplementation', type: 'address' }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newImplementation', type: 'address' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'vault', type: 'address' }], + name: 'vaultData', + outputs: [ + { internalType: 'uint256', name: 'stakedBalance', type: 'uint256' }, + { internalType: 'uint256', name: 'rewardIndex', type: 'uint256' }, + { internalType: 'uint256', name: 'mpAccrued', type: 'uint256' }, + { internalType: 'uint256', name: 'maxMP', type: 'uint256' }, + { internalType: 'uint256', name: 'lastMPUpdateTime', type: 'uint256' }, + { internalType: 'uint256', name: 'lockUntil', type: 'uint256' }, + { internalType: 'uint256', name: 'rewardsAccrued', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'vault', type: 'address' }], + name: 'vaultOwners', + outputs: [{ internalType: 'address', name: 'owner', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'vaultAddress', type: 'address' }, + ], + name: 'vaultShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'uint256', name: '', type: 'uint256' }, + ], + name: 'vaults', + outputs: [{ internalType: 'address', name: 'vault', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] diff --git a/apps/hub/src/app/contracts/tokenAbi.ts b/apps/hub/src/app/contracts/tokenAbi.ts new file mode 100644 index 000000000..fa5ea247d --- /dev/null +++ b/apps/hub/src/app/contracts/tokenAbi.ts @@ -0,0 +1,60 @@ +export const tokenAbi = [ + { + inputs: [{ internalType: 'address', name: '_owner', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: 'balance', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_spender', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_owner', type: 'address' }, + { internalType: 'address', name: '_spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ internalType: 'uint256', name: 'remaining', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/apps/hub/src/app/contracts/vaultAbi.ts b/apps/hub/src/app/contracts/vaultAbi.ts new file mode 100644 index 000000000..77eced031 --- /dev/null +++ b/apps/hub/src/app/contracts/vaultAbi.ts @@ -0,0 +1,229 @@ +export const vaultAbi = [ + { + type: 'constructor', + inputs: [ + { name: 'token', type: 'address', internalType: 'contract IERC20' }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'STAKING_TOKEN', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'contract IERC20' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'amountStaked', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'availableWithdraw', + inputs: [ + { name: '_token', type: 'address', internalType: 'contract IERC20' }, + ], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'emergencyExit', + inputs: [ + { name: '_destination', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'initialize', + inputs: [ + { name: '_owner', type: 'address', internalType: 'address' }, + { name: '_stakeManager', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'leave', + inputs: [ + { name: '_destination', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'lock', + inputs: [{ name: '_seconds', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'migrateToVault', + inputs: [{ name: 'migrateTo', type: 'address', internalType: 'address' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'owner', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'register', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'renounceOwnership', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'stake', + inputs: [ + { name: '_amount', type: 'uint256', internalType: 'uint256' }, + { name: '_seconds', type: 'uint256', internalType: 'uint256' }, + { name: '_from', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'stake', + inputs: [ + { name: '_amount', type: 'uint256', internalType: 'uint256' }, + { name: '_seconds', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'stakeManager', + inputs: [], + outputs: [ + { + name: '', + type: 'address', + internalType: 'contract IStakeManagerProxy', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'stakeManagerImplementationAddress', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'transferOwnership', + inputs: [{ name: 'newOwner', type: 'address', internalType: 'address' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'trustStakeManager', + inputs: [ + { name: 'stakeManagerAddress', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'unstake', + inputs: [{ name: '_amount', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'unstake', + inputs: [ + { name: '_amount', type: 'uint256', internalType: 'uint256' }, + { name: '_destination', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'withdraw', + inputs: [ + { name: '_token', type: 'address', internalType: 'contract IERC20' }, + { name: '_amount', type: 'uint256', internalType: 'uint256' }, + { name: '_destination', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'withdraw', + inputs: [ + { name: '_token', type: 'address', internalType: 'contract IERC20' }, + { name: '_amount', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'event', + name: 'Initialized', + inputs: [ + { name: 'version', type: 'uint8', indexed: false, internalType: 'uint8' }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'OwnershipTransferred', + inputs: [ + { + name: 'previousOwner', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'newOwner', + type: 'address', + indexed: true, + internalType: 'address', + }, + ], + anonymous: false, + }, + { type: 'error', name: 'StakeVault__InvalidDestinationAddress', inputs: [] }, + { type: 'error', name: 'StakeVault__MigrationFailed', inputs: [] }, + { type: 'error', name: 'StakeVault__NotAllowedToExit', inputs: [] }, + { type: 'error', name: 'StakeVault__NotAllowedToLeave', inputs: [] }, + { type: 'error', name: 'StakeVault__NotEnoughAvailableBalance', inputs: [] }, + { + type: 'error', + name: 'StakeVault__StakeManagerImplementationNotTrusted', + inputs: [], + }, + { type: 'error', name: 'StakeVault__StakingFailed', inputs: [] }, + { type: 'error', name: 'StakeVault__UnstakingFailed', inputs: [] }, +] as const diff --git a/apps/hub/src/app/contracts/vaultFactoryAbi.ts b/apps/hub/src/app/contracts/vaultFactoryAbi.ts new file mode 100644 index 000000000..2e1dd3b79 --- /dev/null +++ b/apps/hub/src/app/contracts/vaultFactoryAbi.ts @@ -0,0 +1,11 @@ +export const vaultFactoryAbi = [ + { + inputs: [], + name: 'createVault', + outputs: [ + { internalType: 'contract StakeVault', name: '', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index 9a3a84ae9..744dadc7d 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -3,8 +3,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ConnectKitProvider } from 'connectkit' import { WagmiProvider } from 'wagmi' -import { mainnet } from 'wagmi/chains' +import { statusNetworkTestnet } from '../config/chain' import { defineWagmiConfig } from '../wagmi' import { ActionStatusDialog } from './_components/stake/action-status-dialog' import { useActionStatusContent } from './_components/stake/use-action-status-content' @@ -14,7 +14,7 @@ import { } from './_hooks/vault-state-context' const config = defineWagmiConfig({ - chains: [mainnet], + chains: [statusNetworkTestnet], ssr: false, }) diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index c20d162e7..cb8f793bc 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -14,25 +14,78 @@ import { Button, ButtonLink } from '@status-im/status-network/components' import { ConnectKitButton } from 'connectkit' import Image from 'next/image' import { match } from 'ts-pattern' -import { useAccount } from 'wagmi' +import { + useAccount, + useBalance, + useReadContract, + useWaitForTransactionReceipt, + useWriteContract, +} from 'wagmi' import { HubLayout } from '~components/hub-layout' +import { SNT_TOKEN, STAKING_MANAGER, VAULT_FACTORY } from '../../config' +import { statusNetworkTestnet } from '../../config/chain' +import { formatSNT } from '../../utils/currency' import { LaunchIcon, SNTIcon } from '../_components/icons' import { PromoModal } from '../_components/stake/promo-modal' import { VaultsTable } from '../_components/vaults/table' -import { useSiwe } from '../_hooks/use-siwe' +import { useAccountVaults } from '../_hooks/useAccountVaults' +import { useFaucetMutation, useFaucetQuery } from '../_hooks/useFaucet' +import { useSiwe } from '../_hooks/useSIWE' +import { useWeightedBoost } from '../_hooks/useWeightedBoost' import { useVaultStateContext } from '../_hooks/vault-state-context' +import { stakingManagerAbi, vaultFactoryAbi } from '../contracts' type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' +const TOKEN_TICKER = 'SNT' + export default function StakePage() { - const { isConnected } = useAccount() - const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) const siweHook = useSiwe() const siweRef = useRef(siweHook) siweRef.current = siweHook + const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) + + const { isConnected, address } = useAccount() + + const { mutate: claimTokens } = useFaucetMutation() + const { data: faucetData } = useFaucetQuery() + const { data: vaults } = useAccountVaults() + const weightedBoost = useWeightedBoost(vaults) + + const { data: balance } = useBalance({ + chainId: statusNetworkTestnet.id, + scopeKey: 'balance', + address, + token: SNT_TOKEN.address, + query: { + enabled: isConnected, + }, + }) + + const { data: totalStaked } = useReadContract({ + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'totalStaked', + }) as { data: bigint } + + const { + writeContract, + data: createVaultTxHash, + isError: isTxError, + isSuccess: isTxSuccess, + } = useWriteContract() + + const { + isSuccess: isTxConfirmed, + data: txReceipt, + error: txError, + } = useWaitForTransactionReceipt({ + hash: createVaultTxHash, + }) + // State machine for vault operations const { state: vaultState, @@ -49,67 +102,111 @@ export default function StakePage() { const prevConnectedRef = useRef(isConnected) useEffect(() => { - // Only trigger SIWE when transitioning from disconnected to connected + // Check if user has already completed SIWE for this address + const siweCompleted = address + ? sessionStorage.getItem(`siwe_${address}`) + : null + + // Only trigger SIWE when transitioning from disconnected to connected AND not already signed if ( isConnected && !prevConnectedRef.current && - vaultState.type === 'idle' + vaultState.type === 'idle' && + !siweCompleted ) { sendVaultEvent({ type: 'START_SIWE' }) } prevConnectedRef.current = isConnected - }, [isConnected, vaultState.type, sendVaultEvent]) + }, [isConnected, vaultState.type, sendVaultEvent, address]) - const handleCreateVault = async (e: React.MouseEvent) => { + const handleCreateVault = (e: React.MouseEvent) => { e.preventDefault() if (isConnected) { sendVaultEvent({ type: 'START_CREATE_VAULT' }) } } + // Handle SIWE flow useEffect(() => { - const handleWalletInteraction = async () => { - // Handle SIWE flow - initialize - if (vaultState.type === 'siwe' && vaultState.step === 'initialize') { + if (vaultState.type === 'siwe' && vaultState.step === 'initialize') { + const handleSiwe = async () => { try { - // Sign in with wallet (this shows wallet UI for signing) await siweRef.current.signIn() - - // Move to processing state after user signs sendVaultEvent({ type: 'SIGN' }) - - // Give user visual feedback that it's processing await new Promise(resolve => setTimeout(resolve, 500)) - // Close dialog on success + // Mark SIWE as completed for this address + if (address) { + sessionStorage.setItem(`siwe_${address}`, 'true') + } + resetVault() } catch { sendVaultEvent({ type: 'REJECT' }) } } + handleSiwe() + } + }, [vaultState, sendVaultEvent, resetVault, address]) - // Handle Create Vault flow - if ( - vaultState.type === 'createVault' && - vaultState.step === 'initialize' - ) { - try { - // TODO: Replace with actual wallet signing logic - await new Promise(resolve => setTimeout(resolve, 1000)) - sendVaultEvent({ type: 'SIGN' }) + // Handle vault creation - trigger contract write on initialize + useEffect(() => { + if (vaultState.type === 'createVault' && vaultState.step === 'initialize') { + writeContract({ + chain: statusNetworkTestnet, + account: address, + address: VAULT_FACTORY.address, + abi: vaultFactoryAbi, + functionName: 'createVault', + }) + } + }, [address, vaultState, writeContract]) - // Simulate processing - await new Promise(resolve => setTimeout(resolve, 2000)) - sendVaultEvent({ type: 'COMPLETE' }) - } catch (error) { - console.error(error) - sendVaultEvent({ type: 'REJECT' }) - } - } + // Transition to processing when user signs in wallet + useEffect(() => { + if ( + isTxSuccess && + vaultState.type === 'createVault' && + vaultState.step === 'initialize' + ) { + sendVaultEvent({ type: 'SIGN' }) + } + }, [isTxSuccess, vaultState, sendVaultEvent]) + + // Handle write errors (user rejection, insufficient gas, etc.) + useEffect(() => { + if (isTxError && vaultState.type === 'createVault') { + // TODO: pass error to state machine + sendVaultEvent({ type: 'REJECT' }) } + }, [isTxError, vaultState.type, sendVaultEvent]) - handleWalletInteraction() - }, [vaultState, sendVaultEvent, resetVault]) + // Handle transaction errors (reverted, failed on-chain) + useEffect(() => { + if (txError && vaultState.type === 'createVault') { + sendVaultEvent({ type: 'REJECT' }) + } + }, [txError, vaultState, sendVaultEvent]) + + // Handle transaction confirmation - close dialog immediately + useEffect(() => { + if ( + isTxConfirmed && + vaultState.type === 'createVault' && + vaultState.step === 'processing' + ) { + resetVault() + // TODO: show toast message + } + }, [isTxConfirmed, vaultState, resetVault, txReceipt]) + + // Auto-close success dialog after showing success state (for other flows) + useEffect(() => { + if (vaultState.type === 'success') { + const timer = setTimeout(() => resetVault(), 1500) + return () => clearTimeout(timer) + } + }, [vaultState.type, resetVault]) const handleClosePromoModal = () => { setIsPromoModalOpen(false) @@ -148,24 +245,32 @@ export default function StakePage() {

    Daily limit

    -

    10,000 SNT

    + {formatSNT(faucetData?.dailyLimit ?? 0)} SNT

    Used today

    -

    0 SNT

    + + {formatSNT(faucetData?.accountDailyRequests ?? 0)} SNT +

    Available

    -

    10,000 SNT

    + + {formatSNT(faucetData?.remainingRequests ?? 0)} SNT +
    {/* @ts-expect-error - TODO: fix this */} -
    @@ -198,7 +303,7 @@ export default function StakePage() {
    - SNT + {TOKEN_TICKER}
    @@ -209,7 +314,7 @@ export default function StakePage() { type="button" className="uppercase text-neutral-100" > - MAX 0 SNT + {formatSNT(balance?.value ?? 0)}
    @@ -227,7 +332,7 @@ export default function StakePage() {
    - SNT + {TOKEN_TICKER}
    @@ -319,7 +424,9 @@ export default function StakePage() {
    - 0 SNT + + {formatSNT(totalStaked ?? 0)} {TOKEN_TICKER} +

    Next unlock in 356 days @@ -336,10 +443,14 @@ export default function StakePage() {

    - x0 + + x{weightedBoost.formatted} +

    - No points are ready to compound + {weightedBoost.hasStake + ? `${weightedBoost.totalStaked.toFixed(2)} ${TOKEN_TICKER} staked` + : 'No points are ready to compound'}

    diff --git a/apps/hub/src/config/address.ts b/apps/hub/src/config/address.ts new file mode 100644 index 000000000..716b1ca52 --- /dev/null +++ b/apps/hub/src/config/address.ts @@ -0,0 +1,20 @@ +import type { Address } from 'viem' + +export const STAKING_MANAGER = { + address: '0x785e6c5af58FB26F4a0E43e0cF254af10EaEe0f1' as Address, +} as const + +export const VAULT_FACTORY = { + address: '0xf7b6EC76aCa97b395dc48f7A2861aD810B34b52e' as Address, +} as const + +export const SNT_TOKEN = { + address: '0x1C3Ac2a186c6149Ae7Cb4D716eBbD0766E4f898a' as Address, + name: 'Status Test Token', + symbol: 'STT', + decimals: 18, +} as const + +export const FAUCET = { + address: '0x4Fb609F4a457f47B41D35Dd060447271F000120A' as Address, +} as const diff --git a/apps/hub/src/config/chain.ts b/apps/hub/src/config/chain.ts new file mode 100644 index 000000000..f97d3dd93 --- /dev/null +++ b/apps/hub/src/config/chain.ts @@ -0,0 +1,25 @@ +import type { Chain } from 'wagmi/chains' + +export const statusNetworkTestnet: Chain = { + id: 1660990954, + name: 'Status Network Testnet', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://public.sepolia.rpc.status.network'], + }, + public: { + http: ['https://public.sepolia.rpc.status.network'], + }, + }, + blockExplorers: { + default: { + name: 'Status Explorer', + url: 'https://sepoliascan.status.network', + }, + }, +} diff --git a/apps/hub/src/config/index.ts b/apps/hub/src/config/index.ts new file mode 100644 index 000000000..6f5a55683 --- /dev/null +++ b/apps/hub/src/config/index.ts @@ -0,0 +1 @@ +export * from './address' diff --git a/apps/hub/src/utils/currency.ts b/apps/hub/src/utils/currency.ts index 663c45abd..44ace298b 100644 --- a/apps/hub/src/utils/currency.ts +++ b/apps/hub/src/utils/currency.ts @@ -1,3 +1,9 @@ +import { formatUnits, parseUnits } from 'viem' + +// ============================================================================ +// Types +// ============================================================================ + export interface FormatTokenOptions { /** * Number of decimal places to display @@ -41,23 +47,89 @@ export interface FormatTokenOptions { tokenDecimals?: number } +// ============================================================================ +// Constants +// ============================================================================ + +const DEFAULT_LOCALE = 'en-US' +const DEFAULT_DISPLAY_DECIMALS = 2 +const DEFAULT_TOKEN_DECIMALS = 18 +const COMPACT_THRESHOLD = 1_000_000 + +// ============================================================================ +// Helper Functions +// ============================================================================ + /** - * Convert bigint to number with proper decimal handling + * Convert bigint to number using viem's formatUnits for precision + * + * @param value - The bigint value to convert + * @param decimals - Number of decimals for the token + * @returns Numeric representation of the token amount */ -function bigIntToNumber(value: bigint, decimals: number = 18): number { - const divisor = BigInt(10 ** decimals) - const whole = value / divisor - const remainder = value % divisor +function bigIntToNumber( + value: bigint, + decimals: number = DEFAULT_TOKEN_DECIMALS +): number { + // Use viem's formatUnits for proper decimal handling + const formatted = formatUnits(value, decimals) + return Number(formatted) +} - // Convert remainder to decimal - const decimalStr = remainder.toString().padStart(decimals, '0') - const decimal = Number(`0.${decimalStr}`) +/** + * Converts various input types to a numeric value + * + * @param amount - The amount to convert (bigint, number, or string) + * @param tokenDecimals - Number of decimals for bigint conversion + * @returns Numeric representation of the amount + */ +function toNumericAmount( + amount: number | bigint | string, + tokenDecimals: number +): number { + if (typeof amount === 'bigint') { + return bigIntToNumber(amount, tokenDecimals) + } + + if (typeof amount === 'string') { + return Number(amount) + } + + return amount +} - return Number(whole) + decimal +/** + * Applies rounding down to a numeric value + * + * @param value - The value to round + * @param decimals - Number of decimal places to preserve + * @returns Rounded down value + */ +function roundDownToDecimals(value: number, decimals: number): number { + const factor = 10 ** decimals + return Math.floor(value * factor) / factor } +// ============================================================================ +// Core Formatting Functions +// ============================================================================ + /** - * Format a token amount with proper decimal handling + * Format a token amount with proper decimal handling using viem utilities + * + * @param amount - Token amount (supports bigint, number, or string) + * @param token - Token symbol (e.g., 'SNT', 'ETH') + * @param options - Formatting options + * @returns Formatted token amount string + * + * @example + * ```ts + * formatTokenAmount(1234567890000000000n, 'SNT', { tokenDecimals: 18 }) + * // => "1.23" + * + * formatTokenAmount(1500, 'SNT', { includeSymbol: true, compact: true }) + * // => "1.5K SNT" + * ``` */ export function formatTokenAmount( amount: number | bigint | string, @@ -65,32 +137,25 @@ export function formatTokenAmount( options: FormatTokenOptions = {} ): string { const { - decimals = 2, + decimals = DEFAULT_DISPLAY_DECIMALS, minimumFractionDigits = decimals, maximumFractionDigits = decimals, includeSymbol = false, - locale = 'en-US', + locale = DEFAULT_LOCALE, compact = false, roundDown = false, - tokenDecimals = 18, + tokenDecimals = DEFAULT_TOKEN_DECIMALS, } = options - // Convert to number - let numericAmount: number - if (typeof amount === 'bigint') { - numericAmount = bigIntToNumber(amount, tokenDecimals) - } else if (typeof amount === 'string') { - numericAmount = Number(amount) - } else { - numericAmount = amount - } + // Convert to numeric value + let numericAmount = toNumericAmount(amount, tokenDecimals) - // Round down if requested + // Apply rounding down if requested if (roundDown) { - const factor = Math.pow(10, maximumFractionDigits) - numericAmount = Math.floor(numericAmount * factor) / factor + numericAmount = roundDownToDecimals(numericAmount, maximumFractionDigits) } + // Format using Intl.NumberFormat const formatter = new Intl.NumberFormat(locale, { minimumFractionDigits, maximumFractionDigits, @@ -103,92 +168,138 @@ export function formatTokenAmount( return includeSymbol ? `${formatted} ${token}` : formatted } +// ============================================================================ +// Token-Specific Formatters +// ============================================================================ + /** - * Format SNT token amount + * Format SNT (Status Network Token) amount + * + * @param amount - Token amount in wei (bigint) or display units (number/string) + * @param options - Formatting options + * @returns Formatted SNT amount + * + * @example + * ```ts + * formatSNT(1234567890000000000n) // => "1.23" + * formatSNT(100, { includeSymbol: true }) // => "100.00 SNT" + * ``` */ export function formatSNT( amount: number | bigint | string, - options: Omit & { - includeSymbol?: boolean - } = {} + options: Omit = {} ): string { return formatTokenAmount(amount, 'SNT', { ...options, - // Don't apply decimal conversion for SNT - values are already in display units - tokenDecimals: 0, + tokenDecimals: DEFAULT_TOKEN_DECIMALS, // SNT has 18 decimals }) } /** * Format KARMA token amount + * + * @param amount - Token amount + * @param options - Formatting options + * @returns Formatted KARMA amount */ export function formatKarma( amount: number | bigint | string, - options: Omit & { - includeSymbol?: boolean - } = {} + options: Omit = {} ): string { return formatTokenAmount(amount, 'KARMA', { ...options, - // Don't apply decimal conversion for KARMA - values are already in display units - tokenDecimals: 0, + tokenDecimals: 0, // KARMA values are already in display units }) } /** - * Format ETH amount + * Format ETH (Ether) amount + * + * @param amount - ETH amount in wei (bigint) or display units (number/string) + * @param options - Formatting options + * @returns Formatted ETH amount + * + * @example + * ```ts + * formatETH(1000000000000000000n) // => "1.0000" + * formatETH(0.123456789, { decimals: 6 }) // => "0.123457" + * ``` */ export function formatETH( amount: number | bigint | string, - options: Omit & { - includeSymbol?: boolean - } = {} + options: Omit = {} ): string { return formatTokenAmount(amount, 'ETH', { + decimals: 4, // Default to 4 decimals for ETH ...options, - decimals: options.decimals ?? 4, + tokenDecimals: DEFAULT_TOKEN_DECIMALS, // ETH has 18 decimals }) } /** * Format stablecoin amount (USDC, USDT, DAI) + * + * @param amount - Stablecoin amount in smallest unit (bigint) or display units (number/string) + * @param token - Stablecoin symbol + * @param options - Formatting options + * @returns Formatted stablecoin amount + * + * @example + * ```ts + * formatStablecoin(1000000n, 'USDC') // => "1.00" (USDC has 6 decimals) + * formatStablecoin(1000000000000000000n, 'DAI') // => "1.00" (DAI has 18 decimals) + * ``` */ export function formatStablecoin( amount: number | bigint | string, token: 'USDC' | 'USDT' | 'DAI', - options: Omit & { - includeSymbol?: boolean - } = {} + options: Omit = {} ): string { - return formatTokenAmount(amount, token, options) + // USDC and USDT typically have 6 decimals, DAI has 18 + const decimals = token === 'DAI' ? DEFAULT_TOKEN_DECIMALS : 6 + + return formatTokenAmount(amount, token, { + ...options, + tokenDecimals: decimals, + }) } /** * Format a currency value for display in UI * Automatically handles large numbers with compact notation + * + * @param amount - Currency amount + * @param options - Formatting options with optional currency symbol + * @returns Formatted currency string + * + * @example + * ```ts + * formatCurrency(1234567) // => "1.23M" + * formatCurrency(999, { symbol: '$' }) // => "$999.00" + * formatCurrency(1500000n, { symbol: '$', tokenDecimals: 18 }) // => "$0.00" + * ``` */ export function formatCurrency( amount: number | bigint | string, options: FormatTokenOptions & { symbol?: string } = {} ): string { - const { symbol = '', tokenDecimals = 18, ...rest } = options + const { + symbol = '', + tokenDecimals = DEFAULT_TOKEN_DECIMALS, + locale = DEFAULT_LOCALE, + decimals = DEFAULT_DISPLAY_DECIMALS, + ...rest + } = options - let numericAmount: number - if (typeof amount === 'bigint') { - numericAmount = bigIntToNumber(amount, tokenDecimals) - } else if (typeof amount === 'string') { - numericAmount = Number(amount) - } else { - numericAmount = amount - } + const numericAmount = toNumericAmount(amount, tokenDecimals) - // Auto-enable compact for large numbers + // Auto-enable compact notation for large numbers (>= 1M) const shouldCompact = - rest.compact !== false && Math.abs(numericAmount) >= 1000000 + rest.compact !== false && Math.abs(numericAmount) >= COMPACT_THRESHOLD - const formatter = new Intl.NumberFormat(rest.locale ?? 'en-US', { - minimumFractionDigits: rest.minimumFractionDigits ?? rest.decimals ?? 2, - maximumFractionDigits: rest.maximumFractionDigits ?? rest.decimals ?? 2, + const formatter = new Intl.NumberFormat(locale, { + minimumFractionDigits: rest.minimumFractionDigits ?? decimals, + maximumFractionDigits: rest.maximumFractionDigits ?? decimals, notation: shouldCompact ? 'compact' : 'standard', compactDisplay: 'short', }) @@ -197,8 +308,38 @@ export function formatCurrency( return symbol ? `${symbol}${formatted}` : formatted } +// ============================================================================ +// Parsing Functions +// ============================================================================ + +/** + * Compact notation multipliers + */ +const COMPACT_MULTIPLIERS: Record = { + K: 1_000, + M: 1_000_000, + B: 1_000_000_000, + T: 1_000_000_000_000, +} as const + /** * Parse a formatted token string back to a number + * + * Handles: + * - Comma-separated numbers (1,234.56) + * - Compact notation (1.5K, 2M, 3.5B) + * - Token symbols (100 SNT) + * + * @param formattedAmount - The formatted string to parse + * @param token - Optional token symbol to remove from the string + * @returns Numeric value + * + * @example + * ```ts + * parseTokenAmount('1,234.56') // => 1234.56 + * parseTokenAmount('1.5K') // => 1500 + * parseTokenAmount('100 SNT', 'SNT') // => 100 + * ``` */ export function parseTokenAmount( formattedAmount: string, @@ -210,23 +351,36 @@ export function parseTokenAmount( cleaned = cleaned.replace(token, '').trim() } - // Remove commas and other formatting + // Remove commas and spaces cleaned = cleaned.replace(/[,\s]/g, '') - // Handle compact notation (K, M, B) - const multipliers: Record = { - K: 1000, - M: 1000000, - B: 1000000000, - T: 1000000000000, - } - - const match = cleaned.match(/^([\d.]+)([KMBT])$/i) - if (match) { - const [, num, suffix] = match - const multiplier = multipliers[suffix.toUpperCase()] || 1 - return Number(num) * multiplier + // Handle compact notation (K, M, B, T) + const compactMatch = cleaned.match(/^([\d.]+)([KMBT])$/i) + if (compactMatch) { + const [, numStr, suffix] = compactMatch + const multiplier = COMPACT_MULTIPLIERS[suffix.toUpperCase()] ?? 1 + return Number(numStr) * multiplier } return Number(cleaned) } + +/** + * Parse a user input amount and convert to wei (bigint) using viem's parseUnits + * + * @param amount - User input amount (e.g., "1.5", "100") + * @param decimals - Number of decimals for the token + * @returns Amount in wei as bigint + * + * @example + * ```ts + * parseToWei('1.5', 18) // => 1500000000000000000n + * parseToWei('100', 6) // => 100000000n (for USDC) + * ``` + */ +export function parseToWei( + amount: string, + decimals: number = DEFAULT_TOKEN_DECIMALS +): bigint { + return parseUnits(amount, decimals) +} diff --git a/apps/hub/src/utils/vault.ts b/apps/hub/src/utils/vault.ts new file mode 100644 index 000000000..b4abc5832 --- /dev/null +++ b/apps/hub/src/utils/vault.ts @@ -0,0 +1,60 @@ +import type { Vault } from '../app/_components/vaults/types' +import type { + VaultData, + VaultWithAddress, +} from '../app/_hooks/useAccountVaults' + +/** + * Transforms raw vault data from the contract into the UI-friendly Vault type + */ +export const transformVaultData = ( + vaultData: VaultData, + vaultAddress: string, + index: number +): Vault => { + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(vaultData.lockUntil) + const isLocked = lockUntilTimestamp > now + const daysUntilUnlock = isLocked + ? Math.ceil((lockUntilTimestamp - now) / 86400) + : null + + // Calculate boost based on MP ratio + const boost = + vaultData.stakedBalance > 0n + ? Number(vaultData.mpAccrued) / Number(vaultData.stakedBalance) + : 0 + + const potentialBoost = + vaultData.stakedBalance > 0n && vaultData.maxMP > vaultData.mpAccrued + ? Number(vaultData.maxMP - vaultData.mpAccrued) / + Number(vaultData.stakedBalance) + : undefined + + // Calculate karma based on rewards + const karma = Number(vaultData.rewardsAccrued) / 1e18 + + return { + id: `${index}`, + address: vaultAddress as `0x${string}`, + staked: vaultData.stakedBalance, + unlocksIn: daysUntilUnlock, + boost, + potentialBoost, + karma, + locked: isLocked, + } +} + +/** + * Transforms vault data from contract format to UI format + * Filters out vaults with failed data fetches + */ +export const transformVaults = (vaultDataList: VaultWithAddress[]): Vault[] => { + return vaultDataList + .map((vault, index) => { + if (!vault.data) return null + return transformVaultData(vault.data, vault.address, index) + }) + .filter((vault): vault is Vault => vault !== null) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1320283a2..c3f0346af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -278,7 +278,7 @@ importers: version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) + version: 1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4) cva: specifier: 1.0.0-beta.1 version: 1.0.0-beta.1(typescript@5.8.3) @@ -287,7 +287,7 @@ importers: version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: specifier: 15.1.6 - version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + version: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-mdx-remote: specifier: ^5.0.0 version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) @@ -306,6 +306,9 @@ importers: ts-pattern: specifier: ^5.6.2 version: 5.7.1 + viem: + specifier: ^2.21.1 + version: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) devDependencies: '@ianvs/prettier-plugin-sort-imports': specifier: ^4.3.1 @@ -20179,18 +20182,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/traverse@7.27.1': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/template': 7.27.2 - '@babel/types': 7.27.1 - debug: 4.3.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - '@babel/traverse@7.27.1(supports-color@5.5.0)': dependencies: '@babel/code-frame': 7.27.1 @@ -22349,7 +22340,7 @@ snapshots: dependencies: '@babel/generator': 7.27.1 '@babel/parser': 7.27.2 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) '@babel/types': 7.27.1 prettier: 3.5.3 semver: 7.7.1 @@ -29839,16 +29830,16 @@ snapshots: mdast-util-to-string: 3.2.0 unist-util-visit: 4.1.2 - '@vercel/analytics@1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': + '@vercel/analytics@1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': optionalDependencies: - next: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 svelte: 4.2.2 vue: 3.3.4 - '@vercel/analytics@1.5.0(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': + '@vercel/analytics@1.5.0(next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react@19.1.0)(svelte@4.2.2)(vue@3.3.4)': optionalDependencies: - next: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 svelte: 4.2.2 vue: 3.3.4 @@ -33494,10 +33485,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.7: - dependencies: - ms: 2.1.3 - debug@4.3.7(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -34393,7 +34380,7 @@ snapshots: eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.3.7 + debug: 4.3.7(supports-color@5.5.0) enhanced-resolve: 5.17.1 eslint: 9.14.0(jiti@2.4.2) eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.40.0(eslint@9.14.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.14.0(jiti@2.4.2)))(eslint@9.14.0(jiti@2.4.2)) @@ -36874,6 +36861,10 @@ snapshots: dependencies: ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): + dependencies: + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + it-all@3.0.4: {} it-batched-bytes@2.0.4: @@ -39068,7 +39059,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4): + next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): dependencies: '@next/env': 15.1.6 '@swc/counter': 0.1.3 @@ -39077,7 +39068,7 @@ snapshots: caniuse-lite: 1.0.30001717 postcss: 8.4.31 react: 19.1.0 - react-dom: 19.1.1(react@19.1.0) + react-dom: 19.1.0(react@19.1.0) styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.1.6 @@ -39095,7 +39086,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4): + next@15.1.6(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.0))(react@19.1.0)(sass@1.80.4): dependencies: '@next/env': 15.1.6 '@swc/counter': 0.1.3 @@ -39104,8 +39095,8 @@ snapshots: caniuse-lite: 1.0.30001717 postcss: 8.4.31 react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(react@19.1.0) + react-dom: 19.1.1(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.1.6 '@next/swc-darwin-x64': 15.1.6 @@ -43103,11 +43094,6 @@ snapshots: optionalDependencies: '@babel/core': 7.27.1 - styled-jsx@5.1.6(react@19.1.0): - dependencies: - client-only: 0.0.1 - react: 19.1.0 - stylis@4.2.0: {} sucrase@3.35.0: @@ -44387,6 +44373,23 @@ snapshots: - utf-8-validate - zod + viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): + dependencies: + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite-node@3.1.4(@types/node@22.7.5)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.27.0)(sass@1.80.4)(tsx@4.19.4)(yaml@2.8.1): dependencies: cac: 6.7.14 From 26016b34548cbac3426d0b8cc90b2684be78eb32 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Mon, 6 Oct 2025 09:50:55 +0100 Subject: [PATCH 11/17] feat: enhance vault management with new hooks for token approval, withdrawal, and staking; update UI components and integrate new icons for improved user experience --- apps/hub/package.json | 6 +- apps/hub/src/app/_components/icons/index.ts | 3 +- .../src/app/_components/icons/launch-icon.tsx | 79 +-- .../app/_components/icons/processing-icon.tsx | 2 +- .../src/app/_components/icons/reject-icon.tsx | 27 + .../{new-action-icon.tsx => vault-icon.tsx} | 2 +- .../stake/action-status-dialog.tsx | 10 +- .../stake/use-action-status-content.tsx | 55 +- apps/hub/src/app/_components/vault-select.tsx | 165 +++++ .../app/_components/vaults/table-columns.tsx | 213 ++++--- apps/hub/src/app/_components/vaults/table.tsx | 56 +- .../_components/vaults/vault-lock-modal.tsx | 237 ++++++-- .../app/_components/vaults/withdraw-modal.tsx | 34 +- apps/hub/src/app/_hooks/useCompounder.ts | 188 ++++++ apps/hub/src/app/_hooks/useExchangeRate.ts | 143 +++++ apps/hub/src/app/_hooks/useTokenApproval.ts | 133 +++++ apps/hub/src/app/_hooks/useVault.ts | 174 ++++++ apps/hub/src/app/_hooks/useVaultLock.ts | 166 ++++++ .../src/app/_hooks/useVaultStateMachine.ts | 120 ++++ apps/hub/src/app/_hooks/useVaultTokenStake.ts | 170 ++++++ apps/hub/src/app/_hooks/useVaultWithdraw.ts | 168 ++++++ apps/hub/src/app/providers.tsx | 5 + apps/hub/src/app/stake/page.tsx | 320 +++++----- apps/hub/src/utils/address.ts | 3 + apps/hub/src/utils/currency.ts | 21 +- apps/hub/src/utils/vault.ts | 21 + pnpm-lock.yaml | 562 +++++++++++------- 27 files changed, 2430 insertions(+), 653 deletions(-) create mode 100644 apps/hub/src/app/_components/icons/reject-icon.tsx rename apps/hub/src/app/_components/icons/{new-action-icon.tsx => vault-icon.tsx} (96%) create mode 100644 apps/hub/src/app/_components/vault-select.tsx create mode 100644 apps/hub/src/app/_hooks/useCompounder.ts create mode 100644 apps/hub/src/app/_hooks/useExchangeRate.ts create mode 100644 apps/hub/src/app/_hooks/useTokenApproval.ts create mode 100644 apps/hub/src/app/_hooks/useVault.ts create mode 100644 apps/hub/src/app/_hooks/useVaultLock.ts create mode 100644 apps/hub/src/app/_hooks/useVaultTokenStake.ts create mode 100644 apps/hub/src/app/_hooks/useVaultWithdraw.ts create mode 100644 apps/hub/src/utils/address.ts diff --git a/apps/hub/package.json b/apps/hub/package.json index 083f305e8..c7d6d6160 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -14,6 +14,8 @@ "clean": "rimraf .next .vercel/output node_modules" }, "dependencies": { + "@hookform/devtools": "^4.3.1", + "@hookform/resolvers": "^3.1.1", "@status-im/colors": "workspace:*", "@status-im/components": "workspace:*", "@status-im/icons": "workspace:*", @@ -26,10 +28,12 @@ "next-mdx-remote": "^5.0.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.45.1", "rehype-slug": "^6.0.0", "siwe": "^2.3.2", "ts-pattern": "^5.6.2", - "viem": "^2.21.1" + "viem": "^2.21.1", + "zod": "^3.21.4" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.3.1", diff --git a/apps/hub/src/app/_components/icons/index.ts b/apps/hub/src/app/_components/icons/index.ts index e721b54aa..fbde49ee3 100644 --- a/apps/hub/src/app/_components/icons/index.ts +++ b/apps/hub/src/app/_components/icons/index.ts @@ -11,13 +11,14 @@ export { default as HomeIcon } from './home-icon' export { default as KarmaIcon } from './karma-icon' export { default as LaunchIcon } from './launch-icon' export { default as MintIcon } from './mint-icon' -export { default as NewActionIcon } from './new-action-icon' export { default as PercentIcon } from './percent-icon' export { default as PlusIcon } from './plus-icon' export { default as ProcessingIcon } from './processing-icon' +export { default as RejectIcon } from './reject-icon' export { default as SettingsIcon } from './settings-icon' export { default as SNTIcon } from './snt-icon' export { default as StakeIcon } from './stake-icon' export { default as SubmitAppIcon } from './submit-app-icon' export { default as SwapIcon } from './swap-icon' export { default as TwitterIcon } from './twitter-icon' +export { default as VaultIcon } from './vault-icon' diff --git a/apps/hub/src/app/_components/icons/launch-icon.tsx b/apps/hub/src/app/_components/icons/launch-icon.tsx index 676cf2863..18e105759 100644 --- a/apps/hub/src/app/_components/icons/launch-icon.tsx +++ b/apps/hub/src/app/_components/icons/launch-icon.tsx @@ -1,52 +1,27 @@ -export default function LaunchIcon({ - className = '', - ...props -}: React.SVGProps) { - return ( - - - - - - - - - - - - - - ) -} +import type { SVGProps } from 'react' + +const LaunchIcon = (props: SVGProps) => ( + + + + +) +export default LaunchIcon diff --git a/apps/hub/src/app/_components/icons/processing-icon.tsx b/apps/hub/src/app/_components/icons/processing-icon.tsx index 660432a8b..279c229ff 100644 --- a/apps/hub/src/app/_components/icons/processing-icon.tsx +++ b/apps/hub/src/app/_components/icons/processing-icon.tsx @@ -1,6 +1,6 @@ import type { SVGProps } from 'react' -const ProcessingIcon = (props: SVGProps) => ( +const ProcessingIcon = ({ ...props }: SVGProps) => ( ) => ( + + + + + + + + + + +) +export default RejectIcon diff --git a/apps/hub/src/app/_components/icons/new-action-icon.tsx b/apps/hub/src/app/_components/icons/vault-icon.tsx similarity index 96% rename from apps/hub/src/app/_components/icons/new-action-icon.tsx rename to apps/hub/src/app/_components/icons/vault-icon.tsx index 7c6382e8c..8f3eebf8e 100644 --- a/apps/hub/src/app/_components/icons/new-action-icon.tsx +++ b/apps/hub/src/app/_components/icons/vault-icon.tsx @@ -1,6 +1,6 @@ import type { SVGProps } from 'react' -export default function NewActionIcon({ +export default function VaultIcon({ className, ...props }: SVGProps) { diff --git a/apps/hub/src/app/_components/stake/action-status-dialog.tsx b/apps/hub/src/app/_components/stake/action-status-dialog.tsx index f3fd877ac..767e7666f 100644 --- a/apps/hub/src/app/_components/stake/action-status-dialog.tsx +++ b/apps/hub/src/app/_components/stake/action-status-dialog.tsx @@ -3,7 +3,7 @@ import * as Dialog from '@radix-ui/react-dialog' import { CloseIcon } from '@status-im/icons/20' import { match } from 'ts-pattern' -import { NewActionIcon, ProcessingIcon } from '../icons' +import { ProcessingIcon, RejectIcon, VaultIcon } from '../icons' import type { ActionStatusState } from './use-action-status-content' @@ -36,10 +36,10 @@ const ActionStatusDialog = (props: Props) => { const mapIconToState = (state: ActionStatusState) => { return match(state) - .with('pending', () => ) + .with('pending', () => ) .with('processing', () => ) - .with('error', () => ) - .with('success', () => ) + .with('error', () => ) + .with('success', () => ) .otherwise(() => null) } @@ -65,7 +65,7 @@ const ActionStatusDialog = (props: Props) => {
    {mapIconToState(state)} -

    +

    {title}

    diff --git a/apps/hub/src/app/_components/stake/use-action-status-content.tsx b/apps/hub/src/app/_components/stake/use-action-status-content.tsx index 9f07ff22a..7dfda052e 100644 --- a/apps/hub/src/app/_components/stake/use-action-status-content.tsx +++ b/apps/hub/src/app/_components/stake/use-action-status-content.tsx @@ -1,5 +1,7 @@ import { match } from 'ts-pattern' +import { formatSNT } from '../../../utils/currency' + import type { VaultState } from '../../_hooks/useVaultStateMachine' export type ActionStatusState = @@ -18,6 +20,7 @@ export type ActionStatusContent = { export function useActionStatusContent( state: VaultState ): ActionStatusContent | null { + console.log(state) return ( match(state) // SIWE flow @@ -65,7 +68,7 @@ export function useActionStatusContent( title: 'Increase token allowance', description: 'Please sign the message in your wallet.', state: 'pending', - showCloseButton: true, + showCloseButton: false, })) .with({ type: 'increaseAllowance', step: 'processing' }, () => ({ title: 'Increasing token allowance', @@ -87,7 +90,7 @@ export function useActionStatusContent( step: 'initialize', }, state => ({ - title: `Ready to stake ${state.amount || '0'} SNT`, + title: `Ready to stake ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, description: 'Please sign the message in your wallet.', state: 'pending', showCloseButton: true, @@ -95,7 +98,7 @@ export function useActionStatusContent( ) .with({ type: 'staking', step: 'processing' }, state => ({ - title: `Staking ${state.amount || '0'} SNT`, + title: `Staking ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, description: 'Wait a moment...', state: 'processing', showCloseButton: false, @@ -107,6 +110,52 @@ export function useActionStatusContent( showCloseButton: true, })) + // Withdraw flow + .with( + { + type: 'withdraw', + step: 'initialize', + }, + state => ({ + title: `Ready to withdraw ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, + description: 'Please sign the message in your wallet.', + state: 'pending', + showCloseButton: true, + }) + ) + .with({ type: 'withdraw', step: 'processing' }, state => ({ + title: `Withdrawing ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, + description: 'Wait a moment...', + state: 'processing', + showCloseButton: false, + })) + .with({ type: 'withdraw', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'error', + showCloseButton: true, + })) + + // Lock flow + .with({ type: 'lock', step: 'initialize' }, () => ({ + title: 'Ready to lock vault', + description: 'Please sign the message in your wallet.', + state: 'pending', + showCloseButton: true, + })) + .with({ type: 'lock', step: 'processing' }, () => ({ + title: 'Locking vault', + description: 'Wait a moment...', + state: 'processing', + showCloseButton: false, + })) + .with({ type: 'lock', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'error', + showCloseButton: true, + })) + // Success .with({ type: 'success' }, () => ({ title: 'Success!', diff --git a/apps/hub/src/app/_components/vault-select.tsx b/apps/hub/src/app/_components/vault-select.tsx new file mode 100644 index 000000000..0a7ee93f5 --- /dev/null +++ b/apps/hub/src/app/_components/vault-select.tsx @@ -0,0 +1,165 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import { useState } from 'react' + +import { DropdownMenu } from '@status-im/components' +import { AddSmallIcon, DropdownIcon, UnlockedIcon } from '@status-im/icons/20' + +import { formatSNT } from '../../utils/currency' + +import type { VaultWithAddress } from '../_hooks/useAccountVaults' + +// ============================================================================ +// Types +// ============================================================================ + +interface VaultSelectProps { + /** + * List of available vaults to select from + */ + vaults: VaultWithAddress[] + /** + * Currently selected vault address + */ + value: string + /** + * Callback when vault selection changes + */ + onChange: (vaultAddress: string) => void + /** + * Whether the select is disabled + * @default false + */ + disabled?: boolean + /** + * Placeholder text when no vault is selected + * @default "Add new vault" + */ + placeholder?: string +} + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Gets display label for a vault option + */ +function getVaultLabel(vault: VaultWithAddress, index: number): string { + const stakedAmount = vault.data?.stakedBalance + ? formatSNT(vault.data.stakedBalance) + : '0' + + // Format: #1 - 0xd233...34c4, 100,000,000 SNT + const shortAddress = `${vault.address.slice(0, 6)}...${vault.address.slice(-4)}` + return `#${index + 1} - ${shortAddress}, ${stakedAmount} SNT` +} + +// ============================================================================ +// Component +// ============================================================================ + +/** + * Vault selection dropdown component + * + * Displays a list of user vaults with their staked balances. + * Allows selecting a vault or creating a new one. + * + * @example + * ```tsx + * function StakeForm() { + * const { data: vaults } = useAccountVaults() + * const [selectedVault, setSelectedVault] = useState('') + * + * return ( + * + * ) + * } + * ``` + */ +export function VaultSelect({ + vaults, + value, + onChange, + disabled = false, + placeholder = 'Add new vault', +}: VaultSelectProps) { + const [open, setOpen] = useState(false) + + // Find the selected vault to display its label + const selectedVault = vaults.find(vault => vault.address === value) + const selectedIndex = vaults.findIndex(vault => vault.address === value) + + const displayLabel = + selectedVault && selectedIndex !== -1 + ? getVaultLabel(selectedVault, selectedIndex) + : placeholder + + // If no vaults exist, show the "New vault" option (disabled) + const hasVaults = vaults.length > 0 + + const trigger = ( + + ) + + const content = ( + + } + label={placeholder} + selected={!value} + onSelect={() => { + onChange('') + setOpen(false) + }} + /> + {hasVaults ? ( + <> + {vaults.map((vault, index) => ( + } + label={getVaultLabel(vault, index)} + selected={vault.address === value} + onSelect={() => { + onChange(vault.address) + setOpen(false) + }} + /> + ))} + + ) : null} + + ) + + return ( + + {trigger} + {content} + + ) +} diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index 8c37abd6f..aee6a6115 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -6,34 +6,40 @@ import { Button } from '@status-im/status-network/components' import { createColumnHelper } from '@tanstack/react-table' import { formatKarma, formatSNT } from '../../../utils/currency' +import { calculateVaultBoost } from '../../../utils/vault' +import { type VaultWithAddress } from '../../_hooks/useAccountVaults' import { VaultLockConfigModal } from './vault-lock-modal' import { VaultWithdrawModal } from './withdraw-modal' -import type { Vault } from './types' - interface TableColumnsProps { - data: Vault[] + vaults: VaultWithAddress[] | undefined openModalVaultId: string | null setOpenModalVaultId: (vaultId: string | null) => void } export const createVaultTableColumns = ({ - data, + vaults = [], openModalVaultId, setOpenModalVaultId, }: TableColumnsProps) => { - const totalStaked = data.reduce((acc, vault) => acc + vault.staked, BigInt(0)) - const totalKarma = data.reduce((acc, vault) => acc + vault.karma, 0) - const columnHelper = createColumnHelper() + const totalStaked = vaults.reduce( + (acc, vault) => acc + (vault.data?.stakedBalance || 0n), + BigInt(0) + ) + const totalKarma = vaults.reduce( + (acc, vault) => acc + (vault.data?.rewardsAccrued || 0n), + BigInt(0) + ) + const columnHelper = createColumnHelper() return [ - columnHelper.accessor('id', { + columnHelper.accessor('address', { id: 'vault', header: 'Vault', cell: ({ row }) => { return ( - #{Number(row.original.id) + 1} + #{Number(row.index) + 1} ) }, @@ -62,13 +68,13 @@ export const createVaultTableColumns = ({ cellClassName: 'text-left', }, }), - columnHelper.accessor('staked', { + columnHelper.accessor('data.stakedBalance', { header: 'Staked', cell: ({ row }) => { return (
    - {formatSNT(row.original.staked)} + {formatSNT(row.original.data?.stakedBalance || 0n)} SNT
    @@ -87,16 +93,24 @@ export const createVaultTableColumns = ({ cellClassName: 'text-left', }, }), - columnHelper.accessor('unlocksIn', { + columnHelper.accessor('data.lockUntil', { header: 'Unlocks in', cell: ({ row }) => { - if (row.original.unlocksIn === null) { + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(row.original.data?.lockUntil) + const isLocked = lockUntilTimestamp > now + const daysUntilUnlock = isLocked + ? Math.ceil((lockUntilTimestamp - now) / 86400) + : null + + if (!daysUntilUnlock) { return null } + return (
    - {row.original.unlocksIn} + {daysUntilUnlock} d
    @@ -107,20 +121,26 @@ export const createVaultTableColumns = ({ cellClassName: 'text-left', }, }), - columnHelper.accessor('boost', { + columnHelper.accessor('data.maxMP', { header: 'Boost', cell: ({ row }) => { - if (!row.original.boost) { - return null - } + const stakedBalance = row.original.data?.stakedBalance || 0n + const maxMP = row.original.data?.maxMP || 0n + const mpAccrued = row.original.data?.mpAccrued || 0n + + const potentialBoost = + stakedBalance > 0n && maxMP > mpAccrued + ? Number(maxMP - mpAccrued) / Number(stakedBalance) + : undefined + return (
    - x{row.original.boost.toFixed(2)} + x{calculateVaultBoost(vaults, row.original.address)} - {row.original.potentialBoost && ( + {potentialBoost && ( - x{row.original.potentialBoost.toFixed(2)} if locked + x{potentialBoost.toFixed(2)} if locked )}
    @@ -131,13 +151,14 @@ export const createVaultTableColumns = ({ cellClassName: 'text-left', }, }), - columnHelper.accessor('karma', { + columnHelper.accessor('data.rewardsAccrued', { header: 'Karma', cell: ({ row }) => { + const karma = Number(row.original.data?.rewardsAccrued) / 1e18 return (
    - {formatKarma(row.original.karma)} + {formatKarma(karma)} KARMA
    @@ -156,19 +177,23 @@ export const createVaultTableColumns = ({ cellClassName: 'text-left', }, }), - columnHelper.accessor('locked', { + columnHelper.accessor('data.lockUntil', { id: 'state', header: 'State', cell: ({ row }) => { + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(row.original.data?.lockUntil) + const isLocked = lockUntilTimestamp > now + return (
    - {row.original.locked ? ( + {isLocked ? ( ) : ( - + )} - {row.original.locked ? 'Locked' : 'Open'} + {isLocked ? 'Locked' : 'Open'}
    ) @@ -182,79 +207,87 @@ export const createVaultTableColumns = ({ id: 'actions', header: 'Actions', cell: ({ row }) => { - const withdrawModalId = `withdraw-${row.original.id}` - const lockModalId = `lock-${row.original.id}` + const withdrawModalId = `withdraw-${row.original.address}` + const lockModalId = `lock-${row.original.address}` const isWithdrawModalOpen = openModalVaultId === withdrawModalId const isLockModalOpen = openModalVaultId === lockModalId + const isLocked = row.original?.data?.lockUntil + ? row.original.data.lockUntil > 0 + : false return (
    - - setOpenModalVaultId(open ? withdrawModalId : null) - } - onClose={() => setOpenModalVaultId(null)} - vaultAdress={row.original.address} - > - {/* @ts-expect-error - Button component is not typed */} - - - {row.original.locked ? ( - - setOpenModalVaultId(open ? lockModalId : null) - } - title="Extend lock time" - description="Extending lock time increasing Karma boost" - actions={[ - { - label: 'Cancel', - onClick: () => setOpenModalVaultId(null), - }, - { - label: 'Extend lock', - onClick: () => { - // Handle extend lock logic - setOpenModalVaultId(null) + {isLocked ? ( +
    + + setOpenModalVaultId(open ? withdrawModalId : null) + } + onClose={() => setOpenModalVaultId(null)} + vaultAdress={row.original.address} + > + {/* @ts-expect-error - Button component is not typed */} + + + + setOpenModalVaultId(open ? lockModalId : null) + } + vaultAddress={row.original.address} + title="Extend lock time" + description="Extending lock time increasing Karma boost" + actions={[ + { + label: 'Cancel', + onClick: () => setOpenModalVaultId(null), }, - }, - ]} - onClose={() => setOpenModalVaultId(null)} - boost="x2.5" - unlockDate="30/01/2026" - infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." - > - {/* @ts-expect-error - Button component is not typed */} - - + {/* @ts-expect-error - Button component is not typed */} + + +
    ) : ( setOpenModalVaultId(open ? lockModalId : null) } + vaultAddress={row.original.address} title="Do you want to lock the vault?" description="Lock this vault to receive more Karma" sliderConfig={{ @@ -281,11 +314,9 @@ export const createVaultTableColumns = ({ ]} onClose={() => setOpenModalVaultId(null)} boost="x2.5" - unlockDate="30/01/2025" infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." - onValidate={(years, days) => { - const totalDays = - parseInt(years || '0') * 365 + parseInt(days || '0') + onValidate={(_, days) => { + const totalDays = parseInt(days || '0') return totalDays > 1460 ? 'Maximum lock time is 4 years' : null diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/table.tsx index a66c57710..36e0cffa5 100644 --- a/apps/hub/src/app/_components/vaults/table.tsx +++ b/apps/hub/src/app/_components/vaults/table.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useCallback, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { AddCircleIcon } from '@status-im/icons/12' import { Button } from '@status-im/status-network/components' @@ -10,13 +10,14 @@ import { getCoreRowModel, useReactTable, } from '@tanstack/react-table' +import { useAccount } from 'wagmi' -import { transformVaults } from '../../../utils/vault' import { useAccountVaults } from '../../_hooks/useAccountVaults' -import { useVaultStateContext } from '../../_hooks/vault-state-context' +import { useVaultMutation } from '../../_hooks/useVault' import { createVaultTableColumns } from './table-columns' -import type { Vault, VaultColumnMeta } from './types' +import type { VaultWithAddress } from '../../_hooks/useAccountVaults' +import type { VaultColumnMeta } from './types' /** * Gets the appropriate CSS class for table cell/header based on meta @@ -40,7 +41,7 @@ function getHeaderClassName( // ============================================================================ interface TableHeaderProps { - table: ReturnType> + table: ReturnType> } function TableHeader({ table }: TableHeaderProps) { @@ -68,7 +69,7 @@ function TableHeader({ table }: TableHeaderProps) { } interface TableBodyProps { - table: ReturnType> + table: ReturnType> } function TableBody({ table }: TableBodyProps) { @@ -91,7 +92,7 @@ function TableBody({ table }: TableBodyProps) { } interface TableFooterProps { - table: ReturnType> + table: ReturnType> } function TableFooter({ table }: TableFooterProps) { @@ -124,51 +125,44 @@ function TableFooter({ table }: TableFooterProps) { export function VaultsTable() { const [openModalVaultId, setOpenModalVaultId] = useState(null) - const { send: sendVaultEvent } = useVaultStateContext() const { data: vaultDataList } = useAccountVaults() - - // Transform vault data from contract format to UI format - const vaults = useMemo( - () => (vaultDataList ? transformVaults(vaultDataList) : []), - [vaultDataList] - ) + const { isConnected } = useAccount() + const { mutate: deployVault } = useVaultMutation() const columns = useMemo( () => createVaultTableColumns({ - data: vaults, + vaults: vaultDataList, openModalVaultId, setOpenModalVaultId, }), - [vaults, openModalVaultId] + [openModalVaultId, vaultDataList] ) const table = useReactTable({ - data: vaults, + data: vaultDataList ?? [], columns, getCoreRowModel: getCoreRowModel(), }) - const handleAddVaultModal = useCallback(() => { - sendVaultEvent({ type: 'START_CREATE_VAULT' }) - }, [sendVaultEvent]) - return (

    My vaults

    - {/* @ts-expect-error - Button component is not typed */} - + {isConnected && ( + // @ts-expect-error - Button component is not typed + + )}
    diff --git a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx index ede184d7c..2ea516119 100644 --- a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx +++ b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx @@ -1,25 +1,41 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useState } from 'react' +import { useEffect, useState } from 'react' +import { zodResolver } from '@hookform/resolvers/zod' import * as Dialog from '@radix-ui/react-dialog' import { ContextTag, Input } from '@status-im/components' import { InfoIcon } from '@status-im/icons/16' import { CloseIcon, IncorrectIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' +import { useForm } from 'react-hook-form' +import { z } from 'zod' + +import { useVaultLock } from '../../_hooks/useVaultLock' import type { HTMLAttributes } from 'react' +import type { Address } from 'viem' type ActionButton = HTMLAttributes & { label: string disabled?: boolean } +const createFormSchema = () => { + return z.object({ + years: z.string(), + days: z.string(), + }) +} + +type FormValues = z.infer> + type Props = { open?: boolean onOpenChange?: (open: boolean) => void onClose: () => void + vaultAddress: Address title: string description: string children?: React.ReactNode @@ -36,7 +52,6 @@ type Props = { initialDays?: string // Boost and unlock info boost?: string - unlockDate?: string // Info box content infoMessage?: string // Error validation @@ -53,27 +68,64 @@ const VaultLockConfigModal = (props: Props) => { onClose, children, title, + vaultAddress, description, sliderConfig = { - minLabel: '1 year', + minLabel: '90 days', maxLabel: '4 years', - minDays: 365, + minDays: 90, maxDays: 1460, initialPosition: 0, }, - initialYears = '1', - initialDays = '365', + initialYears = '0', + initialDays = '90', boost = 'x2.5', - unlockDate = '30/01/2026', infoMessage = 'Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks.', errorMessage: externalErrorMessage, onValidate, actions, } = props - const [years, setYears] = useState(initialYears) - const [days, setDays] = useState(initialDays) - const [errorMessage, setErrorMessage] = useState(null) + const { mutate: lockVault } = useVaultLock() + const { watch, setValue, handleSubmit } = useForm({ + resolver: zodResolver(createFormSchema()), + defaultValues: { + years: initialYears, + days: initialDays, + }, + }) + + const years = watch('years') + const days = watch('days') + + // Calculate initial slider value from initialYears and initialDays + const initialSliderValue = + parseInt(initialYears || '0') * 365 + parseInt(initialDays || '0') + + const [sliderValue, setSliderValue] = useState(initialSliderValue) + + // Calculate unlock date based on days input + const calculateUnlockDate = (daysToAdd: number): string => { + const today = new Date() + const unlockDate = new Date(today) + unlockDate.setDate(today.getDate() + daysToAdd) + + const day = String(unlockDate.getDate()).padStart(2, '0') + const month = String(unlockDate.getMonth() + 1).padStart(2, '0') + const year = unlockDate.getFullYear() + + return `${day}/${month}/${year}` + } + + const calculatedUnlockDate = calculateUnlockDate(parseInt(days || '0')) + + // Sync slider with days input value + useEffect(() => { + const daysValue = parseInt(days || '0') + if (!isNaN(daysValue) && daysValue !== sliderValue) { + setSliderValue(daysValue) + } + }, [days, sliderValue]) const handleOpenChange = (nextOpen: boolean) => { if (onOpenChange) { @@ -84,22 +136,96 @@ const VaultLockConfigModal = (props: Props) => { } } + const handleVaultLockAndExtend = async (data: FormValues) => { + const totalDays = parseInt(data.days || '0') + const lockPeriodInSeconds = BigInt(totalDays * 24 * 60 * 60) + + // Close the modal first + onClose() + + // Then trigger the lock transaction + lockVault({ + lockPeriodInSeconds, + vaultAddress, + }) + } + + const handleSliderChange = (e: React.ChangeEvent) => { + const inputDays = parseFloat(e.target.value) + setSliderValue(inputDays) + + // Convert to years (365 days = 1 year) + const yearsValue = (inputDays / 365).toFixed(2) + + setValue('years', yearsValue) + setValue('days', Math.round(inputDays).toString()) + } + const handleYearsChange = (value: string) => { - setYears(value) - if (onValidate) { - const error = onValidate(value, days) - setErrorMessage(error) + setValue('years', value) + + const yearsValue = parseFloat(value || '0') + if (!isNaN(yearsValue)) { + const totalDays = Math.round(yearsValue * 365) + const clampedDays = Math.max( + sliderConfig.minDays, + Math.min(totalDays, sliderConfig.maxDays) + ) + + setSliderValue(clampedDays) + setValue('days', clampedDays.toString()) } } const handleDaysChange = (value: string) => { - setDays(value) - if (onValidate) { - const error = onValidate(years, value) - setErrorMessage(error) + setValue('days', value) + + const inputDays = parseInt(value || '0') + if (!isNaN(inputDays)) { + const clampedDays = Math.max( + sliderConfig.minDays, + Math.min(inputDays, sliderConfig.maxDays) + ) + + setSliderValue(clampedDays) + setValue('years', (clampedDays / 365).toFixed(2)) } } + // Built-in validation based on slider config + const getValidationError = (): string | null => { + const yearsValue = parseFloat(years || '0') + const daysValue = parseInt(days || '0') + + // Check if days is below minimum + if (daysValue > 0 && daysValue < sliderConfig.minDays) { + return `Minimum lock time is ${sliderConfig.minLabel}` + } + + // Check if days is above maximum + if (daysValue > sliderConfig.maxDays) { + return `Maximum lock time is ${sliderConfig.maxLabel}` + } + + // Check if years is below minimum (90 days = 0.246 years approximately) + const minYears = sliderConfig.minDays / 365 + if (yearsValue > 0 && yearsValue < minYears) { + return `Minimum lock time is ${sliderConfig.minLabel}` + } + + // Check if years is above maximum (4 years) + const maxYears = sliderConfig.maxDays / 365 + if (yearsValue > maxYears) { + return `Maximum lock time is ${sliderConfig.maxLabel}` + } + + return null + } + + // Validate on value change + const builtInError = getValidationError() + const customError = onValidate ? onValidate(years, days) : null + const errorMessage = customError || builtInError const displayError = externalErrorMessage || errorMessage const hasError = Boolean(displayError) @@ -109,7 +235,10 @@ const VaultLockConfigModal = (props: Props) => { -
    +
    -
    + diff --git a/apps/hub/src/app/_components/vaults/withdraw-modal.tsx b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx index 6de7d0804..420465281 100644 --- a/apps/hub/src/app/_components/vaults/withdraw-modal.tsx +++ b/apps/hub/src/app/_components/vaults/withdraw-modal.tsx @@ -5,9 +5,10 @@ import * as Dialog from '@radix-ui/react-dialog' import { InfoIcon } from '@status-im/icons/12' import { CloseIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' -import { useAccount, useReadContract, useWriteContract } from 'wagmi' +import { useAccount, useReadContract } from 'wagmi' import { SNT_TOKEN } from '../../../config' +import { useVaultWithdraw } from '../../_hooks/useVaultWithdraw' import { vaultAbi } from '../../contracts' import type { Address } from 'viem' @@ -24,7 +25,7 @@ const VaultWithdrawModal = (props: Props) => { const { onClose, vaultAdress, open, onOpenChange, children } = props const { address } = useAccount() - const { writeContract } = useWriteContract() + const { mutate: withdrawFromVault } = useVaultWithdraw() const { data: availableWithdraw } = useReadContract({ abi: vaultAbi, address: vaultAdress, @@ -42,13 +43,14 @@ const VaultWithdrawModal = (props: Props) => { } const handleVaultWithdrawal = () => { - writeContract({ - abi: vaultAbi, - address: vaultAdress, - functionName: 'unstake', - args: [availableWithdraw || 0n], + if (!availableWithdraw || availableWithdraw === 0n) { + return + } + + withdrawFromVault({ + amountWei: availableWithdraw, + vaultAddress: vaultAdress, }) - onClose() } return ( @@ -56,7 +58,7 @@ const VaultWithdrawModal = (props: Props) => { {children} - +
    -
    -
    -

    +

    +
    +

    {address}

    @@ -110,7 +112,7 @@ const VaultWithdrawModal = (props: Props) => {
    -
    +
    @@ -118,7 +120,7 @@ const VaultWithdrawModal = (props: Props) => {
    -
    +

    Your funds will sent directly to your connected wallet.

    @@ -127,7 +129,7 @@ const VaultWithdrawModal = (props: Props) => { @@ -136,7 +138,7 @@ const VaultWithdrawModal = (props: Props) => {
    -
    +
    {/* @ts-expect-error - Button component is not typed */} + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function CompoundManager({ vaultAddress }: { vaultAddress: Address }) { + * const { mutate: compound, isPending, isSuccess, error } = useCompounder() + * + * const handleCompound = () => { + * compound( + * { vaultAddress }, + * { + * onSuccess: (txHash) => { + * console.log('Transaction hash:', txHash) + * }, + * onError: (error) => { + * console.error('Compound failed:', error) + * }, + * } + * ) + * } + * + * return ( + * + * ) + * } + * ``` + */ +export function useCompounder(): UseMutationResult< + Address, + Error, + CompoundVaultParams, + unknown +> { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, 'vault', address], + mutationFn: async ({ + vaultAddress, + }: CompoundVaultParams): Promise
    => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate vault address + if (!vaultAddress || vaultAddress === '0x0') { + throw new Error('Invalid vault address provided') + } + + // Execute compound transaction + const hash = await writeContractAsync({ + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'updateVault', + args: [vaultAddress], + }) + + return hash + }, + }) +} + +// ============================================================================ +// Compound All Mutation Hook +// ============================================================================ + +/** + * Mutation hook to compound multiplier points (MP) for all user vaults + * + * Calls StakingManager.updateAccount() which updates all vaults owned by + * the connected account in a single transaction. + * + * @returns Mutation result with mutate function to trigger compound all + * + * @throws {Error} When wallet is not connected + * + * @example + * ```tsx + * function CompoundAllButton() { + * const { mutate: compoundAll, isPending } = useCompoundAll() + * + * return ( + * + * ) + * } + * ``` + */ +export function useCompoundAll(): UseMutationResult< + Address, + Error, + void, + unknown +> { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, 'all', address], + mutationFn: async (): Promise
    => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Execute compound all transaction + const hash = await writeContractAsync({ + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'updateAccount', + args: [address], + }) + + return hash + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useExchangeRate.ts b/apps/hub/src/app/_hooks/useExchangeRate.ts new file mode 100644 index 000000000..e374c9f60 --- /dev/null +++ b/apps/hub/src/app/_hooks/useExchangeRate.ts @@ -0,0 +1,143 @@ +import { useQuery } from '@tanstack/react-query' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Exchange rate data for SNT/USDT pair + */ +export interface ExchangeRateData { + /** Exchange rate price (SNT in USDT) */ + price: number + /** Timestamp when the rate was fetched */ + timestamp: number +} + +/** + * Options for exchange rate query configuration + */ +export interface UseExchangeRateOptions { + /** + * Enable or disable the query + * @default true + */ + enabled?: boolean + /** + * Refetch interval in milliseconds + * @default 60000 (1 minute) + */ + refetchInterval?: number + /** + * Time in milliseconds until data is considered stale + * @default 30000 (30 seconds) + */ + staleTime?: number +} + +/** + * Return type for useExchangeRate hook + */ +export type UseExchangeRateReturn = ReturnType + +// ============================================================================ +// Constants +// ============================================================================ + +const QUERY_KEY_PREFIX = 'exchangeRate' +const BINANCE_API_URL = + 'https://api.binance.com/api/v3/ticker/price?symbol=SNTUSDT' +const DEFAULT_REFETCH_INTERVAL = 60_000 // 1 minute +const DEFAULT_STALE_TIME = 30_000 // 30 seconds + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Fetches the current SNT/USDT exchange rate from Binance + * + * @returns Exchange rate data with price and timestamp + * @throws Error if the API request fails or returns invalid data + */ +async function fetchExchangeRate(): Promise { + const response = await fetch(BINANCE_API_URL) + + if (!response.ok) { + throw new Error( + `Failed to fetch exchange rate: ${response.status} ${response.statusText}` + ) + } + + const data = await response.json() + + if (!data.price) { + throw new Error('Invalid response from Binance API: missing price') + } + + const price = parseFloat(data.price) + + if (isNaN(price)) { + throw new Error(`Invalid price value: ${data.price}`) + } + + return { + price, + timestamp: Date.now(), + } +} + +// ============================================================================ +// Hook +// ============================================================================ + +/** + * Hook to fetch and cache the current SNT/USDT exchange rate + * + * Automatically refetches the rate at regular intervals to keep data fresh. + * Provides loading, error, and success states via React Query. + * + * @param options - Query configuration options + * @returns React Query result with exchange rate data + * + * @example + * ```tsx + * function PriceDisplay() { + * const { data, isLoading, error } = useExchangeRate() + * + * if (isLoading) return
    Loading price...
    + * if (error) return
    Failed to load price
    + * + * return
    1 SNT = ${data.price.toFixed(6)} USDT
    + * } + * ``` + * + * @example + * ```tsx + * // Custom refetch interval + * function LivePrice() { + * const { data } = useExchangeRate({ + * refetchInterval: 30000, // Refetch every 30 seconds + * }) + * + * return
    ${data?.price.toFixed(6)}
    + * } + * ``` + */ +export function useExchangeRate(options: UseExchangeRateOptions = {}) { + const { + enabled = true, + refetchInterval = DEFAULT_REFETCH_INTERVAL, + staleTime = DEFAULT_STALE_TIME, + } = options + + return useQuery({ + queryKey: [QUERY_KEY_PREFIX] as const, + queryFn: fetchExchangeRate, + enabled, + refetchInterval, + staleTime, + retry: 3, + retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), + }) +} diff --git a/apps/hub/src/app/_hooks/useTokenApproval.ts b/apps/hub/src/app/_hooks/useTokenApproval.ts new file mode 100644 index 000000000..2ebda07b5 --- /dev/null +++ b/apps/hub/src/app/_hooks/useTokenApproval.ts @@ -0,0 +1,133 @@ +import { useMutation } from '@tanstack/react-query' +import { zeroAddress } from 'viem' +import { parseUnits } from 'viem/utils' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { SNT_TOKEN } from '../../config' +import { tokenAbi } from '../contracts' +import { useVaultStateContext } from './vault-state-context' + +import type { UseMutationResult } from '@tanstack/react-query' +import type { Address } from 'viem/accounts' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for token approval mutation + */ +export interface ApprovalParams { + /** Amount of tokens to approve (in token units, not wei) */ + amount: string + /** Address of the vault to approve tokens for */ + vaultAddress: Address +} + +/** + * Return type for useTokenApproval hook + */ +export type UseTokenApprovalReturn = UseMutationResult< + Address, + Error, + ApprovalParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY_PREFIX = 'token-approval' + +// ============================================================================ +// Hook +// ============================================================================ + +/** + * Hook for approving SNT tokens for a vault + * + * This hook handles the token approval flow: + * 1. Checks current allowance + * 2. If allowance is insufficient, requests approval from user + * 3. Waits for transaction confirmation + * 4. Updates state machine with approval status + * + * @example + * ```tsx + * const { mutate: approveTokens, isPending } = useTokenApproval() + * + * const handleApprove = () => { + * approveTokens({ + * amount: '100', + * vaultAddress: '0x...' + * }) + * } + * ``` + * + * @returns Mutation result with approval function and status + */ +export const useTokenApproval = (): UseTokenApprovalReturn => { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent } = useVaultStateContext() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, address], + mutationFn: async ({ + amount, + vaultAddress, + }: ApprovalParams): Promise
    => { + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + if (!vaultAddress || vaultAddress === zeroAddress) { + throw new Error('Invalid vault address provided') + } + + if (!amount || parseFloat(amount) <= 0) { + throw new Error('Amount must be greater than 0') + } + + const amountWei = parseUnits(amount, SNT_TOKEN.decimals) + + sendVaultEvent({ + type: 'START_INCREASE_ALLOWANCE', + amount: amount, + }) + + try { + const hash = await writeContractAsync({ + address: SNT_TOKEN.address, + abi: tokenAbi, + functionName: 'approve', + args: [vaultAddress, amountWei], + }) + + sendVaultEvent({ type: 'SIGN' }) + + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: 1, + }) + + if (status === 'reverted') { + throw new Error('Transaction was reverted') + } + + sendVaultEvent({ type: 'COMPLETE', amount }) + + return hash + } catch (error) { + console.error('Failed to approve tokens:', error) + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useVault.ts b/apps/hub/src/app/_hooks/useVault.ts new file mode 100644 index 000000000..be806942b --- /dev/null +++ b/apps/hub/src/app/_hooks/useVault.ts @@ -0,0 +1,174 @@ +import { useToast } from '@status-im/components' +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { VAULT_FACTORY } from '../../config' +import { shortenAddress } from '../../utils/address' +import { vaultFactoryAbi } from '../contracts' +import { useAccountVaults } from './useAccountVaults' +import { useVaultStateContext } from './vault-state-context' + +import type { Address, Hash } from 'viem' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Return type for the useVaultMutation hook + */ +export type UseVaultMutationReturn = UseMutationResult< + Address, + Error, + void, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY_PREFIX = 'vault' as const + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to create a new vault + * + * Creates a new vault via the VaultFactory contract and manages the state + * machine transitions for the vault creation process. + * + * @returns Mutation result with mutate function to trigger vault creation + * + * @throws {Error} When wallet is not connected + * + * @example + * ```tsx + * function CreateVaultButton() { + * const { mutate: createVault, isPending } = useVaultMutation() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function CreateVaultManager() { + * const { mutate: createVault, isPending } = useVaultMutation() + * + * const handleCreate = () => { + * createVault(undefined, { + * onSuccess: (txHash) => { + * console.log('Vault created! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Failed to create vault:', error) + * }, + * }) + * } + * + * return ( + * + * ) + * } + * ``` + */ +export function useVaultMutation(): UseVaultMutationReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const { refetch: refetchAccountVaults } = useAccountVaults() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const config = useConfig() + const toast = useToast() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, 'create', address], + mutationFn: async (): Promise => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Send state machine event to start vault creation + sendVaultEvent({ type: 'START_CREATE_VAULT' }) + + try { + // Execute vault creation transaction + const hash = await writeContractAsync({ + address: VAULT_FACTORY.address, + abi: vaultFactoryAbi, + functionName: 'createVault', + }) + + // Transaction submitted successfully, transition to processing + sendVaultEvent({ type: 'SIGN' }) + + const { status, logs } = await waitForTransactionReceipt(config, { + hash, + confirmations: 1, + }) + + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + const vaultCreatedLog = logs.find(log => { + // First check if this log is from our contract + if ( + log.address.toLowerCase() !== VAULT_FACTORY.address.toLowerCase() + ) { + return false + } + + // Check if this is the VaultCreated event signature + // This is the actual signature from the contract + return ( + log.topics[0] === + '0x5d9c31ffa0fecffd7cf379989a3c7af252f0335e0d2a1320b55245912c781f53' + ) + }) + + if (!vaultCreatedLog || !vaultCreatedLog.topics[1]) { + console.log('Receipt logs:', logs) + throw new Error('VaultCreated event not found in transaction logs') + } + + // The vault address is the first indexed parameter + // Remove the padding from the address (first 24 bytes) + const paddedAddress = vaultCreatedLog.topics[1] as `0x${string}` + const deployedVaultAddress = `0x${paddedAddress.slice(26)}` as Address + + // Show toast before resetting state + toast.positive( + `Vault ${shortenAddress(deployedVaultAddress)} has been created` + ) + + // Small delay to ensure toast is rendered before state reset + await new Promise(resolve => setTimeout(resolve, 100)) + resetVault() + refetchAccountVaults() + return hash + } catch (error) { + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useVaultLock.ts b/apps/hub/src/app/_hooks/useVaultLock.ts new file mode 100644 index 000000000..2566edfce --- /dev/null +++ b/apps/hub/src/app/_hooks/useVaultLock.ts @@ -0,0 +1,166 @@ +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { statusNetworkTestnet } from '../../config/chain' +import { vaultAbi } from '../contracts' +import { useAccountVaults } from './useAccountVaults' +import { useVaultStateContext } from './vault-state-context' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for locking a vault + */ +export interface LockParams { + /** Lock period in seconds */ + lockPeriodInSeconds: bigint + /** Vault address */ + vaultAddress: Address +} + +/** + * Return type for the useVaultLock hook + */ +export type UseVaultLockReturn = UseMutationResult< + Address, + Error, + LockParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY_PREFIX = 'vault-lock' as const +const CONFIRMATION_BLOCKS = 1 + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to lock a vault + * + * Locks a vault for a specified period to increase boost multiplier. + * Manages the state machine transitions for the lock process. + * + * @returns Mutation result with mutate function to trigger vault lock + * + * @throws {Error} When wallet is not connected + * @throws {Error} When lock period is invalid + * @throws {Error} When transaction is reverted + * + * @example + * Basic usage + * ```tsx + * function LockButton({ lockPeriod, vaultAddress }: { lockPeriod: bigint, vaultAddress: Address }) { + * const { mutate: lockVault, isPending } = useVaultLock() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function LockManager() { + * const { mutate: lockVault, isPending } = useVaultLock() + * + * const handleLock = (lockPeriod: bigint, vaultAddress: Address) => { + * lockVault( + * { lockPeriodInSeconds: lockPeriod, vaultAddress }, + * { + * onSuccess: (txHash) => { + * console.log('Locked successfully! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Failed to lock:', error) + * }, + * } + * ) + * } + * + * return + * } + * ``` + */ +export function useVaultLock(): UseVaultLockReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const { refetch: refetchAccountVaults } = useAccountVaults() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, address], + mutationFn: async ({ + lockPeriodInSeconds, + vaultAddress, + }: LockParams): Promise
    => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate lock period + if (lockPeriodInSeconds <= 0n) { + throw new Error('Lock period must be greater than 0') + } + + // Send state machine event to start locking + sendVaultEvent({ + type: 'START_LOCK', + }) + + try { + // Execute lock transaction + const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, + address: vaultAddress, + abi: vaultAbi, + functionName: 'lock', + args: [lockPeriodInSeconds], + }) + + // Transaction submitted successfully, transition to processing + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: CONFIRMATION_BLOCKS, + }) + + // Check if transaction was reverted + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + // Transaction successful, close dialog by resetting state + resetVault() + refetchAccountVaults() + return hash + } catch (error) { + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useVaultStateMachine.ts b/apps/hub/src/app/_hooks/useVaultStateMachine.ts index a0f0da09b..066491083 100644 --- a/apps/hub/src/app/_hooks/useVaultStateMachine.ts +++ b/apps/hub/src/app/_hooks/useVaultStateMachine.ts @@ -17,6 +17,15 @@ export type VaultState = step: 'initialize' | 'processing' | 'rejected' amount?: string } + | { + type: 'withdraw' + step: 'initialize' | 'processing' | 'rejected' + amount?: string + } + | { + type: 'lock' + step: 'initialize' | 'processing' | 'rejected' + } | { type: 'success' } // Event definitions @@ -30,6 +39,8 @@ export type VaultEvent = | { type: 'COMPLETE'; amount?: string } | { type: 'READY_TO_STAKE' } | { type: 'START_STAKING'; amount?: string } + | { type: 'START_WITHDRAW'; amount?: string } + | { type: 'START_LOCK' } | { type: 'RESET' } // Transition function @@ -52,6 +63,11 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: event.amount, }) ) + .with([{ type: 'idle' }, { type: 'START_STAKING' }], ([, event]) => ({ + type: 'staking', + step: 'initialize', + amount: event.amount, + })) // SIWE flow .with([{ type: 'siwe', step: 'initialize' }, { type: 'SIGN' }], () => ({ @@ -87,6 +103,13 @@ function transition(state: VaultState, event: VaultEvent): VaultState { step: 'processing', }) ) + .with( + [{ type: 'createVault', step: 'initialize' }, { type: 'REJECT' }], + () => ({ + type: 'createVault', + step: 'rejected', + }) + ) .with( [{ type: 'createVault', step: 'processing' }, { type: 'REJECT' }], () => ({ @@ -112,6 +135,14 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: state.amount, }) ) + .with( + [{ type: 'increaseAllowance', step: 'initialize' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'increaseAllowance', + step: 'rejected', + amount: state.amount, + }) + ) .with( [ { type: 'increaseAllowance', step: 'processing' }, @@ -142,6 +173,10 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: event.amount || state.amount, }) ) + .with( + [{ type: 'increaseAllowance', step: 'rejected' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) // Staking flow .with( @@ -164,6 +199,10 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: state.amount, }) ) + .with( + [{ type: 'staking', step: 'processing' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) .with( [{ type: 'staking', step: 'rejected' }, { type: 'START_STAKING' }], ([state, event]) => ({ @@ -172,6 +211,87 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: event.amount || state.amount, }) ) + .with([{ type: 'staking', step: 'rejected' }, { type: 'RESET' }], () => ({ + type: 'idle', + })) + + // Withdraw flow + .with([{ type: 'idle' }, { type: 'START_WITHDRAW' }], ([, event]) => ({ + type: 'withdraw', + step: 'initialize', + amount: event.amount, + })) + .with( + [{ type: 'withdraw', step: 'initialize' }, { type: 'SIGN' }], + ([state]) => ({ + type: 'withdraw', + step: 'processing', + amount: state.amount, + }) + ) + .with( + [{ type: 'withdraw', step: 'initialize' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'withdraw', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [{ type: 'withdraw', step: 'processing' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'withdraw', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [{ type: 'withdraw', step: 'processing' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) + .with( + [{ type: 'withdraw', step: 'rejected' }, { type: 'START_WITHDRAW' }], + ([state, event]) => ({ + type: 'withdraw', + step: 'initialize', + amount: event.amount || state.amount, + }) + ) + .with( + [{ type: 'withdraw', step: 'rejected' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) + + // Lock flow + .with([{ type: 'idle' }, { type: 'START_LOCK' }], () => ({ + type: 'lock', + step: 'initialize', + })) + .with([{ type: 'lock', step: 'initialize' }, { type: 'SIGN' }], () => ({ + type: 'lock', + step: 'processing', + })) + .with([{ type: 'lock', step: 'initialize' }, { type: 'REJECT' }], () => ({ + type: 'lock', + step: 'rejected', + })) + .with([{ type: 'lock', step: 'processing' }, { type: 'REJECT' }], () => ({ + type: 'lock', + step: 'rejected', + })) + .with([{ type: 'lock', step: 'processing' }, { type: 'RESET' }], () => ({ + type: 'idle', + })) + .with( + [{ type: 'lock', step: 'rejected' }, { type: 'START_LOCK' }], + () => ({ + type: 'lock', + step: 'initialize', + }) + ) + .with([{ type: 'lock', step: 'rejected' }, { type: 'RESET' }], () => ({ + type: 'idle', + })) // Reset from success .with([{ type: 'success' }, { type: 'RESET' }], () => ({ type: 'idle' })) diff --git a/apps/hub/src/app/_hooks/useVaultTokenStake.ts b/apps/hub/src/app/_hooks/useVaultTokenStake.ts new file mode 100644 index 000000000..f4d14f4fa --- /dev/null +++ b/apps/hub/src/app/_hooks/useVaultTokenStake.ts @@ -0,0 +1,170 @@ +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address, formatUnits } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { SNT_TOKEN } from '../../config' +import { statusNetworkTestnet } from '../../config/chain' +import { vaultAbi } from '../contracts' +import { useAccountVaults } from './useAccountVaults' +import { useVaultStateContext } from './vault-state-context' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for staking to a vault + */ +export interface StakeParams { + /** Amount to stake in wei */ + amountWei: bigint + /** Lock period in seconds (0 for no lock) */ + lockPeriod: bigint + /** Vault address */ + vaultAddress: Address +} + +/** + * Return type for the useVaultStake hook + */ +export type UseVaultStakeReturn = UseMutationResult< + Address, + Error, + StakeParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY_PREFIX = 'vault-stake' as const +const CONFIRMATION_BLOCKS = 1 + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to stake tokens to a vault + * + * Stakes tokens to the StakingManager contract with an optional lock period. + * Manages the state machine transitions for the staking process. + * + * @returns Mutation result with mutate function to trigger staking + * + * @throws {Error} When wallet is not connected + * @throws {Error} When transaction is reverted + * + * @example + * Basic usage + * ```tsx + * function StakeButton({ amount, lockPeriod }: { amount: bigint, lockPeriod: bigint }) { + * const { mutate: stake, isPending } = useVaultStake() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function StakeManager() { + * const { mutate: stake, isPending } = useVaultStake() + * + * const handleStake = (amount: bigint, lockPeriod: bigint) => { + * stake( + * { amountWei: amount, lockPeriod }, + * { + * onSuccess: (txHash) => { + * console.log('Staked successfully! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Failed to stake:', error) + * }, + * } + * ) + * } + * + * return + * } + * ``` + */ +export function useVaultTokenStake(): UseVaultStakeReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const { refetch: refetchAccountVaults } = useAccountVaults() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, address], + mutationFn: async ({ + amountWei, + lockPeriod, + vaultAddress, + }: StakeParams): Promise
    => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate amount + if (amountWei <= 0n) { + throw new Error('Amount must be greater than 0') + } + + // Send state machine event to start staking + sendVaultEvent({ + type: 'START_STAKING', + amount: formatUnits(amountWei, SNT_TOKEN.decimals), + }) + + try { + // Execute staking transaction + const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, + address: vaultAddress, + abi: vaultAbi, + functionName: 'stake', + args: [amountWei, lockPeriod], + }) + + // Transaction submitted successfully, transition to processing + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: CONFIRMATION_BLOCKS, + }) + + // Check if transaction was reverted + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + // Transaction successful, close dialog by resetting state + resetVault() + refetchAccountVaults() + return hash + } catch (error) { + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useVaultWithdraw.ts b/apps/hub/src/app/_hooks/useVaultWithdraw.ts new file mode 100644 index 000000000..9283ec4df --- /dev/null +++ b/apps/hub/src/app/_hooks/useVaultWithdraw.ts @@ -0,0 +1,168 @@ +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address, formatUnits } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { SNT_TOKEN } from '../../config' +import { statusNetworkTestnet } from '../../config/chain' +import { vaultAbi } from '../contracts' +import { useAccountVaults } from './useAccountVaults' +import { useVaultStateContext } from './vault-state-context' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for withdrawing from a vault + */ +export interface WithdrawParams { + /** Amount to withdraw in wei */ + amountWei: bigint + /** Vault address */ + vaultAddress: Address +} + +/** + * Return type for the useVaultWithdraw hook + */ +export type UseVaultWithdrawReturn = UseMutationResult< + Address, + Error, + WithdrawParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY_PREFIX = 'vault-withdraw' as const +const CONFIRMATION_BLOCKS = 1 + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to withdraw tokens from a vault + * + * Withdraws (unstakes) tokens from a vault contract. + * Manages the state machine transitions for the withdrawal process. + * + * @returns Mutation result with mutate function to trigger withdrawal + * + * @throws {Error} When wallet is not connected + * @throws {Error} When amount is invalid + * @throws {Error} When transaction is reverted + * + * @example + * Basic usage + * ```tsx + * function WithdrawButton({ amount, vaultAddress }: { amount: bigint, vaultAddress: Address }) { + * const { mutate: withdraw, isPending } = useVaultWithdraw() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function WithdrawManager() { + * const { mutate: withdraw, isPending } = useVaultWithdraw() + * + * const handleWithdraw = (amount: bigint, vaultAddress: Address) => { + * withdraw( + * { amountWei: amount, vaultAddress }, + * { + * onSuccess: (txHash) => { + * console.log('Withdrawn successfully! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Failed to withdraw:', error) + * }, + * } + * ) + * } + * + * return + * } + * ``` + */ +export function useVaultWithdraw(): UseVaultWithdrawReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const { refetch: refetchAccountVaults } = useAccountVaults() + + return useMutation({ + mutationKey: [MUTATION_KEY_PREFIX, address], + mutationFn: async ({ + amountWei, + vaultAddress, + }: WithdrawParams): Promise
    => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate amount + if (amountWei <= 0n) { + throw new Error('Amount must be greater than 0') + } + + // Send state machine event to start withdrawal + sendVaultEvent({ + type: 'START_WITHDRAW', + amount: formatUnits(amountWei, SNT_TOKEN.decimals), + }) + + try { + // Execute withdrawal transaction + const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, + address: vaultAddress, + abi: vaultAbi, + functionName: 'unstake', + args: [amountWei], + }) + + // Transaction submitted successfully, transition to processing + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: CONFIRMATION_BLOCKS, + }) + + // Check if transaction was reverted + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + // Transaction successful, close dialog by resetting state + resetVault() + refetchAccountVaults() + return hash + } catch (error) { + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index 744dadc7d..eeaffed2a 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -1,5 +1,6 @@ 'use client' +import { ToastContainer } from '@status-im/components' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ConnectKitProvider } from 'connectkit' import { WagmiProvider } from 'wagmi' @@ -24,6 +25,9 @@ const ProvidersContent = ({ children }: { children: React.ReactNode }) => { const { state: vaultState, reset: resetVault } = useVaultStateContext() const dialogContent = useActionStatusContent(vaultState) + console.log('vaultState', vaultState) + console.log('dialogContent', dialogContent) + return ( <> {children} @@ -46,6 +50,7 @@ export const Providers = ({ children }: { children: React.ReactNode }) => { {children} + diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index cb8f793bc..44d9d08da 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -1,8 +1,9 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useMemo, useState } from 'react' +import { zodResolver } from '@hookform/resolvers/zod' import { Tooltip } from '@status-im/components' import { DropdownIcon, @@ -13,47 +14,68 @@ import { import { Button, ButtonLink } from '@status-im/status-network/components' import { ConnectKitButton } from 'connectkit' import Image from 'next/image' +import { useForm, useWatch } from 'react-hook-form' import { match } from 'ts-pattern' -import { - useAccount, - useBalance, - useReadContract, - useWaitForTransactionReceipt, - useWriteContract, -} from 'wagmi' +import { parseUnits } from 'viem' +import { useAccount, useBalance, useConfig, useReadContract } from 'wagmi' +import { readContract } from 'wagmi/actions' +import { z } from 'zod' import { HubLayout } from '~components/hub-layout' -import { SNT_TOKEN, STAKING_MANAGER, VAULT_FACTORY } from '../../config' +import { SNT_TOKEN, STAKING_MANAGER } from '../../config' import { statusNetworkTestnet } from '../../config/chain' -import { formatSNT } from '../../utils/currency' +import { formatCurrency, formatSNT } from '../../utils/currency' import { LaunchIcon, SNTIcon } from '../_components/icons' import { PromoModal } from '../_components/stake/promo-modal' +import { VaultSelect } from '../_components/vault-select' import { VaultsTable } from '../_components/vaults/table' import { useAccountVaults } from '../_hooks/useAccountVaults' +import { useExchangeRate } from '../_hooks/useExchangeRate' import { useFaucetMutation, useFaucetQuery } from '../_hooks/useFaucet' -import { useSiwe } from '../_hooks/useSIWE' +import { useTokenApproval } from '../_hooks/useTokenApproval' +import { useVaultMutation } from '../_hooks/useVault' +import { useVaultTokenStake } from '../_hooks/useVaultTokenStake' import { useWeightedBoost } from '../_hooks/useWeightedBoost' import { useVaultStateContext } from '../_hooks/vault-state-context' -import { stakingManagerAbi, vaultFactoryAbi } from '../contracts' +import { stakingManagerAbi, tokenAbi } from '../contracts' + +import type { Address } from 'viem' + +const createSkakeFormSchema = () => { + return z.object({ + amount: z.string(), + vault: z.string(), + }) +} + +type FormValues = z.infer> type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' const TOKEN_TICKER = 'SNT' +// const SUCCESS_TOAST_DURATION_MS = 1500 export default function StakePage() { - const siweHook = useSiwe() - const siweRef = useRef(siweHook) - siweRef.current = siweHook - const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) const { isConnected, address } = useAccount() + const config = useConfig() const { mutate: claimTokens } = useFaucetMutation() const { data: faucetData } = useFaucetQuery() const { data: vaults } = useAccountVaults() const weightedBoost = useWeightedBoost(vaults) + const { data: exchangeRate } = useExchangeRate() + + const form = useForm({ + resolver: zodResolver(createSkakeFormSchema()), + mode: 'onChange', + defaultValues: { + amount: '', + vault: '', + }, + }) const { data: balance } = useBalance({ chainId: statusNetworkTestnet.id, @@ -71,145 +93,80 @@ export default function StakePage() { functionName: 'totalStaked', }) as { data: bigint } - const { - writeContract, - data: createVaultTxHash, - isError: isTxError, - isSuccess: isTxSuccess, - } = useWriteContract() - - const { - isSuccess: isTxConfirmed, - data: txReceipt, - error: txError, - } = useWaitForTransactionReceipt({ - hash: createVaultTxHash, - }) + const { mutate: deployVault } = useVaultMutation() + const { mutate: approveTokens } = useTokenApproval() + const { mutate: stakeVault } = useVaultTokenStake() // State machine for vault operations - const { - state: vaultState, - send: sendVaultEvent, - reset: resetVault, - } = useVaultStateContext() + const { send: sendVaultEvent } = useVaultStateContext() const status: ConnectionStatus = useMemo(() => { if (isConnected) return 'connected' return isPromoModalOpen ? 'uninstalled' : 'disconnected' }, [isConnected, isPromoModalOpen]) - // Track previous connection state to detect new connections - const prevConnectedRef = useRef(isConnected) - - useEffect(() => { - // Check if user has already completed SIWE for this address - const siweCompleted = address - ? sessionStorage.getItem(`siwe_${address}`) - : null - - // Only trigger SIWE when transitioning from disconnected to connected AND not already signed - if ( - isConnected && - !prevConnectedRef.current && - vaultState.type === 'idle' && - !siweCompleted - ) { - sendVaultEvent({ type: 'START_SIWE' }) - } - prevConnectedRef.current = isConnected - }, [isConnected, vaultState.type, sendVaultEvent, address]) + // Watch the amount field for changes + const amountValue = useWatch({ + control: form.control, + name: 'amount', + defaultValue: '', + }) - const handleCreateVault = (e: React.MouseEvent) => { - e.preventDefault() - if (isConnected) { - sendVaultEvent({ type: 'START_CREATE_VAULT' }) + const amountInUSD = useMemo(() => { + const amountInputNumber = parseFloat(amountValue || '0') + if (exchangeRate && !isNaN(amountInputNumber)) { + return amountInputNumber * exchangeRate.price } - } - - // Handle SIWE flow - useEffect(() => { - if (vaultState.type === 'siwe' && vaultState.step === 'initialize') { - const handleSiwe = async () => { - try { - await siweRef.current.signIn() - sendVaultEvent({ type: 'SIGN' }) - await new Promise(resolve => setTimeout(resolve, 500)) - - // Mark SIWE as completed for this address - if (address) { - sessionStorage.setItem(`siwe_${address}`, 'true') + return 0 + }, [amountValue, exchangeRate]) + + const handleSubmit = async (data: FormValues) => { + if (!data.amount || !data.vault || !address) return + + try { + const amountWei = parseUnits(data.amount, SNT_TOKEN.decimals) + + // Check current allowance + const currentAllowance = (await readContract(config, { + address: SNT_TOKEN.address, + abi: tokenAbi, + functionName: 'allowance', + args: [address, data.vault as Address], + })) as bigint + + // Transition to increase allowance if not enough allowance + if (amountWei >= currentAllowance) { + approveTokens( + { + amount: data.amount, + vaultAddress: data.vault as Address, + }, + { + onSuccess: () => { + // After approval completes, proceed to staking + stakeVault({ + amountWei, + lockPeriod: 0n, + vaultAddress: data.vault as Address, + }) + }, + onError: error => { + throw error + }, } - - resetVault() - } catch { - sendVaultEvent({ type: 'REJECT' }) - } + ) + } else { + // Allowance already sufficient, go straight to staking + stakeVault({ + amountWei, + lockPeriod: 0n, + vaultAddress: data.vault as Address, + }) } - handleSiwe() - } - }, [vaultState, sendVaultEvent, resetVault, address]) - - // Handle vault creation - trigger contract write on initialize - useEffect(() => { - if (vaultState.type === 'createVault' && vaultState.step === 'initialize') { - writeContract({ - chain: statusNetworkTestnet, - account: address, - address: VAULT_FACTORY.address, - abi: vaultFactoryAbi, - functionName: 'createVault', - }) - } - }, [address, vaultState, writeContract]) - - // Transition to processing when user signs in wallet - useEffect(() => { - if ( - isTxSuccess && - vaultState.type === 'createVault' && - vaultState.step === 'initialize' - ) { - sendVaultEvent({ type: 'SIGN' }) - } - }, [isTxSuccess, vaultState, sendVaultEvent]) - - // Handle write errors (user rejection, insufficient gas, etc.) - useEffect(() => { - if (isTxError && vaultState.type === 'createVault') { - // TODO: pass error to state machine - sendVaultEvent({ type: 'REJECT' }) - } - }, [isTxError, vaultState.type, sendVaultEvent]) - - // Handle transaction errors (reverted, failed on-chain) - useEffect(() => { - if (txError && vaultState.type === 'createVault') { + } catch (error) { sendVaultEvent({ type: 'REJECT' }) + console.error('Failed to check allowance:', error) } - }, [txError, vaultState, sendVaultEvent]) - - // Handle transaction confirmation - close dialog immediately - useEffect(() => { - if ( - isTxConfirmed && - vaultState.type === 'createVault' && - vaultState.step === 'processing' - ) { - resetVault() - // TODO: show toast message - } - }, [isTxConfirmed, vaultState, resetVault, txReceipt]) - - // Auto-close success dialog after showing success state (for other flows) - useEffect(() => { - if (vaultState.type === 'success') { - const timer = setTimeout(() => resetVault(), 1500) - return () => clearTimeout(timer) - } - }, [vaultState.type, resetVault]) - - const handleClosePromoModal = () => { - setIsPromoModalOpen(false) } return ( @@ -275,7 +232,10 @@ export default function StakePage() {
    -
    +
    @@ -309,12 +269,18 @@ export default function StakePage() {
    - 0 + {formatCurrency(amountInUSD)}
    @@ -353,19 +319,16 @@ export default function StakePage() { {match(status) .with('connected', () => ( - + form.setValue('vault', value)} + /> )) .otherwise(() => (
    - New vault + Add new vault
    @@ -376,7 +339,7 @@ export default function StakePage() { .with('uninstalled', () => ( setIsPromoModalOpen(false)} > {/* @ts-expect-error - TODO: fix this */} - )) + .with('connected', () => { + const selectedVault = form.watch('vault') + const amount = form.watch('amount') + const hasAmount = amount !== '' && parseFloat(amount) > 0 + const hasSelectedVault = + selectedVault !== '' && selectedVault !== 'new' + + return hasSelectedVault && hasAmount ? ( + // @ts-expect-error - TODO: fix this + + ) : ( + // @ts-expect-error - TODO: fix this + + ) + }) .exhaustive()} @@ -447,11 +423,17 @@ export default function StakePage() { x{weightedBoost.formatted}
    -

    - {weightedBoost.hasStake - ? `${weightedBoost.totalStaked.toFixed(2)} ${TOKEN_TICKER} staked` - : 'No points are ready to compound'} -

    +
    +

    + {weightedBoost.hasStake + ? `${weightedBoost.totalStaked.toFixed(2)} ${TOKEN_TICKER} staked` + : 'No points are ready to compound'} +

    + {/* @ts-expect-error - TODO: fix this */} + +
    diff --git a/apps/hub/src/utils/address.ts b/apps/hub/src/utils/address.ts new file mode 100644 index 000000000..d8d0330da --- /dev/null +++ b/apps/hub/src/utils/address.ts @@ -0,0 +1,3 @@ +export const shortenAddress = (address: string) => { + return `${address.slice(0, 6)}...${address.slice(-4)}` +} diff --git a/apps/hub/src/utils/currency.ts b/apps/hub/src/utils/currency.ts index 44ace298b..18a00036d 100644 --- a/apps/hub/src/utils/currency.ts +++ b/apps/hub/src/utils/currency.ts @@ -266,26 +266,28 @@ export function formatStablecoin( /** * Format a currency value for display in UI + * Uses Intl.NumberFormat with USD currency by default * Automatically handles large numbers with compact notation * * @param amount - Currency amount - * @param options - Formatting options with optional currency symbol + * @param options - Formatting options * @returns Formatted currency string * * @example * ```ts - * formatCurrency(1234567) // => "1.23M" - * formatCurrency(999, { symbol: '$' }) // => "$999.00" - * formatCurrency(1500000n, { symbol: '$', tokenDecimals: 18 }) // => "$0.00" + * formatCurrency(1234567) // => "$1.23M" + * formatCurrency(999) // => "$999.00" + * formatCurrency(1500000n, { tokenDecimals: 18 }) // => "$0.00" + * formatCurrency(100, { currency: 'EUR' }) // => "€100.00" * ``` */ export function formatCurrency( amount: number | bigint | string, - options: FormatTokenOptions & { symbol?: string } = {} + options: FormatTokenOptions & { currency?: string } = {} ): string { const { - symbol = '', - tokenDecimals = DEFAULT_TOKEN_DECIMALS, + currency = 'USD', + tokenDecimals = 0, // Default to 0 for currency values (already in display units) locale = DEFAULT_LOCALE, decimals = DEFAULT_DISPLAY_DECIMALS, ...rest @@ -298,14 +300,15 @@ export function formatCurrency( rest.compact !== false && Math.abs(numericAmount) >= COMPACT_THRESHOLD const formatter = new Intl.NumberFormat(locale, { + style: 'currency', + currency, minimumFractionDigits: rest.minimumFractionDigits ?? decimals, maximumFractionDigits: rest.maximumFractionDigits ?? decimals, notation: shouldCompact ? 'compact' : 'standard', compactDisplay: 'short', }) - const formatted = formatter.format(numericAmount) - return symbol ? `${symbol}${formatted}` : formatted + return formatter.format(numericAmount) } // ============================================================================ diff --git a/apps/hub/src/utils/vault.ts b/apps/hub/src/utils/vault.ts index b4abc5832..842e6668d 100644 --- a/apps/hub/src/utils/vault.ts +++ b/apps/hub/src/utils/vault.ts @@ -1,3 +1,7 @@ +import { type Address, formatUnits } from 'viem' + +import { SNT_TOKEN } from '../config' + import type { Vault } from '../app/_components/vaults/types' import type { VaultData, @@ -58,3 +62,20 @@ export const transformVaults = (vaultDataList: VaultWithAddress[]): Vault[] => { }) .filter((vault): vault is Vault => vault !== null) } + +export function calculateVaultBoost( + vaults: VaultWithAddress[], + vaultAddress: Address +): string { + const account = vaults.find(vault => vault.address === vaultAddress) + if (!account || account.data?.stakedBalance === 0n) return '1.00' + const vaultStaked = Number( + formatUnits(account.data?.stakedBalance || 0n, SNT_TOKEN.decimals) + ) + const vaultMp = Number( + formatUnits(account.data?.mpAccrued || 0n, SNT_TOKEN.decimals) + ) + // Add 1 to reflect the boost (base multiplier is 1x) + const boost = vaultMp / vaultStaked + 1 + return boost.toFixed(2) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3f0346af..9e71c27b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -261,6 +261,12 @@ importers: apps/hub: dependencies: + '@hookform/devtools': + specifier: ^4.3.1 + version: 4.4.0(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@hookform/resolvers': + specifier: ^3.1.1 + version: 3.10.0(react-hook-form@7.64.0(react@19.1.0)) '@status-im/colors': specifier: workspace:* version: link:../../packages/colors @@ -297,6 +303,9 @@ importers: react-dom: specifier: ^19.0.0 version: 19.1.0(react@19.1.0) + react-hook-form: + specifier: ^7.45.1 + version: 7.64.0(react@19.1.0) rehype-slug: specifier: ^6.0.0 version: 6.0.0 @@ -309,6 +318,9 @@ importers: viem: specifier: ^2.21.1 version: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + zod: + specifier: 3.23.8 + version: 3.23.8 devDependencies: '@ianvs/prettier-plugin-sort-imports': specifier: ^4.3.1 @@ -516,10 +528,10 @@ importers: version: 0.6.1 connectkit: specifier: ^1.9.0 - version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + version: 1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -576,10 +588,10 @@ importers: version: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -651,10 +663,10 @@ importers: version: 5.0.1 viem: specifier: ^2.21.1 - version: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + version: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) wagmi: specifier: ^2.12.8 - version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + version: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) zod: specifier: 3.23.8 version: 3.23.8 @@ -664,10 +676,10 @@ importers: version: 2.0.0(prettier@3.3.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.25.4) + version: 0.3.4(esbuild@0.19.12) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -676,7 +688,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -790,7 +802,7 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.25.4) + version: 9.2.1(esbuild@0.19.12) nanoid: specifier: ^5.0.8 version: 5.1.5 @@ -1117,7 +1129,7 @@ importers: version: 0.6.1 contentlayer: specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -1177,10 +1189,10 @@ importers: version: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) next-contentlayer: specifier: ^0.3.4 - version: 0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote: specifier: ^5.0.0 - version: 5.0.0(@types/react@19.1.0)(acorn@8.14.0)(react@19.1.0) + version: 5.0.0(@types/react@19.1.0)(acorn@8.14.1)(react@19.1.0) oslo: specifier: 1.0.1 version: 1.0.1(patch_hash=d3ggqvlr26vmqrkdyyommjkaeu) @@ -1256,10 +1268,10 @@ importers: version: 2.0.0(prettier@3.5.3) '@contentlayer/core': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/source-files': specifier: ^0.3.4 - version: 0.3.4(esbuild@0.19.12) + version: 0.3.4(esbuild@0.25.4) '@contentlayer/utils': specifier: ^0.3.4 version: 0.3.4 @@ -1268,7 +1280,7 @@ importers: version: 3.1.0 '@graphql-codegen/cli': specifier: 5.0.2 - version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.8)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) + version: 5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-codegen/import-types-preset': specifier: 3.0.0 version: 3.0.0(graphql@16.11.0) @@ -1382,7 +1394,7 @@ importers: version: 3.2.0 mdx-bundler: specifier: ^9.2.1 - version: 9.2.1(esbuild@0.19.12) + version: 9.2.1(esbuild@0.25.4) nanoid: specifier: ^5.0.8 version: 5.1.5 @@ -16761,6 +16773,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-hook-form@7.64.0: + resolution: {integrity: sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -21571,7 +21589,7 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-codegen/cli@5.0.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(bufferutil@4.0.9)(enquirer@2.3.6)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@babel/generator': 7.27.1 '@babel/template': 7.27.2 @@ -21586,8 +21604,8 @@ snapshots: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) - '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -21595,7 +21613,7 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.11.0 - graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + graphql-config: 5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) inquirer: 8.2.6 is-glob: 4.0.3 jiti: 1.21.6 @@ -21895,16 +21913,16 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/executor-graphql-ws@2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@graphql-tools/executor-common': 0.0.4(graphql@16.11.0) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@whatwg-node/disposablestack': 0.0.6 graphql: 16.11.0 - graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - '@fastify/websocket' - bufferutil @@ -21938,14 +21956,14 @@ snapshots: - bufferutil - utf-8-validate - '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/executor-legacy-ws@1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/ws': 8.5.8 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -22075,9 +22093,9 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/prisma-loader@8.0.17(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.6 @@ -22151,21 +22169,21 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10)': + '@graphql-tools/url-loader@8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3)': dependencies: - '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/executor-graphql-ws': 2.0.5(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/executor-http': 1.3.3(@types/node@22.7.5)(graphql@16.11.0) - '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/executor-legacy-ws': 1.1.17(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) '@graphql-tools/wrap': 10.0.35(graphql@16.11.0) '@types/ws': 8.5.8 '@whatwg-node/fetch': 0.10.6 '@whatwg-node/promise-helpers': 1.3.1 graphql: 16.11.0 - isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)) sync-fetch: 0.6.0-2 tslib: 2.8.1 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -22285,6 +22303,10 @@ snapshots: dependencies: react-hook-form: 7.58.1(react@19.1.0) + '@hookform/resolvers@3.10.0(react-hook-form@7.64.0(react@19.1.0))': + dependencies: + react-hook-form: 7.64.0(react@19.1.0) + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -23078,6 +23100,21 @@ snapshots: '@metamask/safe-event-emitter@3.1.2': {} + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3))': + dependencies: + bufferutil: 4.0.9 + cross-fetch: 4.1.0 + date-fns: 2.30.0 + debug: 4.4.0 + eciesjs: 0.4.14 + eventemitter2: 6.4.9 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + utf-8-validate: 5.0.10 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: bufferutil: 4.0.9 @@ -23097,6 +23134,33 @@ snapshots: dependencies: '@paulmillr/qr': 0.2.1 + '@metamask/sdk@0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + dependencies: + '@babel/runtime': 7.27.1 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.14)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + '@metamask/sdk-install-modal-web': 0.32.0 + '@paulmillr/qr': 0.2.1 + bowser: 2.11.0 + cross-fetch: 4.1.0 + debug: 4.4.0 + eciesjs: 0.4.14 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.2 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.27.1 @@ -27688,35 +27752,35 @@ snapshots: '@react-types/shared': 3.24.1(react@18.3.1) react: 18.3.1 - '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27744,13 +27808,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-controllers@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27782,13 +27846,13 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -27818,13 +27882,13 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-scaffold-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -27854,11 +27918,11 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -27888,11 +27952,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit-ui@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -27922,16 +27986,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27959,16 +28023,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8)': + '@reown/appkit-utils@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27996,9 +28060,9 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 '@walletconnect/logger': 2.1.2 zod: 3.23.8 @@ -28007,9 +28071,9 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 '@walletconnect/logger': 2.1.2 zod: 3.23.8 @@ -28018,20 +28082,20 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3) '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28059,20 +28123,20 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@reown/appkit@1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-controllers': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@19.1.0))(zod@3.23.8) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-scaffold-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-ui': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit-utils': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.0)(react@18.3.1))(zod@3.23.8) + '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + valtio: 1.13.2(@types/react@19.1.0)(react@18.3.1) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28190,9 +28254,9 @@ snapshots: '@rushstack/eslint-patch@1.11.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -28200,9 +28264,9 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -28210,20 +28274,20 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript @@ -30553,16 +30617,16 @@ snapshots: '@vue/shared@3.3.4': {} - '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': + '@wagmi/connectors@5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)': dependencies: '@coinbase/wallet-sdk': 4.3.0 - '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) - '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@metamask/sdk': 0.32.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) + '@walletconnect/ethereum-provider': 2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -30631,11 +30695,11 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))': + '@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.8.3) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) zustand: 5.0.0(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.29.0 @@ -30793,13 +30857,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30807,7 +30871,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30836,7 +30900,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -30850,7 +30914,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30879,13 +30943,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -30893,7 +30957,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30922,7 +30986,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/core@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -30936,7 +31000,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -30969,18 +31033,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31009,18 +31073,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/ethereum-provider@2.20.0(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@reown/appkit': 1.7.3(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/universal-provider': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31086,6 +31150,16 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 tslib: 1.14.1 + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 @@ -31141,16 +31215,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31176,16 +31250,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31211,16 +31285,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31246,16 +31320,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/sign-client@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/core': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -31341,7 +31415,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31350,9 +31424,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31380,7 +31454,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31389,9 +31463,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31419,7 +31493,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31428,9 +31502,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31458,7 +31532,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/universal-provider@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -31467,9 +31541,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/sign-client': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) '@walletconnect/types': 2.20.0 - '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@walletconnect/utils': 2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -31497,7 +31571,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31515,7 +31589,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31540,7 +31614,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31558,7 +31632,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31583,7 +31657,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.20.0(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31601,7 +31675,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -31626,7 +31700,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@walletconnect/utils@2.20.0(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -31644,7 +31718,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -33045,12 +33119,12 @@ snapshots: graceful-fs: 4.2.11 xdg-basedir: 5.1.0 - connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + connectkit@1.9.0(@babel/core@7.27.1)(@tanstack/react-query@5.29.0(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) buffer: 6.0.3 detect-browser: 5.3.0 - family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + family: 0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)) framer-motion: 6.5.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) qrcode: 1.5.4 react: 19.1.0 @@ -33059,8 +33133,8 @@ snapshots: react-use-measure: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) resize-observer-polyfill: 1.5.1 styled-components: 5.3.11(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react-is@18.1.0)(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) transitivePeerDependencies: - '@babel/core' - react-is @@ -33861,6 +33935,18 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 @@ -35134,12 +35220,12 @@ snapshots: viem: 2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8) wagmi: 2.15.2(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@18.3.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) - family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)): + family@0.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8)): optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) - wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) + wagmi: 2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) fast-check@3.16.0: dependencies: @@ -35846,13 +35932,13 @@ snapshots: - uWebSockets.js - utf-8-validate - graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10): + graphql-config@5.1.5(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3): dependencies: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.18(graphql@16.11.0) '@graphql-tools/load': 8.1.0(graphql@16.11.0) '@graphql-tools/merge': 9.0.24(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@5.0.10) + '@graphql-tools/url-loader': 8.0.31(@types/node@22.7.5)(bufferutil@4.0.9)(graphql@16.11.0)(utf-8-validate@6.0.3) '@graphql-tools/utils': 10.8.6(graphql@16.11.0) cosmiconfig: 8.1.3 graphql: 16.11.0 @@ -35890,11 +35976,11 @@ snapshots: optionalDependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): dependencies: graphql: 16.11.0 optionalDependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) graphql@15.9.0: {} @@ -36849,14 +36935,22 @@ snapshots: dependencies: ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isomorphic-ws@5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3)): dependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3) + + isows@1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + dependencies: + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)): + dependencies: + ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + isows@1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -38960,12 +39054,12 @@ snapshots: netmask@2.0.2: {} - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.19.12))(esbuild@0.19.12)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.19.12) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.19.12) - next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -38974,12 +39068,12 @@ snapshots: - markdown-wasm - supports-color - next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-contentlayer@0.3.4(contentlayer@0.3.4(esbuild@0.25.4))(esbuild@0.25.4)(next@15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@contentlayer/core': 0.3.4(esbuild@0.25.4) '@contentlayer/utils': 0.3.4 contentlayer: 0.3.4(esbuild@0.25.4) - next: 15.3.0(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) + next: 15.2.4(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.80.4) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -40653,6 +40747,10 @@ snapshots: dependencies: react: 19.1.0 + react-hook-form@7.64.0(react@19.1.0): + dependencies: + react: 19.1.0 + react-is@16.13.1: {} react-is@17.0.2: {} @@ -42719,6 +42817,17 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 + socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-client: 6.6.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 @@ -44305,69 +44414,69 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - viem@2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.23.2(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) - isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.6.2)(zod@3.23.8) - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + ox: 0.6.7(typescript@5.8.3)(zod@3.23.8) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.23.2(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.8.3)(zod@3.23.8) + ox: 0.6.7(typescript@5.6.2)(zod@3.23.8) ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.8.3 + typescript: 5.6.2 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8): dependencies: '@noble/curves': 1.8.2 '@noble/hashes': 1.7.2 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) - isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.9(typescript@5.6.2)(zod@3.23.8) - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)) + ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) + ws: 8.18.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.29.1(bufferutil@4.0.9)(typescript@5.6.2)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@noble/curves': 1.8.2 '@noble/hashes': 1.7.2 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.23.8) + abitype: 1.0.8(typescript@5.6.2)(zod@3.23.8) isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.9(typescript@5.8.3)(zod@3.23.8) + ox: 0.6.9(typescript@5.6.2)(zod@3.23.8) ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.8.3 + typescript: 5.6.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -44521,14 +44630,14 @@ snapshots: '@vue/server-renderer': 3.3.4(vue@3.3.4) '@vue/shared': 3.3.4 - wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): + wagmi@2.15.2(@tanstack/query-core@5.29.0)(@tanstack/react-query@5.29.0(react@19.1.0))(@types/react@19.1.0)(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.29.0(react@19.1.0) - '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) - '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@wagmi/connectors': 5.8.1(@types/react@19.1.0)(@wagmi/core@2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)))(bufferutil@4.0.8)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@6.0.3)(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8))(zod@3.23.8) + '@wagmi/core': 2.17.1(@tanstack/query-core@5.29.0)(@types/react@19.1.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.29.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.29.1(bufferutil@4.0.8)(typescript@5.8.3)(utf-8-validate@6.0.3)(zod@3.23.8) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -44824,6 +44933,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -44834,6 +44948,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 6.0.3 + ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -44844,6 +44963,11 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 6.0.3 + ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 From eca953789ff848df30d7ae03516243198f6f17c4 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Mon, 6 Oct 2025 19:32:03 +0100 Subject: [PATCH 12/17] feat: integrate emergency mode functionality and enhance slider configuration for vault lockup periods; update UI components for improved user experience --- .../app/_components/vaults/table-columns.tsx | 57 ++++---- apps/hub/src/app/_components/vaults/table.tsx | 13 +- .../_components/vaults/vault-lock-modal.tsx | 106 ++++++++------ apps/hub/src/app/_hooks/useCompounder.ts | 104 +++++--------- apps/hub/src/app/_hooks/useSliderConfig.ts | 135 ++++++++++++++++++ apps/hub/src/app/stake/page.tsx | 21 ++- 6 files changed, 291 insertions(+), 145 deletions(-) create mode 100644 apps/hub/src/app/_hooks/useSliderConfig.ts diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index aee6a6115..7aaa0a3d9 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -15,12 +15,14 @@ interface TableColumnsProps { vaults: VaultWithAddress[] | undefined openModalVaultId: string | null setOpenModalVaultId: (vaultId: string | null) => void + emergencyModeEnabled: unknown } export const createVaultTableColumns = ({ vaults = [], openModalVaultId, setOpenModalVaultId, + emergencyModeEnabled, }: TableColumnsProps) => { const totalStaked = vaults.reduce( (acc, vault) => acc + (vault.data?.stakedBalance || 0n), @@ -219,29 +221,31 @@ export const createVaultTableColumns = ({
    {isLocked ? (
    - - setOpenModalVaultId(open ? withdrawModalId : null) - } - onClose={() => setOpenModalVaultId(null)} - vaultAdress={row.original.address} - > - {/* @ts-expect-error - Button component is not typed */} - - + {/* @ts-expect-error - Button component is not typed */} + + + )} @@ -249,6 +253,8 @@ export const createVaultTableColumns = ({ } vaultAddress={row.original.address} title="Extend lock time" + initialYears="2" + initialDays="732" description="Extending lock time increasing Karma boost" actions={[ { @@ -290,13 +296,6 @@ export const createVaultTableColumns = ({ vaultAddress={row.original.address} title="Do you want to lock the vault?" description="Lock this vault to receive more Karma" - sliderConfig={{ - minLabel: '90 days', - maxLabel: '4 years', - minDays: 90, - maxDays: 1460, - initialPosition: 50, - }} initialYears="2" initialDays="732" actions={[ diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/table.tsx index 36e0cffa5..694f06eed 100644 --- a/apps/hub/src/app/_components/vaults/table.tsx +++ b/apps/hub/src/app/_components/vaults/table.tsx @@ -10,10 +10,12 @@ import { getCoreRowModel, useReactTable, } from '@tanstack/react-table' -import { useAccount } from 'wagmi' +import { useAccount, useReadContract } from 'wagmi' +import { STAKING_MANAGER } from '../../../config' import { useAccountVaults } from '../../_hooks/useAccountVaults' import { useVaultMutation } from '../../_hooks/useVault' +import { stakingManagerAbi } from '../../contracts' import { createVaultTableColumns } from './table-columns' import type { VaultWithAddress } from '../../_hooks/useAccountVaults' @@ -129,14 +131,21 @@ export function VaultsTable() { const { isConnected } = useAccount() const { mutate: deployVault } = useVaultMutation() + const { data: emergencyModeEnabled } = useReadContract({ + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'emergencyModeEnabled', + }) + const columns = useMemo( () => createVaultTableColumns({ vaults: vaultDataList, openModalVaultId, setOpenModalVaultId, + emergencyModeEnabled, }), - [openModalVaultId, vaultDataList] + [openModalVaultId, emergencyModeEnabled, vaultDataList] ) const table = useReactTable({ diff --git a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx index 2ea516119..ac1a37ccd 100644 --- a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx +++ b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ 'use client' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { zodResolver } from '@hookform/resolvers/zod' import * as Dialog from '@radix-ui/react-dialog' @@ -12,6 +12,7 @@ import { Button } from '@status-im/status-network/components' import { useForm } from 'react-hook-form' import { z } from 'zod' +import { useSliderConfig } from '../../_hooks/useSliderConfig' import { useVaultLock } from '../../_hooks/useVaultLock' import type { HTMLAttributes } from 'react' @@ -39,14 +40,6 @@ type Props = { title: string description: string children?: React.ReactNode - // Slider configuration - sliderConfig?: { - minLabel: string - maxLabel: string - minDays: number - maxDays: number - initialPosition?: number // 0-100 percentage - } // Initial values initialYears?: string initialDays?: string @@ -70,13 +63,6 @@ const VaultLockConfigModal = (props: Props) => { title, vaultAddress, description, - sliderConfig = { - minLabel: '90 days', - maxLabel: '4 years', - minDays: 90, - maxDays: 1460, - initialPosition: 0, - }, initialYears = '0', initialDays = '90', boost = 'x2.5', @@ -86,6 +72,8 @@ const VaultLockConfigModal = (props: Props) => { actions, } = props + const [closeAction, submitAction] = actions + const { mutate: lockVault } = useVaultLock() const { watch, setValue, handleSubmit } = useForm({ resolver: zodResolver(createFormSchema()), @@ -95,6 +83,37 @@ const VaultLockConfigModal = (props: Props) => { }, }) + const { data: sliderConfigQuery } = useSliderConfig() + + const sliderConfig = useMemo(() => { + // Convert seconds to days + const SECONDS_PER_DAY = 24 * 60 * 60 + const DAYS_PER_YEAR = 365 + + const minSeconds = sliderConfigQuery?.min || 7776000 // fallback: 90 days in seconds + const maxSeconds = sliderConfigQuery?.max || 126144000 // fallback: 4 years in seconds + + const minDays = Math.round(minSeconds / SECONDS_PER_DAY) + const maxDays = Math.round(maxSeconds / SECONDS_PER_DAY) + + // Format labels + const minYears = minDays / DAYS_PER_YEAR + const maxYears = maxDays / DAYS_PER_YEAR + + const minLabel = + minYears < 1 ? `${minDays} days` : `${minYears.toFixed(1)} years` + const maxLabel = + maxYears < 1 ? `${maxDays} days` : `${Math.round(maxYears)} years` + + return { + minLabel, + maxLabel, + minDays, + maxDays, + initialPosition: 50, + } + }, [sliderConfigQuery]) + const years = watch('years') const days = watch('days') @@ -136,7 +155,7 @@ const VaultLockConfigModal = (props: Props) => { } } - const handleVaultLockAndExtend = async (data: FormValues) => { + const handleVaultLockOrExtend = async (data: FormValues) => { const totalDays = parseInt(data.days || '0') const lockPeriodInSeconds = BigInt(totalDays * 24 * 60 * 60) @@ -236,7 +255,7 @@ const VaultLockConfigModal = (props: Props) => {
    @@ -305,21 +324,23 @@ const VaultLockConfigModal = (props: Props) => {
    -
    -
    -
    -
    - + {hasError && ( +
    +
    +
    +
    + +
    +
    +
    + {displayError}
    -
    -
    - {displayError}
    -
    + )}
    @@ -355,24 +376,27 @@ const VaultLockConfigModal = (props: Props) => {
    - {/* @ts-expect-error - Button component is not typed */} - + + {/* @ts-expect-error - Button component is not typed */} + + {/* @ts-expect-error - Button component is not typed */}
    diff --git a/apps/hub/src/app/_hooks/useCompounder.ts b/apps/hub/src/app/_hooks/useCompounder.ts index 58979acf3..8132be8d8 100644 --- a/apps/hub/src/app/_hooks/useCompounder.ts +++ b/apps/hub/src/app/_hooks/useCompounder.ts @@ -1,10 +1,12 @@ import { useMutation, type UseMutationResult } from '@tanstack/react-query' -import { useAccount, useWriteContract } from 'wagmi' +import { type Address } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' import { STAKING_MANAGER } from '../../config' +import { statusNetworkTestnet } from '../../config/chain' import { stakingManagerAbi } from '../contracts' - -import type { Address } from 'viem' +import { useAccountVaults } from './useAccountVaults' // ============================================================================ // Types @@ -18,21 +20,32 @@ export interface CompoundVaultParams { vaultAddress: Address } +/** + * Return type for the useCompounder hook + */ +export type UseCompounderReturn = UseMutationResult< + Address, + Error, + CompoundVaultParams, + unknown +> + // ============================================================================ // Constants // ============================================================================ const MUTATION_KEY_PREFIX = 'compound' as const +const CONFIRMATION_BLOCKS = 1 // ============================================================================ // Mutation Hook // ============================================================================ /** - * Mutation hook to compound multiplier points (MP) for a specific vault + * Mutation hook to compound multiplier points (MP) for a vault * * **Compounding Process:** - * Calls StakingManager.updateVault() which: + * Calls StakingManager.compound() which: * - Updates global state (reward index, MP accrual) * - Calculates new MP accrued since last update * - Updates the vault's reward index @@ -43,6 +56,7 @@ const MUTATION_KEY_PREFIX = 'compound' as const * * @throws {Error} When wallet is not connected * @throws {Error} When vault address is invalid + * @throws {Error} When transaction is reverted * * @example * Basic usage @@ -89,17 +103,14 @@ const MUTATION_KEY_PREFIX = 'compound' as const * } * ``` */ -export function useCompounder(): UseMutationResult< - Address, - Error, - CompoundVaultParams, - unknown -> { +export function useCompounder(): UseCompounderReturn { const { address } = useAccount() const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { refetch: refetchAccountVaults } = useAccountVaults() return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, 'vault', address], + mutationKey: [MUTATION_KEY_PREFIX, address], mutationFn: async ({ vaultAddress, }: CompoundVaultParams): Promise
    => { @@ -117,70 +128,27 @@ export function useCompounder(): UseMutationResult< // Execute compound transaction const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, address: STAKING_MANAGER.address, abi: stakingManagerAbi, - functionName: 'updateVault', + functionName: 'compound', args: [vaultAddress], }) - return hash - }, - }) -} - -// ============================================================================ -// Compound All Mutation Hook -// ============================================================================ - -/** - * Mutation hook to compound multiplier points (MP) for all user vaults - * - * Calls StakingManager.updateAccount() which updates all vaults owned by - * the connected account in a single transaction. - * - * @returns Mutation result with mutate function to trigger compound all - * - * @throws {Error} When wallet is not connected - * - * @example - * ```tsx - * function CompoundAllButton() { - * const { mutate: compoundAll, isPending } = useCompoundAll() - * - * return ( - * - * ) - * } - * ``` - */ -export function useCompoundAll(): UseMutationResult< - Address, - Error, - void, - unknown -> { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: CONFIRMATION_BLOCKS, + }) - return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, 'all', address], - mutationFn: async (): Promise
    => { - // Validate wallet connection - if (!address) { - throw new Error( - 'Wallet not connected. Please connect your wallet first.' - ) + // Check if transaction was reverted + if (status === 'reverted') { + throw new Error('Transaction was reverted') } - // Execute compound all transaction - const hash = await writeContractAsync({ - address: STAKING_MANAGER.address, - abi: stakingManagerAbi, - functionName: 'updateAccount', - args: [address], - }) + // Refetch account vaults to update UI + refetchAccountVaults() return hash }, diff --git a/apps/hub/src/app/_hooks/useSliderConfig.ts b/apps/hub/src/app/_hooks/useSliderConfig.ts new file mode 100644 index 000000000..75ca179aa --- /dev/null +++ b/apps/hub/src/app/_hooks/useSliderConfig.ts @@ -0,0 +1,135 @@ +import { useQuery, type UseQueryResult } from '@tanstack/react-query' +import { useConfig } from 'wagmi' +import { readContract } from 'wagmi/actions' + +import { STAKING_MANAGER } from '../../config' +import { stakingManagerAbi } from '../contracts' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Slider configuration for lockup periods + */ +export interface SliderConfig { + /** Minimum lockup period in seconds */ + min: number + /** Maximum lockup period in seconds */ + max: number +} + +/** + * Return type for the useSliderConfig hook + */ +export type UseSliderConfigReturn = UseQueryResult + +// ============================================================================ +// Constants +// ============================================================================ + +const QUERY_KEY_PREFIX = 'slider-config' as const +const STALE_TIME = 5 * 60 * 1000 // 5 minutes +const CACHE_TIME = 10 * 60 * 1000 // 10 minutes + +// ============================================================================ +// Query Hook +// ============================================================================ + +/** + * Query hook to fetch slider configuration for vault lockup periods + * + * Retrieves MIN_LOCKUP_PERIOD and MAX_LOCKUP_PERIOD from the staking manager + * contract to configure the lockup period slider UI component. + * + * @returns Query result with slider configuration data + * + * @throws {Error} When contract read fails + * @throws {Error} When contract returns invalid values + * + * @example + * Basic usage + * ```tsx + * function LockupSlider() { + * const { data: config, isLoading } = useSliderConfig() + * + * if (isLoading) return
    Loading...
    + * if (!config) return
    Failed to load config
    + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With error handling + * ```tsx + * function LockupConfig() { + * const { data: config, isLoading, isError, error } = useSliderConfig() + * + * if (isLoading) return + * if (isError) return + * + * return ( + *
    + *

    Min: {config.min} seconds

    + *

    Max: {config.max} seconds

    + *
    + * ) + * } + * ``` + */ +export function useSliderConfig(): UseSliderConfigReturn { + const config = useConfig() + + return useQuery({ + queryKey: [QUERY_KEY_PREFIX], + queryFn: async (): Promise => { + try { + // Fetch minimum lockup period + const minLockupPeriod = await readContract(config, { + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'MIN_LOCKUP_PERIOD', + }) + + // Fetch maximum lockup period + const maxLockupPeriod = await readContract(config, { + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'MAX_LOCKUP_PERIOD', + }) + + // Validate results + if ( + typeof minLockupPeriod !== 'bigint' || + typeof maxLockupPeriod !== 'bigint' + ) { + throw new Error('Invalid lockup period values returned from contract') + } + + // Ensure min is less than max + if (minLockupPeriod >= maxLockupPeriod) { + throw new Error( + 'Minimum lockup period must be less than maximum lockup period' + ) + } + + return { + min: Number(minLockupPeriod), + max: Number(maxLockupPeriod), + } + } catch (error) { + throw new Error( + `Failed to fetch slider configuration: ${error instanceof Error ? error.message : 'Unknown error'}` + ) + } + }, + staleTime: STALE_TIME, + gcTime: CACHE_TIME, + }) +} diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 44d9d08da..773661e7f 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -93,6 +93,13 @@ export default function StakePage() { functionName: 'totalStaked', }) as { data: bigint } + const { data: mpBalanceOfAccount } = useReadContract({ + address: STAKING_MANAGER.address, + abi: stakingManagerAbi, + functionName: 'mpBalanceOfAccount', + args: [address], + }) as { data: bigint } + const { mutate: deployVault } = useVaultMutation() const { mutate: approveTokens } = useTokenApproval() const { mutate: stakeVault } = useVaultTokenStake() @@ -424,13 +431,17 @@ export default function StakePage() {
    -

    - {weightedBoost.hasStake - ? `${weightedBoost.totalStaked.toFixed(2)} ${TOKEN_TICKER} staked` + + {mpBalanceOfAccount > 0n + ? `${formatSNT(mpBalanceOfAccount)} points are ready to compound` : 'No points are ready to compound'} -

    + {/* @ts-expect-error - TODO: fix this */} -
    From cab94d00b87b66ebb5cc8011ff4818583cc00f81 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Mon, 6 Oct 2025 19:52:44 +0100 Subject: [PATCH 13/17] feat: enhance vault selection UI with lock status indicators and improve table overflow handling for better user experience --- apps/hub/src/app/_components/vault-select.tsx | 61 +++++++++++++------ apps/hub/src/app/_components/vaults/table.tsx | 16 ++--- apps/hub/src/app/stake/page.tsx | 20 +++--- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/apps/hub/src/app/_components/vault-select.tsx b/apps/hub/src/app/_components/vault-select.tsx index 0a7ee93f5..2b55da879 100644 --- a/apps/hub/src/app/_components/vault-select.tsx +++ b/apps/hub/src/app/_components/vault-select.tsx @@ -4,7 +4,12 @@ import { useState } from 'react' import { DropdownMenu } from '@status-im/components' -import { AddSmallIcon, DropdownIcon, UnlockedIcon } from '@status-im/icons/20' +import { + AddSmallIcon, + DropdownIcon, + LockedIcon, + UnlockedIcon, +} from '@status-im/icons/20' import { formatSNT } from '../../utils/currency' @@ -95,6 +100,10 @@ export function VaultSelect({ const selectedVault = vaults.find(vault => vault.address === value) const selectedIndex = vaults.findIndex(vault => vault.address === value) + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(selectedVault?.data?.lockUntil) + const isLocked = lockUntilTimestamp > now + const displayLabel = selectedVault && selectedIndex !== -1 ? getVaultLabel(selectedVault, selectedIndex) @@ -107,15 +116,21 @@ export function VaultSelect({
    -
    -
    - - - - -
    +
    +
    +
    + + + + +
    +
    diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index 773661e7f..a6adfba15 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -460,19 +460,23 @@ const InfoTooltip = () => ( -

    +

    + The longer SNT is staked or locked in vaults, the higher this multiplier goes. This rewards long term believers. The maximum multiplier is x9. -

    - - {/* TODO: change link */} - +
    + + Learn more - +
    } From 7b284205d0489087d7e3c62fec8e6d4c4db5e100 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Tue, 7 Oct 2025 18:00:21 +0100 Subject: [PATCH 14/17] refactor: simplify vault table action handlers by removing unnecessary onClick logic for modal state management --- apps/hub/src/app/_components/vaults/table-columns.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index 7aaa0a3d9..e85c8e89c 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -259,14 +259,9 @@ export const createVaultTableColumns = ({ actions={[ { label: 'Cancel', - onClick: () => setOpenModalVaultId(null), }, { label: 'Extend lock', - onClick: () => { - // Handle extend lock logic - setOpenModalVaultId(null) - }, }, ]} onClose={() => setOpenModalVaultId(null)} @@ -301,14 +296,9 @@ export const createVaultTableColumns = ({ actions={[ { label: "Don't lock", - onClick: () => setOpenModalVaultId(null), }, { label: 'Lock', - onClick: () => { - // Handle lock logic - setOpenModalVaultId(null) - }, }, ]} onClose={() => setOpenModalVaultId(null)} From 2c2f7fa15789b3ec86c930e4073fc3d4b7e8e5ec Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Thu, 9 Oct 2025 17:37:05 +0100 Subject: [PATCH 15/17] feat: enhance vault management by adding new hooks for staking, multiplier points, and vault locking; update UI components and improve state management for better user experience --- .../stake/action-status-dialog.tsx | 39 +- .../stake/compound-status-content.tsx | 94 ++++ .../{ => hooks}/use-action-status-content.tsx | 57 ++- .../_components/stake/types/action-status.ts | 19 + apps/hub/src/app/_components/vault-select.tsx | 19 +- .../vaults/modals/base-vault-modal.tsx | 73 ++++ .../modals/lock-vault-modal/constants.ts | 33 ++ .../vaults/modals/lock-vault-modal/index.tsx | 120 +++++ .../lock-vault-modal/lock-duration-slider.tsx | 46 ++ .../lock-vault-modal/lock-vault-form.tsx | 360 +++++++++++++++ .../vaults/modals/withdraw-vault-modal.tsx | 137 ++++++ .../app/_components/vaults/table-columns.tsx | 81 ++-- apps/hub/src/app/_components/vaults/types.ts | 16 - .../_components/vaults/vault-lock-modal.tsx | 412 ------------------ .../vaults/{table.tsx => vaults-table.tsx} | 44 +- .../app/_components/vaults/withdraw-modal.tsx | 170 -------- apps/hub/src/app/_constants/address.ts | 31 ++ .../src/{config => app/_constants}/chain.ts | 0 .../app/{ => _constants}/contracts/faucet.ts | 4 +- .../app/{ => _constants}/contracts/index.ts | 0 .../contracts/stakingManagerAbi.ts | 249 ++++++++++- .../{ => _constants}/contracts/tokenAbi.ts | 0 .../{ => _constants}/contracts/vaultAbi.ts | 233 +++++----- .../_constants/contracts/vaultFactoryAbi.ts | 150 +++++++ .../src/{config => app/_constants}/index.ts | 1 + apps/hub/src/app/_hooks/useApproveToken.ts | 175 ++++++++ .../app/_hooks/useCompoundMultiplierPoints.ts | 167 +++++++ apps/hub/src/app/_hooks/useCompounder.ts | 156 ------- apps/hub/src/app/_hooks/useCreateVault.ts | 186 ++++++++ apps/hub/src/app/_hooks/useExchangeRate.ts | 3 +- apps/hub/src/app/_hooks/useFaucet.ts | 76 ++-- apps/hub/src/app/_hooks/useLockVault.ts | 209 +++++++++ .../hub/src/app/_hooks/useMultiplierPoints.ts | 217 +++++++++ apps/hub/src/app/_hooks/useSIWE.ts | 2 +- apps/hub/src/app/_hooks/useSliderConfig.ts | 7 +- ...seAccountVaults.ts => useStakingVaults.ts} | 165 ++++--- apps/hub/src/app/_hooks/useTokenApproval.ts | 133 ------ apps/hub/src/app/_hooks/useVault.ts | 174 -------- apps/hub/src/app/_hooks/useVaultLock.ts | 166 ------- ...e-context.tsx => useVaultStateContext.tsx} | 4 +- .../src/app/_hooks/useVaultStateMachine.ts | 93 ++-- apps/hub/src/app/_hooks/useVaultTokenStake.ts | 23 +- apps/hub/src/app/_hooks/useVaultWithdraw.ts | 100 ++--- apps/hub/src/app/_hooks/useWeightedBoost.ts | 15 +- apps/hub/src/app/contracts/vaultFactoryAbi.ts | 11 - apps/hub/src/app/providers.tsx | 14 +- apps/hub/src/app/stake/page.tsx | 148 ++++--- apps/hub/src/config/address.ts | 20 - apps/hub/src/utils/vault.ts | 94 ++-- apps/hub/tsconfig.json | 3 + packages/components/src/toast/toast.tsx | 8 +- 51 files changed, 2933 insertions(+), 1824 deletions(-) create mode 100644 apps/hub/src/app/_components/stake/compound-status-content.tsx rename apps/hub/src/app/_components/stake/{ => hooks}/use-action-status-content.tsx (80%) create mode 100644 apps/hub/src/app/_components/stake/types/action-status.ts create mode 100644 apps/hub/src/app/_components/vaults/modals/base-vault-modal.tsx create mode 100644 apps/hub/src/app/_components/vaults/modals/lock-vault-modal/constants.ts create mode 100644 apps/hub/src/app/_components/vaults/modals/lock-vault-modal/index.tsx create mode 100644 apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-duration-slider.tsx create mode 100644 apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-vault-form.tsx create mode 100644 apps/hub/src/app/_components/vaults/modals/withdraw-vault-modal.tsx delete mode 100644 apps/hub/src/app/_components/vaults/types.ts delete mode 100644 apps/hub/src/app/_components/vaults/vault-lock-modal.tsx rename apps/hub/src/app/_components/vaults/{table.tsx => vaults-table.tsx} (83%) delete mode 100644 apps/hub/src/app/_components/vaults/withdraw-modal.tsx create mode 100644 apps/hub/src/app/_constants/address.ts rename apps/hub/src/{config => app/_constants}/chain.ts (100%) rename apps/hub/src/app/{ => _constants}/contracts/faucet.ts (95%) rename apps/hub/src/app/{ => _constants}/contracts/index.ts (100%) rename apps/hub/src/app/{ => _constants}/contracts/stakingManagerAbi.ts (76%) rename apps/hub/src/app/{ => _constants}/contracts/tokenAbi.ts (100%) rename apps/hub/src/app/{ => _constants}/contracts/vaultAbi.ts (50%) create mode 100644 apps/hub/src/app/_constants/contracts/vaultFactoryAbi.ts rename apps/hub/src/{config => app/_constants}/index.ts (52%) create mode 100644 apps/hub/src/app/_hooks/useApproveToken.ts create mode 100644 apps/hub/src/app/_hooks/useCompoundMultiplierPoints.ts delete mode 100644 apps/hub/src/app/_hooks/useCompounder.ts create mode 100644 apps/hub/src/app/_hooks/useCreateVault.ts create mode 100644 apps/hub/src/app/_hooks/useLockVault.ts create mode 100644 apps/hub/src/app/_hooks/useMultiplierPoints.ts rename apps/hub/src/app/_hooks/{useAccountVaults.ts => useStakingVaults.ts} (56%) delete mode 100644 apps/hub/src/app/_hooks/useTokenApproval.ts delete mode 100644 apps/hub/src/app/_hooks/useVault.ts delete mode 100644 apps/hub/src/app/_hooks/useVaultLock.ts rename apps/hub/src/app/_hooks/{vault-state-context.tsx => useVaultStateContext.tsx} (84%) delete mode 100644 apps/hub/src/app/contracts/vaultFactoryAbi.ts delete mode 100644 apps/hub/src/config/address.ts diff --git a/apps/hub/src/app/_components/stake/action-status-dialog.tsx b/apps/hub/src/app/_components/stake/action-status-dialog.tsx index 767e7666f..647e2e5ea 100644 --- a/apps/hub/src/app/_components/stake/action-status-dialog.tsx +++ b/apps/hub/src/app/_components/stake/action-status-dialog.tsx @@ -4,17 +4,17 @@ import { CloseIcon } from '@status-im/icons/20' import { match } from 'ts-pattern' import { ProcessingIcon, RejectIcon, VaultIcon } from '../icons' - -import type { ActionStatusState } from './use-action-status-content' +import { type ActionStatusState } from './types/action-status' type Props = { open: boolean onClose: () => void - title: string - description: string + title?: string + description?: string children?: React.ReactNode state?: ActionStatusState showCloseButton?: boolean + content?: React.ReactNode } const ActionStatusDialog = (props: Props) => { @@ -26,6 +26,7 @@ const ActionStatusDialog = (props: Props) => { children, state = 'pending', showCloseButton = true, + content, } = props const handleOpenChange = (nextOpen: boolean) => { @@ -62,17 +63,25 @@ const ActionStatusDialog = (props: Props) => { )} -
    - {mapIconToState(state)} - -

    - {title} -

    -
    - - {description} - -
    + {content ? ( +
    {content}
    + ) : ( +
    + {mapIconToState(state)} + +

    + {title} +

    +
    + {description && ( + + + {description} + + + )} +
    + )}
    diff --git a/apps/hub/src/app/_components/stake/compound-status-content.tsx b/apps/hub/src/app/_components/stake/compound-status-content.tsx new file mode 100644 index 000000000..7426020bf --- /dev/null +++ b/apps/hub/src/app/_components/stake/compound-status-content.tsx @@ -0,0 +1,94 @@ +/* eslint-disable import/no-unresolved */ +import * as Dialog from '@radix-ui/react-dialog' +import { InfoIcon } from '@status-im/icons/16' +import { formatUnits } from 'viem' + +import { LaunchIcon } from '~components/icons' +import { useMultiplierPointsBalance } from '~hooks/useMultiplierPoints' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useWeightedBoost } from '~hooks/useWeightedBoost' +import { formatSNT } from '~utils/currency' + +import { SNT_TOKEN } from '../../_constants' + +export const CompoundStatusContent = () => { + const { data: vaults } = useStakingVaults() + const weightedBoost = useWeightedBoost(vaults) + const { data: multiplierPointsData } = useMultiplierPointsBalance() + + const earnRateWithBoost = Math.floor(weightedBoost.totalStaked * 0.05) + const earnRateWithoutBoost = Math.floor(weightedBoost.totalStaked * 0.05) + + return ( + <> +
    + +
    + +
    +
    + +

    + {`Ready to compound ${formatSNT( + formatUnits( + multiplierPointsData?.totalUncompounded ?? 0n, + SNT_TOKEN.decimals + ) + )} points`} +

    +
    + +

    + Please sign the message in your wallet. +

    +
    +
    + +
    +
    +

    + Total compounded +

    +

    + {`${formatSNT( + formatUnits( + multiplierPointsData?.totalMpRedeemed ?? 0n, + SNT_TOKEN.decimals + ) + )} points`} +

    +
    + +
    +

    + Your earn rate at {weightedBoost.formatted} boost +

    +

    + {earnRateWithBoost} Karma / day +

    +
    + +
    +

    + Equivalent at x0.00 boost +

    +

    + {earnRateWithoutBoost} Karma / day +

    +
    +
    + +
    +
    + +
    +

    + Boost the rate at which you receive Karma. More points you compound, + the higher your rate. The longer you lock your vault, the higher + your boost, and the faster you accumulate Karma. +

    +
    +
    + + ) +} diff --git a/apps/hub/src/app/_components/stake/use-action-status-content.tsx b/apps/hub/src/app/_components/stake/hooks/use-action-status-content.tsx similarity index 80% rename from apps/hub/src/app/_components/stake/use-action-status-content.tsx rename to apps/hub/src/app/_components/stake/hooks/use-action-status-content.tsx index 7dfda052e..1b236d7d4 100644 --- a/apps/hub/src/app/_components/stake/use-action-status-content.tsx +++ b/apps/hub/src/app/_components/stake/hooks/use-action-status-content.tsx @@ -1,26 +1,18 @@ import { match } from 'ts-pattern' -import { formatSNT } from '../../../utils/currency' +import { type VaultState } from '~hooks/useVaultStateMachine' +import { formatSNT } from '~utils/currency' -import type { VaultState } from '../../_hooks/useVaultStateMachine' - -export type ActionStatusState = - | 'pending' // Waiting for user action (sign, approve) - | 'processing' // Transaction in progress - | 'error' // Failed/rejected - | 'success' // Completed successfully - -export type ActionStatusContent = { - title: string - description: string - state: ActionStatusState - showCloseButton: boolean -} +import { CompoundStatusContent } from '../compound-status-content' +import { type ActionStatusContent } from '../types/action-status' +/** + * Hook to generate action status dialog content based on vault state + * Maps vault state machine states to user-facing dialog content + */ export function useActionStatusContent( state: VaultState ): ActionStatusContent | null { - console.log(state) return ( match(state) // SIWE flow @@ -110,19 +102,7 @@ export function useActionStatusContent( showCloseButton: true, })) - // Withdraw flow - .with( - { - type: 'withdraw', - step: 'initialize', - }, - state => ({ - title: `Ready to withdraw ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, - description: 'Please sign the message in your wallet.', - state: 'pending', - showCloseButton: true, - }) - ) + // Withdraw flow (goes directly to processing, no initialize step) .with({ type: 'withdraw', step: 'processing' }, state => ({ title: `Withdrawing ${formatSNT(state.amount ?? 0, { includeSymbol: true })}`, description: 'Wait a moment...', @@ -156,6 +136,25 @@ export function useActionStatusContent( showCloseButton: true, })) + // compound flow + .with({ type: 'compound', step: 'initialize' }, state => ({ + state: 'pending', + showCloseButton: true, + content: , + })) + .with({ type: 'compound', step: 'processing' }, state => ({ + title: `Compounding ${formatSNT(state.amount ?? 0)} points`, + description: 'Wait a moment...', + state: 'processing', + showCloseButton: false, + })) + .with({ type: 'compound', step: 'rejected' }, () => ({ + title: 'Request was rejected', + description: 'Request was rejected by user', + state: 'error', + showCloseButton: true, + })) + // Success .with({ type: 'success' }, () => ({ title: 'Success!', diff --git a/apps/hub/src/app/_components/stake/types/action-status.ts b/apps/hub/src/app/_components/stake/types/action-status.ts new file mode 100644 index 000000000..3605399fa --- /dev/null +++ b/apps/hub/src/app/_components/stake/types/action-status.ts @@ -0,0 +1,19 @@ +/** + * Represents the current state of an action status dialog + */ +export type ActionStatusState = + | 'pending' // Waiting for user action (sign, approve) + | 'processing' // Transaction in progress + | 'error' // Failed/rejected + | 'success' // Completed successfully + +/** + * Content configuration for action status dialog + */ +export interface ActionStatusContent { + state: ActionStatusState + title?: string + description?: string + showCloseButton?: boolean + content?: React.ReactNode +} diff --git a/apps/hub/src/app/_components/vault-select.tsx b/apps/hub/src/app/_components/vault-select.tsx index 2b55da879..e80cc925a 100644 --- a/apps/hub/src/app/_components/vault-select.tsx +++ b/apps/hub/src/app/_components/vault-select.tsx @@ -11,9 +11,10 @@ import { UnlockedIcon, } from '@status-im/icons/20' -import { formatSNT } from '../../utils/currency' +import { formatSNT } from '~utils/currency' +import { isVaultLocked } from '~utils/vault' -import type { VaultWithAddress } from '../_hooks/useAccountVaults' +import type { StakingVault } from '~hooks/useStakingVaults' // ============================================================================ // Types @@ -23,7 +24,7 @@ interface VaultSelectProps { /** * List of available vaults to select from */ - vaults: VaultWithAddress[] + vaults: StakingVault[] /** * Currently selected vault address */ @@ -51,7 +52,7 @@ interface VaultSelectProps { /** * Gets display label for a vault option */ -function getVaultLabel(vault: VaultWithAddress, index: number): string { +function getVaultLabel(vault: StakingVault, index: number): string { const stakedAmount = vault.data?.stakedBalance ? formatSNT(vault.data.stakedBalance) : '0' @@ -100,9 +101,7 @@ export function VaultSelect({ const selectedVault = vaults.find(vault => vault.address === value) const selectedIndex = vaults.findIndex(vault => vault.address === value) - const now = Math.floor(Date.now() / 1000) - const lockUntilTimestamp = Number(selectedVault?.data?.lockUntil) - const isLocked = lockUntilTimestamp > now + const isLocked = isVaultLocked(selectedVault?.data?.lockUntil) const displayLabel = selectedVault && selectedIndex !== -1 @@ -155,15 +154,13 @@ export function VaultSelect({ {hasVaults ? ( <> {vaults.map((vault, index) => { - const now = Math.floor(Date.now() / 1000) - const lockUntilTimestamp = Number(vault.data?.lockUntil) - const isVaultLocked = lockUntilTimestamp > now + const isLocked = isVaultLocked(vault.data?.lockUntil) return ( ) : ( diff --git a/apps/hub/src/app/_components/vaults/modals/base-vault-modal.tsx b/apps/hub/src/app/_components/vaults/modals/base-vault-modal.tsx new file mode 100644 index 000000000..adb10f176 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/base-vault-modal.tsx @@ -0,0 +1,73 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import * as Dialog from '@radix-ui/react-dialog' +import { CloseIcon } from '@status-im/icons/20' + +import type { ReactNode } from 'react' + +interface BaseVaultModalProps { + open?: boolean + onOpenChange?: (open: boolean) => void + onClose: () => void + title: string + description: string + children?: ReactNode + trigger?: ReactNode +} + +/** + * Base modal component for vault-related actions. + * Provides consistent dialog wrapper with close button, overlay, and styling. + */ +export function BaseVaultModal(props: BaseVaultModalProps) { + const { open, onOpenChange, onClose, title, description, children, trigger } = + props + + const handleOpenChange = (nextOpen: boolean) => { + if (onOpenChange) { + onOpenChange(nextOpen) + } + if (!nextOpen) { + onClose() + } + } + + return ( + + {trigger && {trigger}} + + + +
    + + + + +
    + +
    + + {title} + +
    +
    + +
    + {description} +
    +
    +
    + + {children} +
    +
    +
    +
    + ) +} diff --git a/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/constants.ts b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/constants.ts new file mode 100644 index 000000000..7e93676e1 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/constants.ts @@ -0,0 +1,33 @@ +/** + * Time conversion constants for vault locking calculations + */ +export const TIME_CONSTANTS = { + SECONDS_PER_MINUTE: 60, + MINUTES_PER_HOUR: 60, + HOURS_PER_DAY: 24, + DAYS_PER_YEAR: 365, +} as const + +/** + * Calculated time conversion values + */ +export const SECONDS_PER_DAY = + TIME_CONSTANTS.SECONDS_PER_MINUTE * + TIME_CONSTANTS.MINUTES_PER_HOUR * + TIME_CONSTANTS.HOURS_PER_DAY + +export const MILLISECONDS_PER_DAY = SECONDS_PER_DAY * 1000 + +/** + * Date formatting constants + */ +export const DATE_FORMAT = { + PAD_LENGTH: 2, + PAD_CHAR: '0', + SEPARATOR: '/', +} as const + +export const DEFAULT_LOCK_PERIOD = { + INITIAL_YEARS: '0.25', + INITIAL_DAYS: '90', +} as const diff --git a/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/index.tsx b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/index.tsx new file mode 100644 index 000000000..ccd635b4d --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/index.tsx @@ -0,0 +1,120 @@ +'use client' + +import { useReadContract } from 'wagmi' + +import { vaultAbi } from '~constants/contracts' +import { useLockVault } from '~hooks/useLockVault' + +import { BaseVaultModal } from '../base-vault-modal' +import { DEFAULT_LOCK_PERIOD } from './constants' +import { LockVaultForm } from './lock-vault-form' + +import type { HTMLAttributes } from 'react' +import type { Address } from 'viem' + +type ActionButton = HTMLAttributes & { + label: string + disabled?: boolean +} + +interface LockVaultModalProps { + open?: boolean + onOpenChange?: (open: boolean) => void + onClose: () => void + vaultAddress: Address + title: string + description: string + children?: React.ReactNode + initialYears?: string + initialDays?: string + infoMessage?: string + errorMessage?: string | null + onValidate?: (years: string, days: string) => string | null + actions: [ActionButton, ActionButton] +} + +/** + * Modal for locking vault to earn multiplier boost + */ +export function LockVaultModal(props: LockVaultModalProps) { + const { + open, + onOpenChange, + onClose, + children, + title, + vaultAddress, + description, + infoMessage, + errorMessage, + onValidate, + actions, + } = props + + const { mutate: lockVault } = useLockVault(vaultAddress) + + const { data: lockUntil } = useReadContract({ + abi: vaultAbi, + address: vaultAddress, + functionName: 'lockUntil', + }) as { data: bigint } + + // Calculate initial values based on current lockUntil for extensions + // Only consider it "extending" if the vault is currently locked (lockUntil > now) + const now = BigInt(Math.floor(Date.now() / 1000)) + const isExtending = lockUntil && lockUntil > now + const calculatedInitialDays = isExtending + ? Math.ceil((Number(lockUntil) - Math.floor(Date.now() / 1000)) / 86400) + : undefined + const calculatedInitialYears = calculatedInitialDays + ? (calculatedInitialDays / 365).toFixed(2) + : undefined + + // Use calculated values for extensions, props for new locks + const finalInitialDays = isExtending + ? String(calculatedInitialDays) + : DEFAULT_LOCK_PERIOD.INITIAL_DAYS + const finalInitialYears = isExtending + ? calculatedInitialYears + : DEFAULT_LOCK_PERIOD.INITIAL_YEARS + + /** + * Handle the submit of the lock vault form + * + * @param lockPeriodInSeconds - The increased lock duration in seconds + * The smart contract handles all calculations internally via _calculateLock + */ + const handleSubmit = (lockPeriodInSeconds: bigint) => { + // Always pass the duration in seconds + // The smart contract's _calculateLock function handles: + // - Calculating delta MP + // - Calculating new lock end time (max(currentLockEnd, now) + increasedLockSeconds) + // - Validation (min/max periods, MP overflow checks) + lockVault({ + lockPeriodInSeconds, + }) + } + + return ( + + + + ) +} diff --git a/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-duration-slider.tsx b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-duration-slider.tsx new file mode 100644 index 000000000..c1c2ccf34 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-duration-slider.tsx @@ -0,0 +1,46 @@ +'use client' + +interface SliderConfig { + minLabel: string + maxLabel: string + minDays: number + maxDays: number +} + +interface LockDurationSliderProps { + sliderConfig: SliderConfig + value: number + onChange: (value: number) => void +} + +/** + * Slider component for selecting vault lock duration + */ +export function LockDurationSlider(props: LockDurationSliderProps) { + const { sliderConfig, value, onChange } = props + + const handleSliderChange = (e: React.ChangeEvent) => { + const inputDays = parseFloat(e.target.value) + onChange(inputDays) + } + + return ( +
    +
    + +
    +
    + {sliderConfig.minLabel} + {sliderConfig.maxLabel} +
    +
    + ) +} diff --git a/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-vault-form.tsx b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-vault-form.tsx new file mode 100644 index 000000000..1fc39d6f5 --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/lock-vault-modal/lock-vault-form.tsx @@ -0,0 +1,360 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import { useEffect, useMemo, useState } from 'react' + +import { zodResolver } from '@hookform/resolvers/zod' +import { ContextTag, Input } from '@status-im/components' +import { InfoIcon } from '@status-im/icons/16' +import { IncorrectIcon } from '@status-im/icons/20' +import { Button } from '@status-im/status-network/components' +import { useForm } from 'react-hook-form' +import { z } from 'zod' + +import { useSliderConfig } from '~hooks/useSliderConfig' + +import { DATE_FORMAT, DEFAULT_LOCK_PERIOD, SECONDS_PER_DAY } from './constants' +import { LockDurationSlider } from './lock-duration-slider' + +import type { HTMLAttributes } from 'react' + +const DAYS_PER_YEAR = 365 + +type ActionButton = HTMLAttributes & { + label: string + disabled?: boolean +} + +const createFormSchema = () => { + return z.object({ + years: z.string(), + days: z.string(), + }) +} + +type FormValues = z.infer> + +interface LockVaultFormProps { + onSubmit: (lockPeriodInSeconds: bigint) => void + onClose: () => void + actions: [ActionButton, ActionButton] + initialYears?: string + initialDays?: string + infoMessage?: string + errorMessage?: string | null + onValidate?: (years: string, days: string) => string | null + /** Current lockUntil timestamp (for vault extensions) */ + currentLockUntil?: bigint +} + +/** + * Form component for vault lock configuration + */ +export function LockVaultForm(props: LockVaultFormProps) { + const { + initialYears, + initialDays, + infoMessage = 'Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks.', + errorMessage: externalErrorMessage, + onValidate, + onSubmit, + onClose, + actions, + currentLockUntil, + } = props + + const [closeAction, submitAction] = actions + + const { watch, setValue, handleSubmit } = useForm({ + resolver: zodResolver(createFormSchema()), + defaultValues: { + years: initialYears || DEFAULT_LOCK_PERIOD.INITIAL_YEARS, + days: initialDays || DEFAULT_LOCK_PERIOD.INITIAL_DAYS, + }, + }) + + const { data: sliderConfigQuery } = useSliderConfig() + + const sliderConfig = useMemo(() => { + const minSeconds = sliderConfigQuery?.min || 7776000 // fallback: 90 days in seconds + const maxSeconds = sliderConfigQuery?.max || 126144000 // fallback: 4 years in seconds + + const minDays = Math.round(minSeconds / SECONDS_PER_DAY) + const maxDays = Math.round(maxSeconds / SECONDS_PER_DAY) + + const minYears = minDays / DAYS_PER_YEAR + const maxYears = maxDays / DAYS_PER_YEAR + + const minLabel = + minYears < 1 ? `${minDays} days` : `${minYears.toFixed(1)} years` + const maxLabel = + maxYears < 1 ? `${maxDays} days` : `${Math.round(maxYears)} years` + + return { + minLabel, + maxLabel, + minDays, + maxDays, + initialPosition: 50, + } + }, [sliderConfigQuery]) + + const years = watch('years') + const days = watch('days') + + const initialSliderValue = + parseInt(initialYears || DEFAULT_LOCK_PERIOD.INITIAL_YEARS) * + DAYS_PER_YEAR + + parseInt(initialDays || DEFAULT_LOCK_PERIOD.INITIAL_DAYS) + + const [sliderValue, setSliderValue] = useState(initialSliderValue) + + const calculateUnlockDate = (totalDays: number): string => { + const totalSeconds = totalDays * SECONDS_PER_DAY + // Always calculate from today for consistent UX + const unlockTimestamp = Math.floor(Date.now() / 1000) + totalSeconds + + // Convert to Date for display (multiply by 1000 for milliseconds) + const unlockDate = new Date(unlockTimestamp * 1000) + + const day = String(unlockDate.getDate()).padStart( + DATE_FORMAT.PAD_LENGTH, + DATE_FORMAT.PAD_CHAR + ) + const month = String(unlockDate.getMonth() + 1).padStart( + DATE_FORMAT.PAD_LENGTH, + DATE_FORMAT.PAD_CHAR + ) + const year = unlockDate.getFullYear() + + return `${day}${DATE_FORMAT.SEPARATOR}${month}${DATE_FORMAT.SEPARATOR}${year}` + } + + const calculatedUnlockDate = useMemo(() => { + const daysValue = parseInt( + days || initialDays || DEFAULT_LOCK_PERIOD.INITIAL_DAYS + ) + return calculateUnlockDate(daysValue) + }, [days, initialDays]) + + useEffect(() => { + const daysValue = parseInt(days || DEFAULT_LOCK_PERIOD.INITIAL_DAYS) + if (!isNaN(daysValue) && daysValue !== sliderValue) { + setSliderValue(daysValue) + } + }, [days, sliderValue]) + + const handleVaultLockOrExtend = async (data: FormValues) => { + const totalDays = parseInt(data.days || DEFAULT_LOCK_PERIOD.INITIAL_DAYS) + const userDesiredTotalLockSeconds = BigInt(totalDays * SECONDS_PER_DAY) + const isExtending = currentLockUntil && currentLockUntil > 0n + + let increasedLockSeconds: bigint + + if (isExtending) { + // When extending: calculate how much additional time to add + // to reach the user's desired total lock time from now + const now = BigInt(Math.floor(Date.now() / 1000)) + const currentRemainingSeconds = + currentLockUntil > now ? currentLockUntil - now : 0n + + // If user wants total lock of X seconds from now, and vault currently locked for Y seconds from now, + // we need to add (X - Y) seconds + increasedLockSeconds = + userDesiredTotalLockSeconds - currentRemainingSeconds + + // Ensure we're adding at least some time (can't decrease lock) + if (increasedLockSeconds < 0n) { + increasedLockSeconds = 0n + } + } else { + // New lock: just use the duration as-is + increasedLockSeconds = userDesiredTotalLockSeconds + } + + onClose() + onSubmit(increasedLockSeconds) + } + + const handleSliderChange = (inputDays: number) => { + setSliderValue(inputDays) + + const yearsValue = (inputDays / DAYS_PER_YEAR).toFixed(2) + + setValue('years', yearsValue) + setValue('days', Math.round(inputDays).toString()) + } + + const handleYearsChange = (value: string) => { + setValue('years', value) + + const yearsValue = parseFloat(value || '0') + if (!isNaN(yearsValue)) { + const totalDays = Math.round(yearsValue * DAYS_PER_YEAR) + const clampedDays = Math.max( + sliderConfig.minDays, + Math.min(totalDays, sliderConfig.maxDays) + ) + + setSliderValue(clampedDays) + setValue('days', clampedDays.toString()) + } + } + + const handleDaysChange = (value: string) => { + setValue('days', value) + + const inputDays = parseInt(value || DEFAULT_LOCK_PERIOD.INITIAL_DAYS) + if (!isNaN(inputDays)) { + const clampedDays = Math.max( + sliderConfig.minDays, + Math.min(inputDays, sliderConfig.maxDays) + ) + + setSliderValue(clampedDays) + setValue('years', (clampedDays / DAYS_PER_YEAR).toFixed(2)) + } + } + + const getValidationError = (): string | null => { + const yearsValue = parseFloat(years || DEFAULT_LOCK_PERIOD.INITIAL_YEARS) + const daysValue = parseInt(days || DEFAULT_LOCK_PERIOD.INITIAL_DAYS) + + if (daysValue > 0 && daysValue < sliderConfig.minDays) { + return `Minimum lock time is ${sliderConfig.minLabel}` + } + + if (daysValue > sliderConfig.maxDays) { + return `Maximum lock time is ${sliderConfig.maxLabel}` + } + + const minYears = sliderConfig.minDays / DAYS_PER_YEAR + if (yearsValue > 0 && yearsValue < minYears) { + return `Minimum lock time is ${sliderConfig.minLabel}` + } + + const maxYears = sliderConfig.maxDays / DAYS_PER_YEAR + if (yearsValue > maxYears) { + return `Maximum lock time is ${sliderConfig.maxLabel}` + } + + return null + } + + const builtInError = getValidationError() + const customError = onValidate ? onValidate(years, days) : null + const errorMessage = customError || builtInError + const displayError = externalErrorMessage || errorMessage + const hasError = Boolean(displayError) + + return ( + + + +
    +
    + +
    + + or + +
    + +
    +
    + + {hasError && ( +
    +
    +
    +
    + +
    +
    +
    + {displayError} +
    +
    +
    + )} + +
    +
    +
    + Boost: +
    + + {/* TODO: calculate boost */} + x2.5 + +
    +
    +
    + Unlock: +
    + + {calculatedUnlockDate} + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + {infoMessage} +
    +
    +
    + +
    +
    + {/* @ts-expect-error - Button component is not typed */} + + {/* @ts-expect-error - Button component is not typed */} + +
    +
    + + ) +} diff --git a/apps/hub/src/app/_components/vaults/modals/withdraw-vault-modal.tsx b/apps/hub/src/app/_components/vaults/modals/withdraw-vault-modal.tsx new file mode 100644 index 000000000..42966a61d --- /dev/null +++ b/apps/hub/src/app/_components/vaults/modals/withdraw-vault-modal.tsx @@ -0,0 +1,137 @@ +/* eslint-disable import/no-unresolved */ +'use client' + +import { useCallback } from 'react' + +import * as Dialog from '@radix-ui/react-dialog' +import { InfoIcon } from '@status-im/icons/12' +import { Button } from '@status-im/status-network/components' +import { useAccount } from 'wagmi' + +import { useVaultWithdraw } from '~hooks/useVaultWithdraw' + +import { BaseVaultModal } from './base-vault-modal' + +import type { Address } from 'viem' + +interface WithdrawVaultModalProps { + onClose: () => void + vaultAddress: Address + open?: boolean + onOpenChange?: (open: boolean) => void + children?: React.ReactNode +} + +/** + * Modal for emergency withdrawal from vault + */ +export function WithdrawVaultModal(props: WithdrawVaultModalProps) { + const { onClose, vaultAddress, open, onOpenChange, children } = props + + const { address } = useAccount() + const { mutate: withdraw } = useVaultWithdraw() + + const handleVaultWithdrawal = useCallback(() => { + const amountWei = 1000000000000000000n + + if (!address) { + console.error('No address found - wallet not connected') + return + } + + try { + withdraw({ + amountWei, + vaultAddress, + onSigned: () => { + onClose() + }, + }) + } catch (error) { + console.error('Error calling withdraw:', error) + } + }, [address, onClose, vaultAddress, withdraw]) + + return ( + +
    +
    +
    +
    +

    + Withdraw to +

    +
    +
    +
    +

    + {address} +

    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    + Your funds will sent directly to your connected wallet. +

    +
    + {/* @ts-expect-error - Button component is not typed */} + +
    +
    +
    +
    + +
    +
    + + {/* @ts-expect-error - Button component is not typed */} + + + {/* @ts-expect-error - Button component is not typed */} + +
    +
    +
    + ) +} diff --git a/apps/hub/src/app/_components/vaults/table-columns.tsx b/apps/hub/src/app/_components/vaults/table-columns.tsx index e85c8e89c..c700e2666 100644 --- a/apps/hub/src/app/_components/vaults/table-columns.tsx +++ b/apps/hub/src/app/_components/vaults/table-columns.tsx @@ -1,21 +1,30 @@ /* eslint-disable import/no-unresolved */ -import { AlertIcon, OptionsIcon, TimeIcon } from '@status-im/icons/12' +import { AlertIcon, TimeIcon } from '@status-im/icons/12' import { LockedIcon, UnlockedIcon } from '@status-im/icons/20' import { Button } from '@status-im/status-network/components' import { createColumnHelper } from '@tanstack/react-table' +import { formatUnits } from 'viem' -import { formatKarma, formatSNT } from '../../../utils/currency' -import { calculateVaultBoost } from '../../../utils/vault' -import { type VaultWithAddress } from '../../_hooks/useAccountVaults' -import { VaultLockConfigModal } from './vault-lock-modal' -import { VaultWithdrawModal } from './withdraw-modal' +import { SNT_TOKEN } from '~constants/index' +import { type StakingVault } from '~hooks/useStakingVaults' +import { shortenAddress } from '~utils/address' +import { formatSNT } from '~utils/currency' +import { + calculateDaysUntilUnlock, + calculateVaultBoost, + isVaultLocked, +} from '~utils/vault' + +import { LockVaultModal } from './modals/lock-vault-modal' +import { WithdrawVaultModal } from './modals/withdraw-vault-modal' interface TableColumnsProps { - vaults: VaultWithAddress[] | undefined + vaults: StakingVault[] | undefined openModalVaultId: string | null setOpenModalVaultId: (vaultId: string | null) => void emergencyModeEnabled: unknown + isConnected: boolean } export const createVaultTableColumns = ({ @@ -23,6 +32,7 @@ export const createVaultTableColumns = ({ openModalVaultId, setOpenModalVaultId, emergencyModeEnabled, + isConnected, }: TableColumnsProps) => { const totalStaked = vaults.reduce( (acc, vault) => acc + (vault.data?.stakedBalance || 0n), @@ -32,7 +42,7 @@ export const createVaultTableColumns = ({ (acc, vault) => acc + (vault.data?.rewardsAccrued || 0n), BigInt(0) ) - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper() return [ columnHelper.accessor('address', { @@ -60,8 +70,7 @@ export const createVaultTableColumns = ({ cell: ({ row }) => { return ( - {row.original.address.slice(0, 6)}... - {row.original.address.slice(-4)} + {shortenAddress(row.original.address)} ) }, @@ -98,11 +107,9 @@ export const createVaultTableColumns = ({ columnHelper.accessor('data.lockUntil', { header: 'Unlocks in', cell: ({ row }) => { - const now = Math.floor(Date.now() / 1000) - const lockUntilTimestamp = Number(row.original.data?.lockUntil) - const isLocked = lockUntilTimestamp > now + const isLocked = isVaultLocked(row.original.data?.lockUntil) const daysUntilUnlock = isLocked - ? Math.ceil((lockUntilTimestamp - now) / 86400) + ? calculateDaysUntilUnlock(row.original.data?.lockUntil) : null if (!daysUntilUnlock) { @@ -132,7 +139,7 @@ export const createVaultTableColumns = ({ const potentialBoost = stakedBalance > 0n && maxMP > mpAccrued - ? Number(maxMP - mpAccrued) / Number(stakedBalance) + ? (maxMP - mpAccrued) / stakedBalance : undefined return ( @@ -142,7 +149,8 @@ export const createVaultTableColumns = ({ {potentialBoost && ( - x{potentialBoost.toFixed(2)} if locked + x{formatSNT(formatUnits(potentialBoost, SNT_TOKEN.decimals))} if + locked )}
    @@ -160,7 +168,7 @@ export const createVaultTableColumns = ({ return (
    - {formatKarma(karma)} + {formatSNT(karma)} KARMA
    @@ -169,7 +177,7 @@ export const createVaultTableColumns = ({ footer: () => { return ( - {formatKarma(totalKarma)} + {formatSNT(totalKarma)} KARMA ) @@ -183,9 +191,7 @@ export const createVaultTableColumns = ({ id: 'state', header: 'State', cell: ({ row }) => { - const now = Math.floor(Date.now() / 1000) - const lockUntilTimestamp = Number(row.original.data?.lockUntil) - const isLocked = lockUntilTimestamp > now + const isLocked = isVaultLocked(row.original.data?.lockUntil) return (
    @@ -213,27 +219,29 @@ export const createVaultTableColumns = ({ const lockModalId = `lock-${row.original.address}` const isWithdrawModalOpen = openModalVaultId === withdrawModalId const isLockModalOpen = openModalVaultId === lockModalId + const isLocked = row.original?.data?.lockUntil - ? row.original.data.lockUntil > 0 + ? row.original.data.lockUntil > BigInt(Math.floor(Date.now() / 1000)) : false return ( -
    +
    {isLocked ? (
    - {Boolean(emergencyModeEnabled) && ( - setOpenModalVaultId(open ? withdrawModalId : null) } onClose={() => setOpenModalVaultId(null)} - vaultAdress={row.original.address} + vaultAddress={row.original.address} > {/* @ts-expect-error - Button component is not typed */} - + )} - setOpenModalVaultId(open ? lockModalId : null) @@ -265,13 +273,13 @@ export const createVaultTableColumns = ({ }, ]} onClose={() => setOpenModalVaultId(null)} - boost="x2.5" infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." > {/* @ts-expect-error - Button component is not typed */} - +
    ) : ( - setOpenModalVaultId(open ? lockModalId : null) @@ -291,8 +299,6 @@ export const createVaultTableColumns = ({ vaultAddress={row.original.address} title="Do you want to lock the vault?" description="Lock this vault to receive more Karma" - initialYears="2" - initialDays="732" actions={[ { label: "Don't lock", @@ -302,10 +308,10 @@ export const createVaultTableColumns = ({ }, ]} onClose={() => setOpenModalVaultId(null)} - boost="x2.5" infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks." onValidate={(_, days) => { const totalDays = parseInt(days || '0') + // TODO: read this from the contract return totalDays > 1460 ? 'Maximum lock time is 4 years' : null @@ -315,17 +321,14 @@ export const createVaultTableColumns = ({ - + )} - {/* @ts-expect-error - Button component is not typed */} -
    ) }, diff --git a/apps/hub/src/app/_components/vaults/types.ts b/apps/hub/src/app/_components/vaults/types.ts deleted file mode 100644 index e166f9cf6..000000000 --- a/apps/hub/src/app/_components/vaults/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Vault { - id: string - address: `0x${string}` - staked: bigint - unlocksIn: number | null - boost: number - potentialBoost?: number - karma: number - locked: boolean -} - -export type VaultColumnMeta = { - headerClassName?: string - cellClassName?: string - footerClassName?: string -} diff --git a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx b/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx deleted file mode 100644 index ac1a37ccd..000000000 --- a/apps/hub/src/app/_components/vaults/vault-lock-modal.tsx +++ /dev/null @@ -1,412 +0,0 @@ -/* eslint-disable import/no-unresolved */ -'use client' - -import { useEffect, useMemo, useState } from 'react' - -import { zodResolver } from '@hookform/resolvers/zod' -import * as Dialog from '@radix-ui/react-dialog' -import { ContextTag, Input } from '@status-im/components' -import { InfoIcon } from '@status-im/icons/16' -import { CloseIcon, IncorrectIcon } from '@status-im/icons/20' -import { Button } from '@status-im/status-network/components' -import { useForm } from 'react-hook-form' -import { z } from 'zod' - -import { useSliderConfig } from '../../_hooks/useSliderConfig' -import { useVaultLock } from '../../_hooks/useVaultLock' - -import type { HTMLAttributes } from 'react' -import type { Address } from 'viem' - -type ActionButton = HTMLAttributes & { - label: string - disabled?: boolean -} - -const createFormSchema = () => { - return z.object({ - years: z.string(), - days: z.string(), - }) -} - -type FormValues = z.infer> - -type Props = { - open?: boolean - onOpenChange?: (open: boolean) => void - onClose: () => void - vaultAddress: Address - title: string - description: string - children?: React.ReactNode - // Initial values - initialYears?: string - initialDays?: string - // Boost and unlock info - boost?: string - // Info box content - infoMessage?: string - // Error validation - errorMessage?: string | null - onValidate?: (years: string, days: string) => string | null - // Footer actions (left to right) - actions: [ActionButton, ActionButton] -} - -const VaultLockConfigModal = (props: Props) => { - const { - open, - onOpenChange, - onClose, - children, - title, - vaultAddress, - description, - initialYears = '0', - initialDays = '90', - boost = 'x2.5', - infoMessage = 'Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks.', - errorMessage: externalErrorMessage, - onValidate, - actions, - } = props - - const [closeAction, submitAction] = actions - - const { mutate: lockVault } = useVaultLock() - const { watch, setValue, handleSubmit } = useForm({ - resolver: zodResolver(createFormSchema()), - defaultValues: { - years: initialYears, - days: initialDays, - }, - }) - - const { data: sliderConfigQuery } = useSliderConfig() - - const sliderConfig = useMemo(() => { - // Convert seconds to days - const SECONDS_PER_DAY = 24 * 60 * 60 - const DAYS_PER_YEAR = 365 - - const minSeconds = sliderConfigQuery?.min || 7776000 // fallback: 90 days in seconds - const maxSeconds = sliderConfigQuery?.max || 126144000 // fallback: 4 years in seconds - - const minDays = Math.round(minSeconds / SECONDS_PER_DAY) - const maxDays = Math.round(maxSeconds / SECONDS_PER_DAY) - - // Format labels - const minYears = minDays / DAYS_PER_YEAR - const maxYears = maxDays / DAYS_PER_YEAR - - const minLabel = - minYears < 1 ? `${minDays} days` : `${minYears.toFixed(1)} years` - const maxLabel = - maxYears < 1 ? `${maxDays} days` : `${Math.round(maxYears)} years` - - return { - minLabel, - maxLabel, - minDays, - maxDays, - initialPosition: 50, - } - }, [sliderConfigQuery]) - - const years = watch('years') - const days = watch('days') - - // Calculate initial slider value from initialYears and initialDays - const initialSliderValue = - parseInt(initialYears || '0') * 365 + parseInt(initialDays || '0') - - const [sliderValue, setSliderValue] = useState(initialSliderValue) - - // Calculate unlock date based on days input - const calculateUnlockDate = (daysToAdd: number): string => { - const today = new Date() - const unlockDate = new Date(today) - unlockDate.setDate(today.getDate() + daysToAdd) - - const day = String(unlockDate.getDate()).padStart(2, '0') - const month = String(unlockDate.getMonth() + 1).padStart(2, '0') - const year = unlockDate.getFullYear() - - return `${day}/${month}/${year}` - } - - const calculatedUnlockDate = calculateUnlockDate(parseInt(days || '0')) - - // Sync slider with days input value - useEffect(() => { - const daysValue = parseInt(days || '0') - if (!isNaN(daysValue) && daysValue !== sliderValue) { - setSliderValue(daysValue) - } - }, [days, sliderValue]) - - const handleOpenChange = (nextOpen: boolean) => { - if (onOpenChange) { - onOpenChange(nextOpen) - } - if (!nextOpen) { - onClose() - } - } - - const handleVaultLockOrExtend = async (data: FormValues) => { - const totalDays = parseInt(data.days || '0') - const lockPeriodInSeconds = BigInt(totalDays * 24 * 60 * 60) - - // Close the modal first - onClose() - - // Then trigger the lock transaction - lockVault({ - lockPeriodInSeconds, - vaultAddress, - }) - } - - const handleSliderChange = (e: React.ChangeEvent) => { - const inputDays = parseFloat(e.target.value) - setSliderValue(inputDays) - - // Convert to years (365 days = 1 year) - const yearsValue = (inputDays / 365).toFixed(2) - - setValue('years', yearsValue) - setValue('days', Math.round(inputDays).toString()) - } - - const handleYearsChange = (value: string) => { - setValue('years', value) - - const yearsValue = parseFloat(value || '0') - if (!isNaN(yearsValue)) { - const totalDays = Math.round(yearsValue * 365) - const clampedDays = Math.max( - sliderConfig.minDays, - Math.min(totalDays, sliderConfig.maxDays) - ) - - setSliderValue(clampedDays) - setValue('days', clampedDays.toString()) - } - } - - const handleDaysChange = (value: string) => { - setValue('days', value) - - const inputDays = parseInt(value || '0') - if (!isNaN(inputDays)) { - const clampedDays = Math.max( - sliderConfig.minDays, - Math.min(inputDays, sliderConfig.maxDays) - ) - - setSliderValue(clampedDays) - setValue('years', (clampedDays / 365).toFixed(2)) - } - } - - // Built-in validation based on slider config - const getValidationError = (): string | null => { - const yearsValue = parseFloat(years || '0') - const daysValue = parseInt(days || '0') - - // Check if days is below minimum - if (daysValue > 0 && daysValue < sliderConfig.minDays) { - return `Minimum lock time is ${sliderConfig.minLabel}` - } - - // Check if days is above maximum - if (daysValue > sliderConfig.maxDays) { - return `Maximum lock time is ${sliderConfig.maxLabel}` - } - - // Check if years is below minimum (90 days = 0.246 years approximately) - const minYears = sliderConfig.minDays / 365 - if (yearsValue > 0 && yearsValue < minYears) { - return `Minimum lock time is ${sliderConfig.minLabel}` - } - - // Check if years is above maximum (4 years) - const maxYears = sliderConfig.maxDays / 365 - if (yearsValue > maxYears) { - return `Maximum lock time is ${sliderConfig.maxLabel}` - } - - return null - } - - // Validate on value change - const builtInError = getValidationError() - const customError = onValidate ? onValidate(years, days) : null - const errorMessage = customError || builtInError - const displayError = externalErrorMessage || errorMessage - const hasError = Boolean(displayError) - - return ( - - {children} - - - -
    - - - - -
    - -
    - - {title} - -
    -
    - -
    - {description} -
    -
    -
    - -
    -
    - -
    -
    - {sliderConfig.minLabel} - {sliderConfig.maxLabel} -
    -
    - -
    -
    - -
    - - or - -
    - -
    -
    - - {hasError && ( -
    -
    -
    -
    - -
    -
    -
    - {displayError} -
    -
    -
    - )} - -
    -
    -
    - Boost: -
    - - {boost} - -
    -
    -
    - Unlock: -
    - - {calculatedUnlockDate} - -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - {infoMessage} -
    -
    -
    - -
    -
    - - {/* @ts-expect-error - Button component is not typed */} - - - {/* @ts-expect-error - Button component is not typed */} - -
    -
    -
    -
    -
    -
    - ) -} - -export { VaultLockConfigModal } -// Backward compatibility -export { VaultLockConfigModal as VaultLockModal } diff --git a/apps/hub/src/app/_components/vaults/table.tsx b/apps/hub/src/app/_components/vaults/vaults-table.tsx similarity index 83% rename from apps/hub/src/app/_components/vaults/table.tsx rename to apps/hub/src/app/_components/vaults/vaults-table.tsx index b290c434f..c4fb25501 100644 --- a/apps/hub/src/app/_components/vaults/table.tsx +++ b/apps/hub/src/app/_components/vaults/vaults-table.tsx @@ -12,14 +12,17 @@ import { } from '@tanstack/react-table' import { useAccount, useReadContract } from 'wagmi' -import { STAKING_MANAGER } from '../../../config' -import { useAccountVaults } from '../../_hooks/useAccountVaults' -import { useVaultMutation } from '../../_hooks/useVault' -import { stakingManagerAbi } from '../../contracts' +import { STAKING_MANAGER } from '~constants/index' +import { useCreateVault } from '~hooks/useCreateVault' +import { type StakingVault, useStakingVaults } from '~hooks/useStakingVaults' + import { createVaultTableColumns } from './table-columns' -import type { VaultWithAddress } from '../../_hooks/useAccountVaults' -import type { VaultColumnMeta } from './types' +interface VaultColumnMeta { + headerClassName?: string + cellClassName?: string + footerClassName?: string +} /** * Gets the appropriate CSS class for table cell/header based on meta @@ -42,11 +45,11 @@ function getHeaderClassName( // Sub-components // ============================================================================ -interface TableHeaderProps { - table: ReturnType> +interface TableProps { + table: ReturnType> } -function TableHeader({ table }: TableHeaderProps) { +function TableHeader({ table }: TableProps) { return ( {table.getHeaderGroups().map(headerGroup => ( @@ -70,11 +73,7 @@ function TableHeader({ table }: TableHeaderProps) { ) } -interface TableBodyProps { - table: ReturnType> -} - -function TableBody({ table }: TableBodyProps) { +function TableBody({ table }: TableProps) { return ( {table.getRowModel().rows.map(row => ( @@ -93,11 +92,7 @@ function TableBody({ table }: TableBodyProps) { ) } -interface TableFooterProps { - table: ReturnType> -} - -function TableFooter({ table }: TableFooterProps) { +function TableFooter({ table }: TableProps) { return ( {table.getFooterGroups().map(footerGroup => ( @@ -127,13 +122,13 @@ function TableFooter({ table }: TableFooterProps) { export function VaultsTable() { const [openModalVaultId, setOpenModalVaultId] = useState(null) - const { data: vaultDataList } = useAccountVaults() + const { data: vaultDataList } = useStakingVaults() const { isConnected } = useAccount() - const { mutate: deployVault } = useVaultMutation() + const { mutate: createVault } = useCreateVault() const { data: emergencyModeEnabled } = useReadContract({ address: STAKING_MANAGER.address, - abi: stakingManagerAbi, + abi: STAKING_MANAGER.abi, functionName: 'emergencyModeEnabled', }) @@ -144,8 +139,9 @@ export function VaultsTable() { openModalVaultId, setOpenModalVaultId, emergencyModeEnabled, + isConnected, }), - [openModalVaultId, emergencyModeEnabled, vaultDataList] + [vaultDataList, openModalVaultId, emergencyModeEnabled, isConnected] ) const table = useReactTable({ @@ -165,7 +161,7 @@ export function VaultsTable() { - - -
    -
    -
    - -
    -

    - Emergency withdrawal -

    -
    -
    - -
    -

    - In the event of a hack or contract compromise, you can - use this feature to immediately withdraw your funds from - the vault. -

    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -

    - Withdraw to -

    -
    -
    -
    -

    - {address} -

    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -

    - Your funds will sent directly to your connected wallet. -

    -
    - {/* @ts-expect-error - Button component is not typed */} - -
    -
    -
    -
    - -
    -
    - {/* @ts-expect-error - Button component is not typed */} - - {/* @ts-expect-error - Button component is not typed */} - -
    -
    -
    - - - - ) -} - -export { VaultWithdrawModal } diff --git a/apps/hub/src/app/_constants/address.ts b/apps/hub/src/app/_constants/address.ts new file mode 100644 index 000000000..20a81cee3 --- /dev/null +++ b/apps/hub/src/app/_constants/address.ts @@ -0,0 +1,31 @@ +import { + faucetAbi, + stakingManagerAbi, + tokenAbi, + vaultFactoryAbi, +} from './contracts' + +import type { Abi, Address } from 'viem' + +export const STAKING_MANAGER = { + address: '0x5cDf1646E4c1D21eE94DED1DA8da3Ca450dc96D1' as Address, + abi: stakingManagerAbi as Abi, +} as const + +export const VAULT_FACTORY = { + address: '0xddDcd43a0B0dA865decf3e4Ae71FbBE3e2DfFF14' as Address, + abi: vaultFactoryAbi as Abi, +} as const + +export const SNT_TOKEN = { + address: '0x1C3Ac2a186c6149Ae7Cb4D716eBbD0766E4f898a' as Address, + name: 'Status Test Token', + symbol: 'STT', + decimals: 18, + abi: tokenAbi as Abi, +} as const + +export const FAUCET = { + address: '0x4Fb609F4a457f47B41D35Dd060447271F000120A' as Address, + abi: faucetAbi as Abi, +} as const diff --git a/apps/hub/src/config/chain.ts b/apps/hub/src/app/_constants/chain.ts similarity index 100% rename from apps/hub/src/config/chain.ts rename to apps/hub/src/app/_constants/chain.ts diff --git a/apps/hub/src/app/contracts/faucet.ts b/apps/hub/src/app/_constants/contracts/faucet.ts similarity index 95% rename from apps/hub/src/app/contracts/faucet.ts rename to apps/hub/src/app/_constants/contracts/faucet.ts index 43a45328e..36184e86a 100644 --- a/apps/hub/src/app/contracts/faucet.ts +++ b/apps/hub/src/app/_constants/contracts/faucet.ts @@ -1,6 +1,4 @@ -import type { Abi } from 'viem' - -export const faucetAbi: Abi = [ +export const faucetAbi = [ { inputs: [{ internalType: 'address', name: '_token', type: 'address' }], stateMutability: 'nonpayable', diff --git a/apps/hub/src/app/contracts/index.ts b/apps/hub/src/app/_constants/contracts/index.ts similarity index 100% rename from apps/hub/src/app/contracts/index.ts rename to apps/hub/src/app/_constants/contracts/index.ts diff --git a/apps/hub/src/app/contracts/stakingManagerAbi.ts b/apps/hub/src/app/_constants/contracts/stakingManagerAbi.ts similarity index 76% rename from apps/hub/src/app/contracts/stakingManagerAbi.ts rename to apps/hub/src/app/_constants/contracts/stakingManagerAbi.ts index 6e6fa25f3..51e592803 100644 --- a/apps/hub/src/app/contracts/stakingManagerAbi.ts +++ b/apps/hub/src/app/_constants/contracts/stakingManagerAbi.ts @@ -3,10 +3,12 @@ export const stakingManagerAbi = [ { inputs: [], name: 'StakeManager__AmountCannotBeZero', type: 'error' }, { inputs: [], name: 'StakeManager__DurationCannotBeZero', type: 'error' }, { inputs: [], name: 'StakeManager__EmergencyModeEnabled', type: 'error' }, + { inputs: [], name: 'StakeManager__FundsLocked', type: 'error' }, { inputs: [], name: 'StakeManager__InsufficientBalance', type: 'error' }, { inputs: [], name: 'StakeManager__InvalidVault', type: 'error' }, { inputs: [], name: 'StakeManager__MigrationTargetHasFunds', type: 'error' }, { inputs: [], name: 'StakeManager__RewardPeriodNotEnded', type: 'error' }, + { inputs: [], name: 'StakeManager__RewardTransferFailed', type: 'error' }, { inputs: [], name: 'StakeManager__Unauthorized', type: 'error' }, { inputs: [], name: 'StakeManager__VaultAlreadyRegistered', type: 'error' }, { inputs: [], name: 'StakeManager__VaultNotRegistered', type: 'error' }, @@ -89,20 +91,137 @@ export const stakingManagerAbi = [ { anonymous: false, inputs: [ + { + indexed: false, + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'duration', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'startTime', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'endTime', + type: 'uint256', + }, + ], + name: 'RewardSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'RewardsRedeemed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'supplier', + type: 'address', + }, + ], + name: 'RewardsSupplierSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'bytes32', + name: 'previousAdminRole', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'newAdminRole', + type: 'bytes32', + }, + ], + name: 'RoleAdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, { indexed: true, internalType: 'address', - name: 'previousOwner', + name: 'account', type: 'address', }, { indexed: true, internalType: 'address', - name: 'newOwner', + name: 'sender', type: 'address', }, ], - name: 'OwnershipTransferred', + name: 'RoleRevoked', type: 'event', }, { @@ -157,6 +276,19 @@ export const stakingManagerAbi = [ name: 'TrustedCodehashUpdated', type: 'event', }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'Unpaused', + type: 'event', + }, { anonymous: false, inputs: [ @@ -262,6 +394,20 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [], + name: 'DEFAULT_ADMIN_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'GUARDIAN_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, { inputs: [], name: 'MAX_BALANCE', @@ -318,6 +464,13 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [], + name: 'REWARD_TOKEN', + outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, { inputs: [], name: 'SCALE_FACTOR', @@ -383,6 +536,13 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [{ internalType: 'bytes32', name: 'role', type: 'bytes32' }], + name: 'getRoleAdmin', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, { inputs: [ { internalType: 'address', name: 'vaultAddress', type: 'address' }, @@ -400,7 +560,6 @@ export const stakingManagerAbi = [ name: 'lastMPUpdateTime', type: 'uint256', }, - { internalType: 'uint256', name: 'lockUntil', type: 'uint256' }, { internalType: 'uint256', name: 'rewardsAccrued', type: 'uint256' }, ], internalType: 'struct StakeManager.VaultData', @@ -411,10 +570,31 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'grantRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'hasRole', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, { inputs: [ { internalType: 'address', name: '_owner', type: 'address' }, { internalType: 'address', name: '_stakingToken', type: 'address' }, + { internalType: 'address', name: '_rewardToken', type: 'address' }, ], name: 'initialize', outputs: [], @@ -457,9 +637,14 @@ export const stakingManagerAbi = [ type: 'function', }, { - inputs: [{ internalType: 'uint256', name: 'lockPeriod', type: 'uint256' }], + inputs: [ + { internalType: 'uint256', name: 'lockPeriod', type: 'uint256' }, + { internalType: 'uint256', name: 'currentLockUntil', type: 'uint256' }, + ], name: 'lock', - outputs: [], + outputs: [ + { internalType: 'uint256', name: 'newLockUntil', type: 'uint256' }, + ], stateMutability: 'nonpayable', type: 'function', }, @@ -497,8 +682,15 @@ export const stakingManagerAbi = [ }, { inputs: [], - name: 'owner', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'paused', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], stateMutability: 'view', type: 'function', }, @@ -509,6 +701,13 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'redeemRewards', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, { inputs: [], name: 'registerVault', @@ -517,8 +716,21 @@ export const stakingManagerAbi = [ type: 'function', }, { - inputs: [], - name: 'renounceOwnership', + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'renounceRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'revokeRole', outputs: [], stateMutability: 'nonpayable', type: 'function', @@ -600,9 +812,12 @@ export const stakingManagerAbi = [ inputs: [ { internalType: 'uint256', name: 'amount', type: 'uint256' }, { internalType: 'uint256', name: 'lockPeriod', type: 'uint256' }, + { internalType: 'uint256', name: 'currentLockUntil', type: 'uint256' }, ], name: 'stake', - outputs: [], + outputs: [ + { internalType: 'uint256', name: 'newLockUntil', type: 'uint256' }, + ], stateMutability: 'nonpayable', type: 'function', }, @@ -615,6 +830,13 @@ export const stakingManagerAbi = [ stateMutability: 'view', type: 'function', }, + { + inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }], + name: 'supportsInterface', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, { inputs: [], name: 'totalMP', @@ -672,8 +894,8 @@ export const stakingManagerAbi = [ type: 'function', }, { - inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], - name: 'transferOwnership', + inputs: [], + name: 'unpause', outputs: [], stateMutability: 'nonpayable', type: 'function', @@ -736,7 +958,6 @@ export const stakingManagerAbi = [ { internalType: 'uint256', name: 'mpAccrued', type: 'uint256' }, { internalType: 'uint256', name: 'maxMP', type: 'uint256' }, { internalType: 'uint256', name: 'lastMPUpdateTime', type: 'uint256' }, - { internalType: 'uint256', name: 'lockUntil', type: 'uint256' }, { internalType: 'uint256', name: 'rewardsAccrued', type: 'uint256' }, ], stateMutability: 'view', diff --git a/apps/hub/src/app/contracts/tokenAbi.ts b/apps/hub/src/app/_constants/contracts/tokenAbi.ts similarity index 100% rename from apps/hub/src/app/contracts/tokenAbi.ts rename to apps/hub/src/app/_constants/contracts/tokenAbi.ts diff --git a/apps/hub/src/app/contracts/vaultAbi.ts b/apps/hub/src/app/_constants/contracts/vaultAbi.ts similarity index 50% rename from apps/hub/src/app/contracts/vaultAbi.ts rename to apps/hub/src/app/_constants/contracts/vaultAbi.ts index 77eced031..f9b8cb975 100644 --- a/apps/hub/src/app/contracts/vaultAbi.ts +++ b/apps/hub/src/app/_constants/contracts/vaultAbi.ts @@ -1,229 +1,258 @@ -export const vaultAbi = [ +import type { Abi } from 'viem' + +export const vaultAbi: Abi = [ { - type: 'constructor', inputs: [ - { name: 'token', type: 'address', internalType: 'contract IERC20' }, + { internalType: 'contract IERC20', name: 'token', type: 'address' }, ], stateMutability: 'nonpayable', + type: 'constructor', }, + { inputs: [], name: 'StakeVault__FundsLocked', type: 'error' }, + { inputs: [], name: 'StakeVault__InvalidDestinationAddress', type: 'error' }, + { inputs: [], name: 'StakeVault__MigrationFailed', type: 'error' }, + { inputs: [], name: 'StakeVault__NotAllowedToExit', type: 'error' }, + { inputs: [], name: 'StakeVault__NotAllowedToLeave', type: 'error' }, + { inputs: [], name: 'StakeVault__NotAuthorized', type: 'error' }, + { inputs: [], name: 'StakeVault__NotEnoughAvailableBalance', type: 'error' }, { - type: 'function', - name: 'STAKING_TOKEN', inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'contract IERC20' }], - stateMutability: 'view', + name: 'StakeVault__StakeManagerImplementationNotTrusted', + type: 'error', + }, + { inputs: [], name: 'StakeVault__StakingFailed', type: 'error' }, + { inputs: [], name: 'StakeVault__UnstakingFailed', type: 'error' }, + { inputs: [], name: 'StakeVault__WithdrawFromVaultFailed', type: 'error' }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: 'uint8', name: 'version', type: 'uint8' }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', }, { - type: 'function', - name: 'amountStaked', inputs: [], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + name: 'STAKING_TOKEN', + outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }], stateMutability: 'view', + type: 'function', }, { + inputs: [], + name: 'amountStaked', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', type: 'function', - name: 'availableWithdraw', + }, + { inputs: [ - { name: '_token', type: 'address', internalType: 'contract IERC20' }, + { internalType: 'contract IERC20', name: '_token', type: 'address' }, ], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + name: 'availableWithdraw', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], stateMutability: 'view', + type: 'function', }, { - type: 'function', - name: 'emergencyExit', inputs: [ - { name: '_destination', type: 'address', internalType: 'address' }, + { internalType: 'address', name: '_destination', type: 'address' }, ], + name: 'emergencyExit', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'initialize', inputs: [ - { name: '_owner', type: 'address', internalType: 'address' }, - { name: '_stakeManager', type: 'address', internalType: 'address' }, + { internalType: 'address', name: '_owner', type: 'address' }, + { internalType: 'address', name: '_stakeManager', type: 'address' }, ], + name: 'initialize', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'leave', inputs: [ - { name: '_destination', type: 'address', internalType: 'address' }, + { internalType: 'address', name: '_destination', type: 'address' }, ], + name: 'leave', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', + inputs: [{ internalType: 'uint256', name: '_seconds', type: 'uint256' }], name: 'lock', - inputs: [{ name: '_seconds', type: 'uint256', internalType: 'uint256' }], outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { + inputs: [], + name: 'lockUntil', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'migrateTo', type: 'address' }], name: 'migrateToVault', - inputs: [{ name: 'migrateTo', type: 'address', internalType: 'address' }], outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'owner', inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'address' }], + name: 'owner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', + type: 'function', }, { - type: 'function', - name: 'register', inputs: [], + name: 'register', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'renounceOwnership', inputs: [], + name: 'renounceOwnership', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'stake', inputs: [ - { name: '_amount', type: 'uint256', internalType: 'uint256' }, - { name: '_seconds', type: 'uint256', internalType: 'uint256' }, - { name: '_from', type: 'address', internalType: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_seconds', type: 'uint256' }, + { internalType: 'address', name: '_from', type: 'address' }, ], + name: 'stake', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'stake', inputs: [ - { name: '_amount', type: 'uint256', internalType: 'uint256' }, - { name: '_seconds', type: 'uint256', internalType: 'uint256' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_seconds', type: 'uint256' }, ], + name: 'stake', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'stakeManager', inputs: [], + name: 'stakeManager', outputs: [ { + internalType: 'contract IStakeManagerProxy', name: '', type: 'address', - internalType: 'contract IStakeManagerProxy', }, ], stateMutability: 'view', + type: 'function', }, { - type: 'function', - name: 'stakeManagerImplementationAddress', inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'address' }], + name: 'stakeManagerImplementationAddress', + outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', + type: 'function', }, { - type: 'function', + inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], name: 'transferOwnership', - inputs: [{ name: 'newOwner', type: 'address', internalType: 'address' }], outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'trustStakeManager', inputs: [ - { name: 'stakeManagerAddress', type: 'address', internalType: 'address' }, + { internalType: 'address', name: 'stakeManagerAddress', type: 'address' }, ], + name: 'trustStakeManager', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', + inputs: [{ internalType: 'uint256', name: '_amount', type: 'uint256' }], name: 'unstake', - inputs: [{ name: '_amount', type: 'uint256', internalType: 'uint256' }], outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'unstake', inputs: [ - { name: '_amount', type: 'uint256', internalType: 'uint256' }, - { name: '_destination', type: 'address', internalType: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'address', name: '_destination', type: 'address' }, ], + name: 'unstake', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'withdraw', - inputs: [ - { name: '_token', type: 'address', internalType: 'contract IERC20' }, - { name: '_amount', type: 'uint256', internalType: 'uint256' }, - { name: '_destination', type: 'address', internalType: 'address' }, - ], + inputs: [{ internalType: 'uint256', name: '_lockUntil', type: 'uint256' }], + name: 'updateLockUntil', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'function', - name: 'withdraw', inputs: [ - { name: '_token', type: 'address', internalType: 'contract IERC20' }, - { name: '_amount', type: 'uint256', internalType: 'uint256' }, + { internalType: 'contract IERC20', name: '_token', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'address', name: '_destination', type: 'address' }, ], + name: 'withdraw', outputs: [], stateMutability: 'nonpayable', + type: 'function', }, { - type: 'event', - name: 'Initialized', inputs: [ - { name: 'version', type: 'uint8', indexed: false, internalType: 'uint8' }, + { internalType: 'contract IERC20', name: '_token', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, ], - anonymous: false, + name: 'withdraw', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', }, { - type: 'event', - name: 'OwnershipTransferred', inputs: [ - { - name: 'previousOwner', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'newOwner', - type: 'address', - indexed: true, - internalType: 'address', - }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'address', name: '_destination', type: 'address' }, ], - anonymous: false, - }, - { type: 'error', name: 'StakeVault__InvalidDestinationAddress', inputs: [] }, - { type: 'error', name: 'StakeVault__MigrationFailed', inputs: [] }, - { type: 'error', name: 'StakeVault__NotAllowedToExit', inputs: [] }, - { type: 'error', name: 'StakeVault__NotAllowedToLeave', inputs: [] }, - { type: 'error', name: 'StakeVault__NotEnoughAvailableBalance', inputs: [] }, - { - type: 'error', - name: 'StakeVault__StakeManagerImplementationNotTrusted', - inputs: [], + name: 'withdrawFromVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', }, - { type: 'error', name: 'StakeVault__StakingFailed', inputs: [] }, - { type: 'error', name: 'StakeVault__UnstakingFailed', inputs: [] }, -] as const +] diff --git a/apps/hub/src/app/_constants/contracts/vaultFactoryAbi.ts b/apps/hub/src/app/_constants/contracts/vaultFactoryAbi.ts new file mode 100644 index 000000000..7648ad0a3 --- /dev/null +++ b/apps/hub/src/app/_constants/contracts/vaultFactoryAbi.ts @@ -0,0 +1,150 @@ +export const vaultFactoryAbi = [ + { + inputs: [ + { internalType: 'address', name: '_owner', type: 'address' }, + { internalType: 'address', name: '_stakeManager', type: 'address' }, + { + internalType: 'address', + name: '_vaultImplementation', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [], + name: 'VaultFactory__InvalidStakeManagerAddress', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'newStakeManagerAddress', + type: 'address', + }, + ], + name: 'StakeManagerAddressChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'vault', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'VaultCreated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'newVaultImplementation', + type: 'address', + }, + ], + name: 'VaultImplementationChanged', + type: 'event', + }, + { + inputs: [], + name: 'createVault', + outputs: [ + { internalType: 'contract StakeVault', name: 'clone', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_stakeManager', type: 'address' }, + ], + name: 'setStakeManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_vaultImplementation', + type: 'address', + }, + ], + name: 'setVaultImplementation', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'stakeManager', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'vaultImplementation', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] diff --git a/apps/hub/src/config/index.ts b/apps/hub/src/app/_constants/index.ts similarity index 52% rename from apps/hub/src/config/index.ts rename to apps/hub/src/app/_constants/index.ts index 6f5a55683..6b4627b25 100644 --- a/apps/hub/src/config/index.ts +++ b/apps/hub/src/app/_constants/index.ts @@ -1 +1,2 @@ export * from './address' +export * from './chain' diff --git a/apps/hub/src/app/_hooks/useApproveToken.ts b/apps/hub/src/app/_hooks/useApproveToken.ts new file mode 100644 index 000000000..8973b2945 --- /dev/null +++ b/apps/hub/src/app/_hooks/useApproveToken.ts @@ -0,0 +1,175 @@ +import { useToast } from '@status-im/components' +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address, parseUnits, zeroAddress } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { SNT_TOKEN } from '~constants/index' +import { useVaultStateContext } from '~hooks/useVaultStateContext' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for token approval + */ +export interface ApproveTokenParams { + /** Amount of tokens to approve (in token units, not wei) */ + amount: string + /** Address of the spender (vault) to approve tokens for */ + spenderAddress: Address +} + +/** + * Return type for useApproveToken hook + */ +export type UseApproveTokenReturn = UseMutationResult< + void, + Error, + ApproveTokenParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY = 'approve-token' as const + +const TRANSACTION_CONFIG = { + CONFIRMATION_BLOCKS: 1, +} as const + +// ============================================================================ +// Hook +// ============================================================================ + +/** + * Mutation hook to approve SNT tokens for a spender (typically a vault) + * + * **Approval Process:** + * 1. Validates wallet connection and parameters + * 2. Converts amount to wei + * 3. Calls ERC20 approve function + * 4. Waits for transaction confirmation + * 5. Updates state machine with approval status + * + * @returns Mutation result with approval function and status + * + * @throws {Error} When wallet is not connected + * @throws {Error} When spender address is invalid + * @throws {Error} When amount is invalid or zero + * @throws {Error} When transaction is reverted + * + * @example + * Basic usage + * ```tsx + * function ApproveButton() { + * const { mutate: approveToken, isPending } = useApproveToken() + * + * const handleApprove = () => { + * approveToken({ + * amount: '100', + * spenderAddress: vaultAddress + * }) + * } + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With callbacks + * ```tsx + * approveToken( + * { amount: '100', spenderAddress: vaultAddress }, + * { + * onSuccess: (txHash) => { + * console.log('Approved! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Approval failed:', error) + * }, + * } + * ) + * ``` + */ +export function useApproveToken(): UseApproveTokenReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent } = useVaultStateContext() + const toast = useToast() + + return useMutation({ + mutationKey: [MUTATION_KEY, address], + mutationFn: async ({ + amount, + spenderAddress, + }: ApproveTokenParams): Promise => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate spender address + if (!spenderAddress || spenderAddress === zeroAddress) { + throw new Error('Invalid spender address provided') + } + + // Validate amount + if (!amount || parseFloat(amount) <= 0) { + throw new Error('Amount must be greater than 0') + } + + // Convert amount to wei + const amountWei = parseUnits(amount, SNT_TOKEN.decimals) + + // Notify state machine of approval start + sendVaultEvent({ + type: 'START_INCREASE_ALLOWANCE', + amount, + }) + + try { + // Execute approval transaction + const hash = await writeContractAsync({ + address: SNT_TOKEN.address, + abi: SNT_TOKEN.abi, + functionName: 'approve', + args: [spenderAddress, amountWei], + }) + + // Transaction submitted, notify state machine + sendVaultEvent({ type: 'SIGN' }) + + // Wait for confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: TRANSACTION_CONFIG.CONFIRMATION_BLOCKS, + }) + + // Check for revert + if (status === 'reverted') { + throw new Error('Transaction was reverted') + } + + // Approval successful, notify state machine + sendVaultEvent({ type: 'COMPLETE', amount }) + toast.positive('Token Allowance has been increased') + } catch (error) { + // Handle approval failure + console.error('Failed to approve tokens:', error) + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useCompoundMultiplierPoints.ts b/apps/hub/src/app/_hooks/useCompoundMultiplierPoints.ts new file mode 100644 index 000000000..a6b2d0363 --- /dev/null +++ b/apps/hub/src/app/_hooks/useCompoundMultiplierPoints.ts @@ -0,0 +1,167 @@ +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { formatUnits, zeroAddress } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { + SNT_TOKEN, + STAKING_MANAGER, + statusNetworkTestnet, +} from '~constants/index' +import { useMultiplierPointsBalance } from '~hooks/useMultiplierPoints' + +import { useVaultStateContext } from './useVaultStateContext' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Return type for the useCompoundMultiplierPoints mutation hook + */ +export type UseCompoundMultiplierPointsReturn = UseMutationResult< + void, + Error, + void, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY = 'compound-multiplier-points' as const + +const TRANSACTION_CONFIG = { + CONFIRMATION_BLOCKS: 1, +} as const + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to compound multiplier points (MP) for the connected account + * + * **Compounding Process:** + * Calls StakingManager.updateAccount() which: + * - Updates global state (reward index, MP accrual) + * - Calculates new MP accrued since last update + * - Updates the account's reward index + * - Compounds rewards based on current MP balance + * - Emits AccountUpdated event + * + * @returns Mutation result with mutate function to trigger compounding + * + * @throws {Error} When wallet is not connected + * @throws {Error} When account address is invalid + * @throws {Error} When transaction is reverted + * + * @example + * Basic usage + * ```tsx + * function CompoundButton() { + * const { mutate: compound, isPending } = useCompoundMultiplierPoints() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success/error handling + * ```tsx + * function CompoundManager() { + * const { mutate: compound, isPending } = useCompoundMultiplierPoints() + * + * const handleCompound = () => { + * compound(undefined, { + * onSuccess: (txHash) => { + * toast.success(`Compounded! Tx: ${txHash}`) + * }, + * onError: (error) => { + * toast.error(`Failed to compound: ${error.message}`) + * }, + * }) + * } + * + * return ( + * + * ) + * } + * ``` + */ +export function useCompoundMultiplierPoints(): UseCompoundMultiplierPointsReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { data: mpBalance, refetch: refetchMultiplierPoints } = + useMultiplierPointsBalance() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + + return useMutation({ + mutationKey: [MUTATION_KEY, address], + mutationFn: async (): Promise => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate account address + if (address === zeroAddress) { + throw new Error('Invalid account address provided') + } + + // Get the current MP balance to show in the dialog + const formattedAmount = formatUnits(mpBalance || 0n, SNT_TOKEN.decimals) + + sendVaultEvent({ + type: 'START_COMPOUND', + amount: formattedAmount, + }) + + // Execute compound transaction via updateAccount + const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, + address: STAKING_MANAGER.address, + abi: STAKING_MANAGER.abi, + functionName: 'updateAccount', + args: [address], + }) + + // Signal user to sign transaction + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: TRANSACTION_CONFIG.CONFIRMATION_BLOCKS, + }) + + // Check if transaction was reverted + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + }, + onSuccess: () => { + resetVault() + // Refetch account vaults to update UI with new compounded values + refetchMultiplierPoints() + }, + onError: () => { + sendVaultEvent({ type: 'REJECT' }) + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useCompounder.ts b/apps/hub/src/app/_hooks/useCompounder.ts deleted file mode 100644 index 8132be8d8..000000000 --- a/apps/hub/src/app/_hooks/useCompounder.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { useMutation, type UseMutationResult } from '@tanstack/react-query' -import { type Address } from 'viem' -import { useAccount, useConfig, useWriteContract } from 'wagmi' -import { waitForTransactionReceipt } from 'wagmi/actions' - -import { STAKING_MANAGER } from '../../config' -import { statusNetworkTestnet } from '../../config/chain' -import { stakingManagerAbi } from '../contracts' -import { useAccountVaults } from './useAccountVaults' - -// ============================================================================ -// Types -// ============================================================================ - -/** - * Parameters for compounding a vault - */ -export interface CompoundVaultParams { - /** The vault address to compound */ - vaultAddress: Address -} - -/** - * Return type for the useCompounder hook - */ -export type UseCompounderReturn = UseMutationResult< - Address, - Error, - CompoundVaultParams, - unknown -> - -// ============================================================================ -// Constants -// ============================================================================ - -const MUTATION_KEY_PREFIX = 'compound' as const -const CONFIRMATION_BLOCKS = 1 - -// ============================================================================ -// Mutation Hook -// ============================================================================ - -/** - * Mutation hook to compound multiplier points (MP) for a vault - * - * **Compounding Process:** - * Calls StakingManager.compound() which: - * - Updates global state (reward index, MP accrual) - * - Calculates new MP accrued since last update - * - Updates the vault's reward index - * - Compounds rewards based on current MP balance - * - Emits VaultUpdated event - * - * @returns Mutation result with mutate function to trigger compound - * - * @throws {Error} When wallet is not connected - * @throws {Error} When vault address is invalid - * @throws {Error} When transaction is reverted - * - * @example - * Basic usage - * ```tsx - * function CompoundButton({ vaultAddress }: { vaultAddress: Address }) { - * const { mutate: compound, isPending } = useCompounder() - * - * return ( - * - * ) - * } - * ``` - * - * @example - * With success/error handling - * ```tsx - * function CompoundManager({ vaultAddress }: { vaultAddress: Address }) { - * const { mutate: compound, isPending, isSuccess, error } = useCompounder() - * - * const handleCompound = () => { - * compound( - * { vaultAddress }, - * { - * onSuccess: (txHash) => { - * console.log('Transaction hash:', txHash) - * }, - * onError: (error) => { - * console.error('Compound failed:', error) - * }, - * } - * ) - * } - * - * return ( - * - * ) - * } - * ``` - */ -export function useCompounder(): UseCompounderReturn { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() - const config = useConfig() - const { refetch: refetchAccountVaults } = useAccountVaults() - - return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, address], - mutationFn: async ({ - vaultAddress, - }: CompoundVaultParams): Promise
    => { - // Validate wallet connection - if (!address) { - throw new Error( - 'Wallet not connected. Please connect your wallet first.' - ) - } - - // Validate vault address - if (!vaultAddress || vaultAddress === '0x0') { - throw new Error('Invalid vault address provided') - } - - // Execute compound transaction - const hash = await writeContractAsync({ - chain: statusNetworkTestnet, - account: address, - address: STAKING_MANAGER.address, - abi: stakingManagerAbi, - functionName: 'compound', - args: [vaultAddress], - }) - - // Wait for transaction confirmation - const { status } = await waitForTransactionReceipt(config, { - hash, - confirmations: CONFIRMATION_BLOCKS, - }) - - // Check if transaction was reverted - if (status === 'reverted') { - throw new Error('Transaction was reverted') - } - - // Refetch account vaults to update UI - refetchAccountVaults() - - return hash - }, - }) -} diff --git a/apps/hub/src/app/_hooks/useCreateVault.ts b/apps/hub/src/app/_hooks/useCreateVault.ts new file mode 100644 index 000000000..e5d7725fa --- /dev/null +++ b/apps/hub/src/app/_hooks/useCreateVault.ts @@ -0,0 +1,186 @@ +import { useToast } from '@status-im/components' +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address, type Hash } from 'viem' +import { useAccount, useConfig, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { VAULT_FACTORY } from '~constants/index' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useVaultStateContext } from '~hooks/useVaultStateContext' +import { shortenAddress } from '~utils/address' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Return type for the useCreateVault hook + */ +export type UseCreateVaultReturn = UseMutationResult + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY = 'create-vault' as const + +const DEFAULT_DELAY = 100 as const + +const TRANSACTION_CONFIG = { + CONFIRMATION_BLOCKS: 1, +} as const + +// Event signature for VaultCreated(address indexed vault, address indexed owner) +const VAULT_CREATED_EVENT_SIGNATURE = + '0x5d9c31ffa0fecffd7cf379989a3c7af252f0335e0d2a1320b55245912c781f53' as const + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Extracts the vault address from VaultCreated event logs + */ +function extractVaultAddressFromLogs( + logs: Array<{ address: Address; topics: readonly string[] }> +): Address { + const vaultCreatedLog = logs.find(log => { + // Check if this log is from the vault factory contract + if (log.address.toLowerCase() !== VAULT_FACTORY.address.toLowerCase()) { + return false + } + + // Check if this is the VaultCreated event + return log.topics[0] === VAULT_CREATED_EVENT_SIGNATURE + }) + + if (!vaultCreatedLog || !vaultCreatedLog.topics[1]) { + throw new Error('VaultCreated event not found in transaction logs') + } + + // The vault address is the first indexed parameter (topics[1]) + // Remove the padding from the address (first 24 bytes) + const paddedAddress = vaultCreatedLog.topics[1] as `0x${string}` + return `0x${paddedAddress.slice(26)}` as Address +} + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to create a new staking vault + * + * **Creation Process:** + * 1. Calls VaultFactory.createVault() + * 2. Waits for transaction confirmation + * 3. Extracts the new vault address from event logs + * 4. Updates state machine and refetches vaults + * 5. Shows success toast notification + * + * @returns Mutation result with mutate function to trigger vault creation + * + * @throws {Error} When wallet is not connected + * @throws {Error} When transaction is reverted + * @throws {Error} When vault address cannot be extracted + * + * @example + * Basic usage + * ```tsx + * function CreateVaultButton() { + * const { mutate: createVault, isPending } = useCreateVault() + * + * return ( + * + * ) + * } + * ``` + * + * @example + * With success handling + * ```tsx + * const { mutate: createVault } = useCreateVault() + * + * createVault(undefined, { + * onSuccess: (txHash) => { + * console.log('Vault created! Transaction:', txHash) + * }, + * onError: (error) => { + * console.error('Failed to create vault:', error) + * }, + * }) + * ``` + */ +export function useCreateVault(): UseCreateVaultReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const { refetch: refetchStakingVaults } = useStakingVaults() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const config = useConfig() + const toast = useToast() + + return useMutation({ + mutationKey: [MUTATION_KEY, address], + mutationFn: async (): Promise => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Notify state machine of vault creation start + sendVaultEvent({ type: 'START_CREATE_VAULT' }) + + try { + // Execute vault creation transaction + const hash = await writeContractAsync({ + address: VAULT_FACTORY.address, + abi: VAULT_FACTORY.abi, + functionName: 'createVault', + }) + + // Transaction submitted, notify state machine + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation and get logs + const { status, logs } = await waitForTransactionReceipt(config, { + hash, + confirmations: TRANSACTION_CONFIG.CONFIRMATION_BLOCKS, + }) + + // Check for transaction revert + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + // Extract the newly created vault address from logs + const deployedVaultAddress = extractVaultAddressFromLogs(logs) + + // Show success toast + toast.positive( + `Vault ${shortenAddress(deployedVaultAddress)} has been created` + ) + + // Small delay to ensure toast is rendered before state reset + await new Promise(resolve => setTimeout(resolve, DEFAULT_DELAY)) + + // Reset state machine and refetch vaults + resetVault() + await refetchStakingVaults() + + return hash + } catch (error) { + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useExchangeRate.ts b/apps/hub/src/app/_hooks/useExchangeRate.ts index e374c9f60..a59a68441 100644 --- a/apps/hub/src/app/_hooks/useExchangeRate.ts +++ b/apps/hub/src/app/_hooks/useExchangeRate.ts @@ -138,6 +138,7 @@ export function useExchangeRate(options: UseExchangeRateOptions = {}) { refetchInterval, staleTime, retry: 3, - retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), + retryDelay: attemptIndex => + Math.min(1000 * 2 ** attemptIndex, DEFAULT_STALE_TIME), }) } diff --git a/apps/hub/src/app/_hooks/useFaucet.ts b/apps/hub/src/app/_hooks/useFaucet.ts index d6284d091..c23ffa171 100644 --- a/apps/hub/src/app/_hooks/useFaucet.ts +++ b/apps/hub/src/app/_hooks/useFaucet.ts @@ -2,15 +2,13 @@ import { useMutation, type UseMutationResult, useQuery, + useQueryClient, type UseQueryResult, } from '@tanstack/react-query' import { useAccount, useChainId, useConfig, useWriteContract } from 'wagmi' import { readContracts } from 'wagmi/actions' -import { FAUCET } from '../../config' -import { faucetAbi } from '../contracts' - -import type { Address } from 'viem' +import { FAUCET, statusNetworkTestnet } from '~constants/index' // ============================================================================ // Types @@ -32,12 +30,16 @@ export interface FaucetData { * Derived faucet state information */ export interface FaucetState extends FaucetData { - /** Whether the account can request tokens (hasn't hit daily limit) */ - canRequest: boolean + /** Whether the account can claim tokens (hasn't hit daily limit) */ + canClaim: boolean /** Number of requests remaining for the account today */ remainingRequests: bigint - /** Time until the daily limit resets (in seconds) */ - timeUntilReset: number + /** Whether the account has used the faucet today */ + hasUsedFaucet: boolean + /** Amount of tokens actually used today */ + actualUsedToday: bigint + /** Amount of tokens remaining for the account today */ + remainingAmount: bigint } /** @@ -50,6 +52,10 @@ export interface UseFaucetQueryOptions { refetchInterval?: number } +export interface UseFaucetMutationOptions { + amount?: bigint +} + // ============================================================================ // Constants // ============================================================================ @@ -82,29 +88,42 @@ const DEFAULT_REFETCH_INTERVAL = 30_000 // 30 seconds * ``` */ export function useFaucetMutation(): UseMutationResult< - Address, - Error, void, + Error, + UseFaucetMutationOptions, unknown > { const { address } = useAccount() - const { writeContractAsync } = useWriteContract() + const { writeContract } = useWriteContract() + const chainId = useChainId() + const queryClient = useQueryClient() + const { refetch: refetchFaucetQuery } = useFaucetQuery() return useMutation({ mutationKey: [QUERY_KEY_PREFIX, 'request', address], - mutationFn: async (): Promise
    => { + mutationFn: async ({ amount }: UseFaucetMutationOptions) => { if (!address) { throw new Error('Wallet not connected') } - const hash = await writeContractAsync({ + return writeContract({ + chain: statusNetworkTestnet, + account: address, address: FAUCET.address, - abi: faucetAbi, + abi: FAUCET.abi, functionName: 'requestTokens', - args: [address], + args: [amount ?? 0n, address], + }) + }, + onSuccess: () => { + refetchFaucetQuery() + // Invalidate faucet query to refetch updated data + queryClient.invalidateQueries({ + queryKey: [QUERY_KEY_PREFIX, address, chainId], }) - - return hash + }, + onError: error => { + console.error('Failed to request tokens:', error) }, }) } @@ -160,20 +179,20 @@ export function useFaucetQuery( { chainId, address: FAUCET.address, - abi: faucetAbi, + abi: FAUCET.abi, functionName: 'DAILY_LIMIT', }, { chainId, address: FAUCET.address, - abi: faucetAbi, + abi: FAUCET.abi, functionName: 'accountDailyRequests', args: [address], }, { chainId, address: FAUCET.address, - abi: faucetAbi, + abi: FAUCET.abi, functionName: 'accountResetTime', args: [address], }, @@ -203,19 +222,26 @@ export function useFaucetQuery( dailyLimit > accountDailyRequests ? dailyLimit - accountDailyRequests : 0n - const canRequest = remainingRequests > 0n const now = Math.floor(Date.now() / 1000) - const resetTimestamp = Number(accountResetTime) - const timeUntilReset = Math.max(0, resetTimestamp - now) + + // const remainingRequests = dailyLimit > accountDailyRequests + const remainingAmount = + accountResetTime <= now ? dailyLimit : dailyLimit - accountDailyRequests + const actualUsedToday = + accountResetTime <= now ? 0n : accountDailyRequests + const hasUsedFaucet = actualUsedToday > 0n + const canClaim = remainingAmount > 0n return { dailyLimit, accountDailyRequests, accountResetTime, - canRequest, + hasUsedFaucet, + actualUsedToday, + remainingAmount, remainingRequests, - timeUntilReset, + canClaim, } }, enabled: options?.enabled ?? !!address, diff --git a/apps/hub/src/app/_hooks/useLockVault.ts b/apps/hub/src/app/_hooks/useLockVault.ts new file mode 100644 index 000000000..bf2a346d6 --- /dev/null +++ b/apps/hub/src/app/_hooks/useLockVault.ts @@ -0,0 +1,209 @@ +import { useToast } from '@status-im/components' +import { useMutation, type UseMutationResult } from '@tanstack/react-query' +import { type Address, type Hash } from 'viem' +import { useAccount, useConfig, useReadContract, useWriteContract } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import { vaultAbi } from '~constants/contracts' +import { statusNetworkTestnet } from '~constants/index' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useVaultStateContext } from '~hooks/useVaultStateContext' +import { shortenAddress } from '~utils/address' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Parameters for locking a vault + */ +export interface LockVaultParams { + /** + * Increased lock duration in seconds + * The smart contract's _calculateLock handles all calculations: + * - New lock end = max(currentLockEnd, now) + increasedLockSeconds + * - Delta MP calculation + * - Validation (min/max periods, MP overflow) + */ + lockPeriodInSeconds: bigint +} + +/** + * Return type for the useLockVault hook + */ +export type UseLockVaultReturn = UseMutationResult< + Hash, + Error, + LockVaultParams, + unknown +> + +// ============================================================================ +// Constants +// ============================================================================ + +const MUTATION_KEY = 'lock-vault' as const + +const TRANSACTION_CONFIG = { + CONFIRMATION_BLOCKS: 1, +} as const + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Generates a success toast message based on the lock operation performed + * + * @param vaultAddress - The vault address that was locked/extended + * @param wasAlreadyLocked - True if extending an existing lock, false if creating a new lock + * @returns Formatted success message for the toast notification + */ +const formatLockSuccessMessage = ( + vaultAddress: Address, + wasAlreadyLocked: boolean +): string => { + if (wasAlreadyLocked) { + return 'Lock time extended successfully' + } + + return `Vault ${shortenAddress(vaultAddress)} has been locked` +} + +// ============================================================================ +// Mutation Hook +// ============================================================================ + +/** + * Mutation hook to lock or extend a staking vault's lock period + * + * **Locking Process:** + * Locks a vault for a specified duration to increase the boost multiplier. + * The longer the lock period, the higher the multiplier (max 9x). + * + * **How it works:** + * - Always pass the increased lock duration in seconds + * - The smart contract's `_calculateLock` function handles all calculations internally: + * - New lock end = max(currentLockEnd, now) + increasedLockSeconds + * - Delta MP = _bonusMP(balance, increasedLockSeconds) + * - Validation (min/max periods, MP overflow checks) + * + * **Process Flow:** + * 1. Validates wallet connection and lock period + * 2. Sends START_LOCK event to state machine + * 3. Calls `Vault.lock(increasedLockSeconds)` - contract handles the rest + * 4. Waits for transaction confirmation + * 5. Updates state machine and refetches vaults + * 6. Shows success toast notification + * + * @returns Mutation result with mutate function to trigger vault lock + * + * @throws {Error} When wallet is not connected + * @throws {Error} When lock period is invalid (zero or negative) + * @throws {Error} When transaction is reverted + * + * @example + * Locking or extending a vault for 30 days + * ```tsx + * function LockButton({ vaultAddress }: Props) { + * const { mutate: lockVault, isPending } = useLockVault(vaultAddress) + * + * const handleLock = () => { + * const thirtyDaysInSeconds = BigInt(30 * 24 * 60 * 60) + * lockVault({ + * lockPeriodInSeconds: thirtyDaysInSeconds, + * }) + * } + * + * return ( + * + * ) + * } + * ``` + */ +export function useLockVault(vaultAddress: Address): UseLockVaultReturn { + const { address } = useAccount() + const { writeContractAsync } = useWriteContract() + const config = useConfig() + const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() + const { refetch: refetchStakingVaults } = useStakingVaults() + const toast = useToast() + + // Read current lockUntil to determine if vault is already locked + const { data: currentLockUntil } = useReadContract({ + abi: vaultAbi, + address: vaultAddress, + functionName: 'lockUntil', + }) as { data: bigint | undefined } + + return useMutation({ + mutationKey: [MUTATION_KEY, vaultAddress, address], + mutationFn: async ({ + lockPeriodInSeconds, + }: LockVaultParams): Promise => { + // Validate wallet connection + if (!address) { + throw new Error( + 'Wallet not connected. Please connect your wallet first.' + ) + } + + // Validate lock period + if (lockPeriodInSeconds <= 0n) { + throw new Error('Lock period must be greater than 0') + } + + // Notify state machine of lock start + sendVaultEvent({ type: 'START_LOCK' }) + + try { + // Call Vault.lock with the increased lock duration in seconds + // The smart contract's _calculateLock handles all the math + const hash = await writeContractAsync({ + chain: statusNetworkTestnet, + account: address, + address: vaultAddress, + abi: vaultAbi, + functionName: 'lock', + args: [lockPeriodInSeconds], + }) + + // Transaction submitted, notify state machine + sendVaultEvent({ type: 'SIGN' }) + + // Wait for transaction confirmation + const { status } = await waitForTransactionReceipt(config, { + hash, + confirmations: TRANSACTION_CONFIG.CONFIRMATION_BLOCKS, + }) + + // Check for transaction revert + if (status === 'reverted') { + sendVaultEvent({ type: 'REJECT' }) + throw new Error('Transaction was reverted') + } + + // Check if vault was already locked before this transaction + const wasAlreadyLocked = currentLockUntil && currentLockUntil > 0n + + // Show success toast + toast.positive( + formatLockSuccessMessage(vaultAddress, !!wasAlreadyLocked) + ) + + // Reset state machine and refetch vaults + resetVault() + await refetchStakingVaults() + + return hash + } catch (error) { + console.error('Failed to lock vault:', error) + // Transaction failed or user rejected + sendVaultEvent({ type: 'REJECT' }) + throw error + } + }, + }) +} diff --git a/apps/hub/src/app/_hooks/useMultiplierPoints.ts b/apps/hub/src/app/_hooks/useMultiplierPoints.ts new file mode 100644 index 000000000..c8c7acd07 --- /dev/null +++ b/apps/hub/src/app/_hooks/useMultiplierPoints.ts @@ -0,0 +1,217 @@ +import { useQuery, type UseQueryResult } from '@tanstack/react-query' +import { type Address } from 'viem' +import { useAccount, useChainId, useConfig } from 'wagmi' +import { readContract } from 'wagmi/actions' + +import { STAKING_MANAGER } from '~constants/index' +import { type StakingVault, useStakingVaults } from '~hooks/useStakingVaults' + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Result data for multiplier points balance query + */ +export interface MultiplierPointsData { + /** Map of vault addresses to their current MP balances */ + vaultBalances: Record + /** Total uncompounded MP across all vaults */ + totalUncompounded: bigint + /** Total multiplier points redeemed by a user */ + totalMpRedeemed: bigint +} + +/** + * Return type for the useMultiplierPointsBalance query hook + */ +export type UseMultiplierPointsBalanceReturn = + UseQueryResult + +// ============================================================================ +// Constants +// ============================================================================ + +const QUERY_KEYS = { + MULTIPLIER_POINTS: 'multiplier-points-balance', +} as const + +const DEFAULT_MP_DATA: MultiplierPointsData = { + vaultBalances: {}, + totalUncompounded: 0n, + totalMpRedeemed: 0n, +} + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Fetches the multiplier points balance for a specific vault + */ +async function fetchVaultMpBalance( + config: ReturnType, + vaultAddress: Address +): Promise { + const result = (await readContract(config, { + address: STAKING_MANAGER.address, + abi: STAKING_MANAGER.abi, + functionName: 'mpBalanceOf', + args: [vaultAddress], + })) as bigint + + return result +} + +/** + * Calculates total uncompounded MP by comparing vault balances with staked amounts + */ +function calculateTotalUncompounded( + vaultBalances: Record, + vaults: StakingVault[] +): bigint { + return vaults.reduce((total, vault) => { + const currentBalance = vaultBalances[vault.address] ?? 0n + const stakedAmount = vault.data?.mpAccrued ?? 0n + const uncompounded = + currentBalance > stakedAmount ? currentBalance - stakedAmount : 0n + return total + uncompounded + }, 0n) +} + +/** + * Fetches the total multiplier points redeemed by a user + */ +async function fetchMpBalanceOfAccount( + config: ReturnType, + chainId: number, + address: Address +): Promise { + try { + const result = (await readContract(config, { + chainId, + address: STAKING_MANAGER.address, + abi: STAKING_MANAGER.abi, + functionName: 'mpBalanceOfAccount', + args: [address], + })) as bigint + + return result + } catch (error) { + console.error( + `Failed to fetch account MP redeemed for ${address}:`, + error instanceof Error ? error.message : String(error) + ) + return 0n + } +} + +// ============================================================================ +// Query Hook +// ============================================================================ + +/** + * Query hook to fetch multiplier points balances across all vaults + * + * **Data Retrieved:** + * - Individual vault MP balances + * - Total uncompounded MP (difference between current balance and staked amount) + * + * @returns Query result with MP balance data + * + * @example + * Basic usage + * ```tsx + * function MultiplierPointsDisplay() { + * const { data, isLoading, error } = useMultiplierPointsBalance() + * + * if (isLoading) return
    Loading...
    + * if (error) return
    Error: {error.message}
    + * + * return ( + *
    + *

    Total Uncompounded: {data?.totalUncompounded.toString()}

    + *

    Vaults: {Object.keys(data?.vaultBalances ?? {}).length}

    + *
    + * ) + * } + * ``` + * + * @example + * With formatted display + * ```tsx + * function CompoundInfo() { + * const { data } = useMultiplierPointsBalance() + * const hasUncompounded = (data?.totalUncompounded ?? 0n) > 0n + * + * return ( + *
    + * {hasUncompounded ? ( + *

    {formatSNT(data.totalUncompounded)} points ready to compound

    + * ) : ( + *

    No points ready to compound

    + * )} + *
    + * ) + * } + * ``` + */ +export function useMultiplierPointsBalance(): UseMultiplierPointsBalanceReturn { + const { address } = useAccount() + const config = useConfig() + const chainId = useChainId() + const { data: vaults } = useStakingVaults() + + return useQuery({ + queryKey: [QUERY_KEYS.MULTIPLIER_POINTS, address, vaults?.length], + queryFn: async (): Promise => { + if (!vaults || vaults.length === 0 || !address || !chainId) { + return DEFAULT_MP_DATA + } + + try { + const vaultAddresses = vaults.map(vault => vault.address) + + // Fetch all MP balances in parallel + const balances = await Promise.all( + vaultAddresses.map(vaultAddress => + fetchVaultMpBalance(config, vaultAddress) + ) + ) + + // Map addresses to balances + const vaultBalances = vaultAddresses.reduce( + (acc, vaultAddress, index) => { + acc[vaultAddress] = balances[index] + return acc + }, + {} as Record + ) + + const totalMpRedeemed = await fetchMpBalanceOfAccount( + config, + chainId, + address + ) + + // Calculate total uncompounded MPs + const totalUncompounded = calculateTotalUncompounded( + vaultBalances, + vaults + ) + + return { + vaultBalances, + totalUncompounded, + totalMpRedeemed, + } + } catch (error) { + console.error('Failed to fetch multiplier points balances:', error) + return DEFAULT_MP_DATA + } + }, + enabled: !!address && !!vaults && vaults.length > 0, + staleTime: 30_000, // Consider data fresh for 30 seconds + refetchInterval: 60_000, // Refetch every 60 seconds + }) +} diff --git a/apps/hub/src/app/_hooks/useSIWE.ts b/apps/hub/src/app/_hooks/useSIWE.ts index f97e816c2..6d32af77a 100644 --- a/apps/hub/src/app/_hooks/useSIWE.ts +++ b/apps/hub/src/app/_hooks/useSIWE.ts @@ -30,7 +30,7 @@ export function useSiwe() { message: preparedMessage, }) - // Verify the signature (you would typically do this on the backend) + // TODO: Verify the signature (you would typically do this on the backend) // For now, we just return the signature return { message: preparedMessage, diff --git a/apps/hub/src/app/_hooks/useSliderConfig.ts b/apps/hub/src/app/_hooks/useSliderConfig.ts index 75ca179aa..7ff08f2d9 100644 --- a/apps/hub/src/app/_hooks/useSliderConfig.ts +++ b/apps/hub/src/app/_hooks/useSliderConfig.ts @@ -2,8 +2,7 @@ import { useQuery, type UseQueryResult } from '@tanstack/react-query' import { useConfig } from 'wagmi' import { readContract } from 'wagmi/actions' -import { STAKING_MANAGER } from '../../config' -import { stakingManagerAbi } from '../contracts' +import { STAKING_MANAGER } from '~constants/index' // ============================================================================ // Types @@ -93,14 +92,14 @@ export function useSliderConfig(): UseSliderConfigReturn { // Fetch minimum lockup period const minLockupPeriod = await readContract(config, { address: STAKING_MANAGER.address, - abi: stakingManagerAbi, + abi: STAKING_MANAGER.abi, functionName: 'MIN_LOCKUP_PERIOD', }) // Fetch maximum lockup period const maxLockupPeriod = await readContract(config, { address: STAKING_MANAGER.address, - abi: stakingManagerAbi, + abi: STAKING_MANAGER.abi, functionName: 'MAX_LOCKUP_PERIOD', }) diff --git a/apps/hub/src/app/_hooks/useAccountVaults.ts b/apps/hub/src/app/_hooks/useStakingVaults.ts similarity index 56% rename from apps/hub/src/app/_hooks/useAccountVaults.ts rename to apps/hub/src/app/_hooks/useStakingVaults.ts index 446c069f3..933d25cfd 100644 --- a/apps/hub/src/app/_hooks/useAccountVaults.ts +++ b/apps/hub/src/app/_hooks/useStakingVaults.ts @@ -1,22 +1,20 @@ -import { useQuery } from '@tanstack/react-query' +import { useQuery, type UseQueryResult } from '@tanstack/react-query' +import { type Address } from 'viem' import { useAccount, useChainId, useConfig } from 'wagmi' -import { readContract } from 'wagmi/actions' +import { readContract, readContracts } from 'wagmi/actions' -import { STAKING_MANAGER } from '../../config' -import { stakingManagerAbi } from '../contracts' - -import type { UseQueryResult } from '@tanstack/react-query' -import type { Address } from 'viem' +import { vaultAbi } from '~constants/contracts' +import { STAKING_MANAGER } from '~constants/index' // ============================================================================ // Types // ============================================================================ /** - * Vault data structure returned from the StakeManager contract's getVault function + * Vault data structure returned from the StakingManager contract * Maps directly to the VaultData struct in the smart contract */ -export interface VaultData { +export interface StakingVaultData { /** Total amount of tokens staked in the vault */ stakedBalance: bigint /** Current reward index for calculating accrued rewards */ @@ -34,19 +32,19 @@ export interface VaultData { } /** - * Enriched vault data with its contract address + * Vault with its address and associated data */ -export interface VaultWithAddress { +export interface StakingVault { /** The vault contract address */ address: Address /** Vault data from the contract, or null if fetch failed */ - data: VaultData | null + data: StakingVaultData | null } /** - * Configuration options for the useAccountVaults hook + * Configuration options for the useStakingVaults hook */ -export interface UseAccountVaultsOptions { +export interface UseStakingVaultsOptions { /** Whether to enable the query. Defaults to true when wallet is connected */ enabled?: boolean /** Cache duration in milliseconds. Default: 10000ms (10 seconds) */ @@ -56,72 +54,99 @@ export interface UseAccountVaultsOptions { } /** - * Return type for the useAccountVaults hook + * Return type for the useStakingVaults hook */ -export type UseAccountVaultsReturn = UseQueryResult +export type UseStakingVaultsReturn = UseQueryResult // ============================================================================ // Constants // ============================================================================ -const DEFAULT_STALE_TIME = 10_000 // 10 seconds -const QUERY_KEY_PREFIX = 'accountVaults' as const +const QUERY_KEY = 'staking-vaults' as const + +const CACHE_CONFIG = { + DEFAULT_STALE_TIME: 10_000, // 10 seconds +} as const // ============================================================================ // Helper Functions // ============================================================================ /** - * Fetches vault addresses for a given account from the StakeManager contract - * - * @param config - Wagmi configuration - * @param chainId - The chain ID to query - * @param accountAddress - The account address to fetch vaults for - * @returns Array of vault addresses owned by the account + * Fetches vault addresses for a given account from the StakingManager contract */ -async function fetchVaultAddresses( +async function fetchAccountVaultAddresses( config: ReturnType, chainId: number, accountAddress: Address ): Promise { - const addresses = await readContract(config, { + const addresses = (await readContract(config, { chainId, address: STAKING_MANAGER.address, - abi: stakingManagerAbi, + abi: STAKING_MANAGER.abi, functionName: 'getAccountVaults', args: [accountAddress], - }) + })) as Address[] - return addresses as Address[] + return addresses } /** * Fetches vault data for a specific vault address with error handling - * - * @param config - Wagmi configuration - * @param chainId - The chain ID to query - * @param vaultAddress - The vault address to fetch data for - * @returns Vault data if successful, null if the request fails */ async function fetchVaultData( config: ReturnType, chainId: number, vaultAddress: Address -): Promise { +): Promise { try { - const data = await readContract(config, { - chainId, - address: STAKING_MANAGER.address, - abi: stakingManagerAbi, - functionName: 'getVault', - args: [vaultAddress], + const results = await readContracts(config, { + contracts: [ + { + chainId, + address: STAKING_MANAGER.address, + abi: STAKING_MANAGER.abi, + functionName: 'getVault', + args: [vaultAddress], + }, + { + chainId, + address: vaultAddress, + abi: vaultAbi, + functionName: 'lockUntil', + args: [], + }, + ], }) - return data as VaultData + // Check if both contract calls succeeded + const [vaultResult, lockUntilResult] = results + + if ( + vaultResult.status !== 'success' || + lockUntilResult.status !== 'success' + ) { + console.error( + `Failed to fetch vault data for ${vaultAddress}:`, + vaultResult.status !== 'success' + ? vaultResult.error + : lockUntilResult.error + ) + return null + } + + // Extract the actual data from successful results + const vaultData = vaultResult.result as Omit + const lockUntil = lockUntilResult.result as bigint + + return { + ...vaultData, + lockUntil, + } } catch (error) { // Log error for debugging but don't throw - allows partial results console.error( - `[useAccountVaults] Failed to fetch vault data for ${vaultAddress}:`, + `Failed to fetch vault data for ${vaultAddress}:`, error instanceof Error ? error.message : String(error) ) return null @@ -129,18 +154,13 @@ async function fetchVaultData( } /** - * Enriches a vault address with its data, creating a VaultWithAddress object - * - * @param config - Wagmi configuration - * @param chainId - The chain ID to query - * @param vaultAddress - The vault address to enrich - * @returns VaultWithAddress object containing address and data (or null if fetch failed) + * Enriches a vault address with its data */ -async function enrichVaultAddress( +async function enrichVaultWithData( config: ReturnType, chainId: number, vaultAddress: Address -): Promise { +): Promise { const data = await fetchVaultData(config, chainId, vaultAddress) return { address: vaultAddress, data } } @@ -150,10 +170,10 @@ async function enrichVaultAddress( // ============================================================================ /** - * React Query hook to fetch all vaults owned by the connected wallet account + * Query hook to fetch all staking vaults owned by the connected account * * **Features:** - * - Fetches vault addresses from StakeManager contract + * - Fetches vault addresses from StakingManager contract * - Enriches each address with detailed vault data in parallel * - Graceful error handling - failed vaults return with `data: null` * - Automatic refetching and caching via React Query @@ -171,7 +191,7 @@ async function enrichVaultAddress( * Basic usage * ```tsx * function VaultsList() { - * const { data: vaults, isLoading, error } = useAccountVaults() + * const { data: vaults, isLoading, error } = useStakingVaults() * * if (isLoading) return * if (error) return @@ -180,7 +200,7 @@ async function enrichVaultAddress( *
    * {vaults?.map((vault) => * vault.data ? ( - * + * * ) : ( * * ) @@ -193,42 +213,53 @@ async function enrichVaultAddress( * @example * With custom options * ```tsx - * const { data: vaults } = useAccountVaults({ + * const { data: vaults, refetch } = useStakingVaults({ * enabled: isConnected, * staleTime: 30_000, // 30 seconds * refetchInterval: 60_000, // 1 minute * }) * ``` + * + * @example + * Filtering vaults with data + * ```tsx + * const { data: vaults } = useStakingVaults() + * const validVaults = vaults?.filter((v) => v.data !== null) ?? [] + * ``` */ -export function useAccountVaults( - options?: UseAccountVaultsOptions -): UseAccountVaultsReturn { +export function useStakingVaults( + options?: UseStakingVaultsOptions +): UseStakingVaultsReturn { const { address } = useAccount() const config = useConfig() const chainId = useChainId() - return useQuery({ - queryKey: [QUERY_KEY_PREFIX, address, chainId] as const, - queryFn: async (): Promise => { + return useQuery({ + queryKey: [QUERY_KEY, address, chainId], + queryFn: async (): Promise => { // Guard: Return empty array if no wallet is connected if (!address) { return [] } // Step 1: Fetch all vault addresses for the account - const vaultAddresses = await fetchVaultAddresses(config, chainId, address) + const vaultAddresses = await fetchAccountVaultAddresses( + config, + chainId, + address + ) // Step 2: Fetch detailed data for each vault in parallel - const vaultsWithData = await Promise.all( + const vaults = await Promise.all( vaultAddresses.map(vaultAddress => - enrichVaultAddress(config, chainId, vaultAddress) + enrichVaultWithData(config, chainId, vaultAddress) ) ) - return vaultsWithData + return vaults }, enabled: options?.enabled ?? !!address, - staleTime: options?.staleTime ?? DEFAULT_STALE_TIME, + staleTime: options?.staleTime ?? CACHE_CONFIG.DEFAULT_STALE_TIME, refetchInterval: options?.refetchInterval, }) } diff --git a/apps/hub/src/app/_hooks/useTokenApproval.ts b/apps/hub/src/app/_hooks/useTokenApproval.ts deleted file mode 100644 index 2ebda07b5..000000000 --- a/apps/hub/src/app/_hooks/useTokenApproval.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { useMutation } from '@tanstack/react-query' -import { zeroAddress } from 'viem' -import { parseUnits } from 'viem/utils' -import { useAccount, useConfig, useWriteContract } from 'wagmi' -import { waitForTransactionReceipt } from 'wagmi/actions' - -import { SNT_TOKEN } from '../../config' -import { tokenAbi } from '../contracts' -import { useVaultStateContext } from './vault-state-context' - -import type { UseMutationResult } from '@tanstack/react-query' -import type { Address } from 'viem/accounts' - -// ============================================================================ -// Types -// ============================================================================ - -/** - * Parameters for token approval mutation - */ -export interface ApprovalParams { - /** Amount of tokens to approve (in token units, not wei) */ - amount: string - /** Address of the vault to approve tokens for */ - vaultAddress: Address -} - -/** - * Return type for useTokenApproval hook - */ -export type UseTokenApprovalReturn = UseMutationResult< - Address, - Error, - ApprovalParams, - unknown -> - -// ============================================================================ -// Constants -// ============================================================================ - -const MUTATION_KEY_PREFIX = 'token-approval' - -// ============================================================================ -// Hook -// ============================================================================ - -/** - * Hook for approving SNT tokens for a vault - * - * This hook handles the token approval flow: - * 1. Checks current allowance - * 2. If allowance is insufficient, requests approval from user - * 3. Waits for transaction confirmation - * 4. Updates state machine with approval status - * - * @example - * ```tsx - * const { mutate: approveTokens, isPending } = useTokenApproval() - * - * const handleApprove = () => { - * approveTokens({ - * amount: '100', - * vaultAddress: '0x...' - * }) - * } - * ``` - * - * @returns Mutation result with approval function and status - */ -export const useTokenApproval = (): UseTokenApprovalReturn => { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() - const config = useConfig() - const { send: sendVaultEvent } = useVaultStateContext() - - return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, address], - mutationFn: async ({ - amount, - vaultAddress, - }: ApprovalParams): Promise
    => { - if (!address) { - throw new Error( - 'Wallet not connected. Please connect your wallet first.' - ) - } - - if (!vaultAddress || vaultAddress === zeroAddress) { - throw new Error('Invalid vault address provided') - } - - if (!amount || parseFloat(amount) <= 0) { - throw new Error('Amount must be greater than 0') - } - - const amountWei = parseUnits(amount, SNT_TOKEN.decimals) - - sendVaultEvent({ - type: 'START_INCREASE_ALLOWANCE', - amount: amount, - }) - - try { - const hash = await writeContractAsync({ - address: SNT_TOKEN.address, - abi: tokenAbi, - functionName: 'approve', - args: [vaultAddress, amountWei], - }) - - sendVaultEvent({ type: 'SIGN' }) - - const { status } = await waitForTransactionReceipt(config, { - hash, - confirmations: 1, - }) - - if (status === 'reverted') { - throw new Error('Transaction was reverted') - } - - sendVaultEvent({ type: 'COMPLETE', amount }) - - return hash - } catch (error) { - console.error('Failed to approve tokens:', error) - sendVaultEvent({ type: 'REJECT' }) - throw error - } - }, - }) -} diff --git a/apps/hub/src/app/_hooks/useVault.ts b/apps/hub/src/app/_hooks/useVault.ts deleted file mode 100644 index be806942b..000000000 --- a/apps/hub/src/app/_hooks/useVault.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { useToast } from '@status-im/components' -import { useMutation, type UseMutationResult } from '@tanstack/react-query' -import { useAccount, useConfig, useWriteContract } from 'wagmi' -import { waitForTransactionReceipt } from 'wagmi/actions' - -import { VAULT_FACTORY } from '../../config' -import { shortenAddress } from '../../utils/address' -import { vaultFactoryAbi } from '../contracts' -import { useAccountVaults } from './useAccountVaults' -import { useVaultStateContext } from './vault-state-context' - -import type { Address, Hash } from 'viem' - -// ============================================================================ -// Types -// ============================================================================ - -/** - * Return type for the useVaultMutation hook - */ -export type UseVaultMutationReturn = UseMutationResult< - Address, - Error, - void, - unknown -> - -// ============================================================================ -// Constants -// ============================================================================ - -const MUTATION_KEY_PREFIX = 'vault' as const - -// ============================================================================ -// Mutation Hook -// ============================================================================ - -/** - * Mutation hook to create a new vault - * - * Creates a new vault via the VaultFactory contract and manages the state - * machine transitions for the vault creation process. - * - * @returns Mutation result with mutate function to trigger vault creation - * - * @throws {Error} When wallet is not connected - * - * @example - * ```tsx - * function CreateVaultButton() { - * const { mutate: createVault, isPending } = useVaultMutation() - * - * return ( - * - * ) - * } - * ``` - * - * @example - * With success/error handling - * ```tsx - * function CreateVaultManager() { - * const { mutate: createVault, isPending } = useVaultMutation() - * - * const handleCreate = () => { - * createVault(undefined, { - * onSuccess: (txHash) => { - * console.log('Vault created! Transaction:', txHash) - * }, - * onError: (error) => { - * console.error('Failed to create vault:', error) - * }, - * }) - * } - * - * return ( - * - * ) - * } - * ``` - */ -export function useVaultMutation(): UseVaultMutationReturn { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() - const { refetch: refetchAccountVaults } = useAccountVaults() - const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() - const config = useConfig() - const toast = useToast() - - return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, 'create', address], - mutationFn: async (): Promise => { - // Validate wallet connection - if (!address) { - throw new Error( - 'Wallet not connected. Please connect your wallet first.' - ) - } - - // Send state machine event to start vault creation - sendVaultEvent({ type: 'START_CREATE_VAULT' }) - - try { - // Execute vault creation transaction - const hash = await writeContractAsync({ - address: VAULT_FACTORY.address, - abi: vaultFactoryAbi, - functionName: 'createVault', - }) - - // Transaction submitted successfully, transition to processing - sendVaultEvent({ type: 'SIGN' }) - - const { status, logs } = await waitForTransactionReceipt(config, { - hash, - confirmations: 1, - }) - - if (status === 'reverted') { - sendVaultEvent({ type: 'REJECT' }) - throw new Error('Transaction was reverted') - } - - const vaultCreatedLog = logs.find(log => { - // First check if this log is from our contract - if ( - log.address.toLowerCase() !== VAULT_FACTORY.address.toLowerCase() - ) { - return false - } - - // Check if this is the VaultCreated event signature - // This is the actual signature from the contract - return ( - log.topics[0] === - '0x5d9c31ffa0fecffd7cf379989a3c7af252f0335e0d2a1320b55245912c781f53' - ) - }) - - if (!vaultCreatedLog || !vaultCreatedLog.topics[1]) { - console.log('Receipt logs:', logs) - throw new Error('VaultCreated event not found in transaction logs') - } - - // The vault address is the first indexed parameter - // Remove the padding from the address (first 24 bytes) - const paddedAddress = vaultCreatedLog.topics[1] as `0x${string}` - const deployedVaultAddress = `0x${paddedAddress.slice(26)}` as Address - - // Show toast before resetting state - toast.positive( - `Vault ${shortenAddress(deployedVaultAddress)} has been created` - ) - - // Small delay to ensure toast is rendered before state reset - await new Promise(resolve => setTimeout(resolve, 100)) - resetVault() - refetchAccountVaults() - return hash - } catch (error) { - // Transaction failed or user rejected - sendVaultEvent({ type: 'REJECT' }) - throw error - } - }, - }) -} diff --git a/apps/hub/src/app/_hooks/useVaultLock.ts b/apps/hub/src/app/_hooks/useVaultLock.ts deleted file mode 100644 index 2566edfce..000000000 --- a/apps/hub/src/app/_hooks/useVaultLock.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { useMutation, type UseMutationResult } from '@tanstack/react-query' -import { type Address } from 'viem' -import { useAccount, useConfig, useWriteContract } from 'wagmi' -import { waitForTransactionReceipt } from 'wagmi/actions' - -import { statusNetworkTestnet } from '../../config/chain' -import { vaultAbi } from '../contracts' -import { useAccountVaults } from './useAccountVaults' -import { useVaultStateContext } from './vault-state-context' - -// ============================================================================ -// Types -// ============================================================================ - -/** - * Parameters for locking a vault - */ -export interface LockParams { - /** Lock period in seconds */ - lockPeriodInSeconds: bigint - /** Vault address */ - vaultAddress: Address -} - -/** - * Return type for the useVaultLock hook - */ -export type UseVaultLockReturn = UseMutationResult< - Address, - Error, - LockParams, - unknown -> - -// ============================================================================ -// Constants -// ============================================================================ - -const MUTATION_KEY_PREFIX = 'vault-lock' as const -const CONFIRMATION_BLOCKS = 1 - -// ============================================================================ -// Mutation Hook -// ============================================================================ - -/** - * Mutation hook to lock a vault - * - * Locks a vault for a specified period to increase boost multiplier. - * Manages the state machine transitions for the lock process. - * - * @returns Mutation result with mutate function to trigger vault lock - * - * @throws {Error} When wallet is not connected - * @throws {Error} When lock period is invalid - * @throws {Error} When transaction is reverted - * - * @example - * Basic usage - * ```tsx - * function LockButton({ lockPeriod, vaultAddress }: { lockPeriod: bigint, vaultAddress: Address }) { - * const { mutate: lockVault, isPending } = useVaultLock() - * - * return ( - * - * ) - * } - * ``` - * - * @example - * With success/error handling - * ```tsx - * function LockManager() { - * const { mutate: lockVault, isPending } = useVaultLock() - * - * const handleLock = (lockPeriod: bigint, vaultAddress: Address) => { - * lockVault( - * { lockPeriodInSeconds: lockPeriod, vaultAddress }, - * { - * onSuccess: (txHash) => { - * console.log('Locked successfully! Transaction:', txHash) - * }, - * onError: (error) => { - * console.error('Failed to lock:', error) - * }, - * } - * ) - * } - * - * return - * } - * ``` - */ -export function useVaultLock(): UseVaultLockReturn { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() - const config = useConfig() - const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() - const { refetch: refetchAccountVaults } = useAccountVaults() - - return useMutation({ - mutationKey: [MUTATION_KEY_PREFIX, address], - mutationFn: async ({ - lockPeriodInSeconds, - vaultAddress, - }: LockParams): Promise
    => { - // Validate wallet connection - if (!address) { - throw new Error( - 'Wallet not connected. Please connect your wallet first.' - ) - } - - // Validate lock period - if (lockPeriodInSeconds <= 0n) { - throw new Error('Lock period must be greater than 0') - } - - // Send state machine event to start locking - sendVaultEvent({ - type: 'START_LOCK', - }) - - try { - // Execute lock transaction - const hash = await writeContractAsync({ - chain: statusNetworkTestnet, - account: address, - address: vaultAddress, - abi: vaultAbi, - functionName: 'lock', - args: [lockPeriodInSeconds], - }) - - // Transaction submitted successfully, transition to processing - sendVaultEvent({ type: 'SIGN' }) - - // Wait for transaction confirmation - const { status } = await waitForTransactionReceipt(config, { - hash, - confirmations: CONFIRMATION_BLOCKS, - }) - - // Check if transaction was reverted - if (status === 'reverted') { - sendVaultEvent({ type: 'REJECT' }) - throw new Error('Transaction was reverted') - } - - // Transaction successful, close dialog by resetting state - resetVault() - refetchAccountVaults() - return hash - } catch (error) { - // Transaction failed or user rejected - sendVaultEvent({ type: 'REJECT' }) - throw error - } - }, - }) -} diff --git a/apps/hub/src/app/_hooks/vault-state-context.tsx b/apps/hub/src/app/_hooks/useVaultStateContext.tsx similarity index 84% rename from apps/hub/src/app/_hooks/vault-state-context.tsx rename to apps/hub/src/app/_hooks/useVaultStateContext.tsx index 5f5182ff6..4153dbcf4 100644 --- a/apps/hub/src/app/_hooks/vault-state-context.tsx +++ b/apps/hub/src/app/_hooks/useVaultStateContext.tsx @@ -2,9 +2,9 @@ import { createContext, useContext } from 'react' -import { useVaultStateMachine } from './useVaultStateMachine' +import { useVaultStateMachine } from '~hooks/useVaultStateMachine' -import type { VaultEvent, VaultState } from './useVaultStateMachine' +import type { VaultEvent, VaultState } from '~hooks/useVaultStateMachine' type VaultStateContextType = { state: VaultState diff --git a/apps/hub/src/app/_hooks/useVaultStateMachine.ts b/apps/hub/src/app/_hooks/useVaultStateMachine.ts index 066491083..211dc97bc 100644 --- a/apps/hub/src/app/_hooks/useVaultStateMachine.ts +++ b/apps/hub/src/app/_hooks/useVaultStateMachine.ts @@ -19,13 +19,18 @@ export type VaultState = } | { type: 'withdraw' - step: 'initialize' | 'processing' | 'rejected' + step: 'processing' | 'rejected' amount?: string } | { type: 'lock' step: 'initialize' | 'processing' | 'rejected' } + | { + type: 'compound' + step: 'initialize' | 'processing' | 'rejected' + amount?: string + } | { type: 'success' } // Event definitions @@ -35,12 +40,13 @@ export type VaultEvent = | { type: 'START_INCREASE_ALLOWANCE'; amount?: string } | { type: 'SIGN' } | { type: 'REJECT' } - | { type: 'PROCESS' } + | { type: 'PROCESS'; amount?: string } | { type: 'COMPLETE'; amount?: string } | { type: 'READY_TO_STAKE' } | { type: 'START_STAKING'; amount?: string } | { type: 'START_WITHDRAW'; amount?: string } | { type: 'START_LOCK' } + | { type: 'START_COMPOUND'; amount?: string } | { type: 'RESET' } // Transition function @@ -68,6 +74,11 @@ function transition(state: VaultState, event: VaultEvent): VaultState { step: 'initialize', amount: event.amount, })) + .with([{ type: 'idle' }, { type: 'START_COMPOUND' }], ([, event]) => ({ + type: 'compound', + step: 'initialize', + amount: event.amount, + })) // SIWE flow .with([{ type: 'siwe', step: 'initialize' }, { type: 'SIGN' }], () => ({ @@ -215,27 +226,15 @@ function transition(state: VaultState, event: VaultEvent): VaultState { type: 'idle', })) - // Withdraw flow + // Withdraw flow - goes directly to processing when started .with([{ type: 'idle' }, { type: 'START_WITHDRAW' }], ([, event]) => ({ type: 'withdraw', - step: 'initialize', + step: 'processing', amount: event.amount, })) .with( - [{ type: 'withdraw', step: 'initialize' }, { type: 'SIGN' }], - ([state]) => ({ - type: 'withdraw', - step: 'processing', - amount: state.amount, - }) - ) - .with( - [{ type: 'withdraw', step: 'initialize' }, { type: 'REJECT' }], - ([state]) => ({ - type: 'withdraw', - step: 'rejected', - amount: state.amount, - }) + [{ type: 'withdraw', step: 'processing' }, { type: 'RESET' }], + () => ({ type: 'idle' }) ) .with( [{ type: 'withdraw', step: 'processing' }, { type: 'REJECT' }], @@ -245,18 +244,6 @@ function transition(state: VaultState, event: VaultEvent): VaultState { amount: state.amount, }) ) - .with( - [{ type: 'withdraw', step: 'processing' }, { type: 'RESET' }], - () => ({ type: 'idle' }) - ) - .with( - [{ type: 'withdraw', step: 'rejected' }, { type: 'START_WITHDRAW' }], - ([state, event]) => ({ - type: 'withdraw', - step: 'initialize', - amount: event.amount || state.amount, - }) - ) .with( [{ type: 'withdraw', step: 'rejected' }, { type: 'RESET' }], () => ({ type: 'idle' }) @@ -293,6 +280,52 @@ function transition(state: VaultState, event: VaultEvent): VaultState { type: 'idle', })) + // compound flow + .with( + [{ type: 'compound', step: 'initialize' }, { type: 'SIGN' }], + ([state]) => ({ + type: 'compound', + step: 'processing', + amount: state.amount, + }) + ) + .with( + [{ type: 'compound', step: 'initialize' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'compound', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [{ type: 'compound', step: 'processing' }, { type: 'COMPLETE' }], + () => ({ type: 'success' }) + ) + .with( + [{ type: 'compound', step: 'processing' }, { type: 'REJECT' }], + ([state]) => ({ + type: 'compound', + step: 'rejected', + amount: state.amount, + }) + ) + .with( + [{ type: 'compound', step: 'processing' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) + .with( + [{ type: 'compound', step: 'rejected' }, { type: 'START_COMPOUND' }], + ([state, event]) => ({ + type: 'compound', + step: 'initialize', + amount: event.amount || state.amount, + }) + ) + .with( + [{ type: 'compound', step: 'rejected' }, { type: 'RESET' }], + () => ({ type: 'idle' }) + ) + // Reset from success .with([{ type: 'success' }, { type: 'RESET' }], () => ({ type: 'idle' })) diff --git a/apps/hub/src/app/_hooks/useVaultTokenStake.ts b/apps/hub/src/app/_hooks/useVaultTokenStake.ts index f4d14f4fa..654c14775 100644 --- a/apps/hub/src/app/_hooks/useVaultTokenStake.ts +++ b/apps/hub/src/app/_hooks/useVaultTokenStake.ts @@ -3,11 +3,12 @@ import { type Address, formatUnits } from 'viem' import { useAccount, useConfig, useWriteContract } from 'wagmi' import { waitForTransactionReceipt } from 'wagmi/actions' -import { SNT_TOKEN } from '../../config' -import { statusNetworkTestnet } from '../../config/chain' -import { vaultAbi } from '../contracts' -import { useAccountVaults } from './useAccountVaults' -import { useVaultStateContext } from './vault-state-context' +import { vaultAbi } from '~constants/contracts' +import { SNT_TOKEN, statusNetworkTestnet } from '~constants/index' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useVaultStateContext } from '~hooks/useVaultStateContext' + +import { useMultiplierPointsBalance } from './useMultiplierPoints' // ============================================================================ // Types @@ -29,7 +30,7 @@ export interface StakeParams { * Return type for the useVaultStake hook */ export type UseVaultStakeReturn = UseMutationResult< - Address, + void, Error, StakeParams, unknown @@ -103,7 +104,8 @@ export function useVaultTokenStake(): UseVaultStakeReturn { const { writeContractAsync } = useWriteContract() const config = useConfig() const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() - const { refetch: refetchAccountVaults } = useAccountVaults() + const { refetch: refetchStakingVaults } = useStakingVaults() + const { refetch: refetchMultiplierPoints } = useMultiplierPointsBalance() return useMutation({ mutationKey: [MUTATION_KEY_PREFIX, address], @@ -111,7 +113,7 @@ export function useVaultTokenStake(): UseVaultStakeReturn { amountWei, lockPeriod, vaultAddress, - }: StakeParams): Promise
    => { + }: StakeParams): Promise => { // Validate wallet connection if (!address) { throw new Error( @@ -156,10 +158,9 @@ export function useVaultTokenStake(): UseVaultStakeReturn { throw new Error('Transaction was reverted') } - // Transaction successful, close dialog by resetting state + // Transaction successful, refetch data and close dialog + await Promise.all([refetchStakingVaults(), refetchMultiplierPoints()]) resetVault() - refetchAccountVaults() - return hash } catch (error) { // Transaction failed or user rejected sendVaultEvent({ type: 'REJECT' }) diff --git a/apps/hub/src/app/_hooks/useVaultWithdraw.ts b/apps/hub/src/app/_hooks/useVaultWithdraw.ts index 9283ec4df..4a6d9f117 100644 --- a/apps/hub/src/app/_hooks/useVaultWithdraw.ts +++ b/apps/hub/src/app/_hooks/useVaultWithdraw.ts @@ -1,13 +1,13 @@ import { useMutation, type UseMutationResult } from '@tanstack/react-query' -import { type Address, formatUnits } from 'viem' +import { type Address } from 'viem' import { useAccount, useConfig, useWriteContract } from 'wagmi' import { waitForTransactionReceipt } from 'wagmi/actions' -import { SNT_TOKEN } from '../../config' -import { statusNetworkTestnet } from '../../config/chain' -import { vaultAbi } from '../contracts' -import { useAccountVaults } from './useAccountVaults' -import { useVaultStateContext } from './vault-state-context' +import { vaultAbi } from '~constants/contracts' +import { SNT_TOKEN, statusNetworkTestnet } from '~constants/index' +import { useMultiplierPointsBalance } from '~hooks/useMultiplierPoints' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useVaultStateContext } from '~hooks/useVaultStateContext' // ============================================================================ // Types @@ -21,13 +21,15 @@ export interface WithdrawParams { amountWei: bigint /** Vault address */ vaultAddress: Address + /** Optional callback called immediately after user signs transaction */ + onSigned?: () => void } /** * Return type for the useVaultWithdraw hook */ export type UseVaultWithdrawReturn = UseMutationResult< - Address, + void, Error, WithdrawParams, unknown @@ -50,6 +52,15 @@ const CONFIRMATION_BLOCKS = 1 * Withdraws (unstakes) tokens from a vault contract. * Manages the state machine transitions for the withdrawal process. * + * **Process Flow:** + * 1. Validates wallet connection and amount + * 2. Calls vault.withdraw() and waits for user to sign + * 3. After signing, sends START_WITHDRAW event → Goes directly to processing state + * 4. Calls onSigned callback (typically to close modal) + * 5. Waits for transaction confirmation + * 6. On success: Refetches data and resets state machine + * 7. On error: Sends REJECT event → Shows rejected state + * * @returns Mutation result with mutate function to trigger withdrawal * * @throws {Error} When wallet is not connected @@ -57,43 +68,23 @@ const CONFIRMATION_BLOCKS = 1 * @throws {Error} When transaction is reverted * * @example - * Basic usage - * ```tsx - * function WithdrawButton({ amount, vaultAddress }: { amount: bigint, vaultAddress: Address }) { - * const { mutate: withdraw, isPending } = useVaultWithdraw() - * - * return ( - * - * ) - * } - * ``` - * - * @example - * With success/error handling + * Basic usage with modal closing after sign * ```tsx - * function WithdrawManager() { - * const { mutate: withdraw, isPending } = useVaultWithdraw() + * function WithdrawModal({ amount, vaultAddress, onClose }: Props) { + * const { mutate: withdraw } = useVaultWithdraw() * - * const handleWithdraw = (amount: bigint, vaultAddress: Address) => { - * withdraw( - * { amountWei: amount, vaultAddress }, - * { - * onSuccess: (txHash) => { - * console.log('Withdrawn successfully! Transaction:', txHash) - * }, - * onError: (error) => { - * console.error('Failed to withdraw:', error) - * }, + * const handleWithdraw = () => { + * withdraw({ + * amountWei: amount, + * vaultAddress, + * onSigned: () => { + * // Close modal after user signs in wallet + * onClose() * } - * ) + * }) * } * - * return + * return * } * ``` */ @@ -102,14 +93,16 @@ export function useVaultWithdraw(): UseVaultWithdrawReturn { const { writeContractAsync } = useWriteContract() const config = useConfig() const { send: sendVaultEvent, reset: resetVault } = useVaultStateContext() - const { refetch: refetchAccountVaults } = useAccountVaults() + const { refetch: refetchStakingVaults } = useStakingVaults() + const { refetch: refetchMultiplierPoints } = useMultiplierPointsBalance() return useMutation({ mutationKey: [MUTATION_KEY_PREFIX, address], mutationFn: async ({ amountWei, vaultAddress, - }: WithdrawParams): Promise
    => { + onSigned, + }: WithdrawParams): Promise => { // Validate wallet connection if (!address) { throw new Error( @@ -122,12 +115,6 @@ export function useVaultWithdraw(): UseVaultWithdrawReturn { throw new Error('Amount must be greater than 0') } - // Send state machine event to start withdrawal - sendVaultEvent({ - type: 'START_WITHDRAW', - amount: formatUnits(amountWei, SNT_TOKEN.decimals), - }) - try { // Execute withdrawal transaction const hash = await writeContractAsync({ @@ -135,12 +122,18 @@ export function useVaultWithdraw(): UseVaultWithdrawReturn { account: address, address: vaultAddress, abi: vaultAbi, - functionName: 'unstake', - args: [amountWei], + functionName: 'withdraw', + args: [SNT_TOKEN.address, amountWei], + }) + + // Transaction submitted successfully, send START_WITHDRAW event to show processing state + sendVaultEvent({ + type: 'START_WITHDRAW', + amount: formatUnits(amountWei, SNT_TOKEN.decimals), }) - // Transaction submitted successfully, transition to processing - sendVaultEvent({ type: 'SIGN' }) + // Call onSigned callback to close the modal + onSigned?.() // Wait for transaction confirmation const { status } = await waitForTransactionReceipt(config, { @@ -154,10 +147,9 @@ export function useVaultWithdraw(): UseVaultWithdrawReturn { throw new Error('Transaction was reverted') } - // Transaction successful, close dialog by resetting state + // Transaction successful, refetch data and close dialog + await Promise.all([refetchStakingVaults(), refetchMultiplierPoints()]) resetVault() - refetchAccountVaults() - return hash } catch (error) { // Transaction failed or user rejected sendVaultEvent({ type: 'REJECT' }) diff --git a/apps/hub/src/app/_hooks/useWeightedBoost.ts b/apps/hub/src/app/_hooks/useWeightedBoost.ts index 7468a77e1..424bf5ec5 100644 --- a/apps/hub/src/app/_hooks/useWeightedBoost.ts +++ b/apps/hub/src/app/_hooks/useWeightedBoost.ts @@ -2,9 +2,8 @@ import { useMemo } from 'react' import { formatUnits } from 'viem' -import { SNT_TOKEN } from '../../config' - -import type { VaultWithAddress } from './useAccountVaults' +import { SNT_TOKEN } from '~constants/index' +import { type StakingVault } from '~hooks/useStakingVaults' // ============================================================================ // Types @@ -84,7 +83,7 @@ function calculateVaultBoost( * @returns Weighted boost calculation result */ function calculateWeightedBoost( - vaults: VaultWithAddress[], + vaults: StakingVault[], decimals: number ): WeightedBoost { let totalWeightedBoost = 0 @@ -123,7 +122,7 @@ function calculateWeightedBoost( return { value: weightedAverage, - formatted: weightedAverage.toFixed(DEFAULT_DECIMALS), + formatted: `x${weightedAverage.toFixed(DEFAULT_DECIMALS)}`, totalStaked, hasStake: true, } @@ -145,7 +144,7 @@ function calculateWeightedBoost( * @example * ```tsx * function BoostDisplay() { - * const { data: vaults } = useAccountVaults() + * const { data: vaults } = useStakingVaults() * const boost = useWeightedBoost(vaults) * * return ( @@ -157,7 +156,9 @@ function calculateWeightedBoost( * } * ``` */ -export function useWeightedBoost(vaults?: VaultWithAddress[]): WeightedBoost { +export function useWeightedBoost( + vaults: StakingVault[] | undefined +): WeightedBoost { return useMemo(() => { if (!vaults || vaults.length === 0) { return { diff --git a/apps/hub/src/app/contracts/vaultFactoryAbi.ts b/apps/hub/src/app/contracts/vaultFactoryAbi.ts deleted file mode 100644 index 2e1dd3b79..000000000 --- a/apps/hub/src/app/contracts/vaultFactoryAbi.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const vaultFactoryAbi = [ - { - inputs: [], - name: 'createVault', - outputs: [ - { internalType: 'contract StakeVault', name: '', type: 'address' }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/apps/hub/src/app/providers.tsx b/apps/hub/src/app/providers.tsx index eeaffed2a..620268fc7 100644 --- a/apps/hub/src/app/providers.tsx +++ b/apps/hub/src/app/providers.tsx @@ -5,14 +5,15 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ConnectKitProvider } from 'connectkit' import { WagmiProvider } from 'wagmi' -import { statusNetworkTestnet } from '../config/chain' -import { defineWagmiConfig } from '../wagmi' -import { ActionStatusDialog } from './_components/stake/action-status-dialog' -import { useActionStatusContent } from './_components/stake/use-action-status-content' +import { ActionStatusDialog } from '~components/stake/action-status-dialog' +import { useActionStatusContent } from '~components/stake/hooks/use-action-status-content' +import { statusNetworkTestnet } from '~constants/index' import { useVaultStateContext, VaultStateProvider, -} from './_hooks/vault-state-context' +} from '~hooks/useVaultStateContext' + +import { defineWagmiConfig } from '../wagmi' const config = defineWagmiConfig({ chains: [statusNetworkTestnet], @@ -25,9 +26,6 @@ const ProvidersContent = ({ children }: { children: React.ReactNode }) => { const { state: vaultState, reset: resetVault } = useVaultStateContext() const dialogContent = useActionStatusContent(vaultState) - console.log('vaultState', vaultState) - console.log('dialogContent', dialogContent) - return ( <> {children} diff --git a/apps/hub/src/app/stake/page.tsx b/apps/hub/src/app/stake/page.tsx index a6adfba15..d4ce166c2 100644 --- a/apps/hub/src/app/stake/page.tsx +++ b/apps/hub/src/app/stake/page.tsx @@ -16,60 +16,63 @@ import { ConnectKitButton } from 'connectkit' import Image from 'next/image' import { useForm, useWatch } from 'react-hook-form' import { match } from 'ts-pattern' -import { parseUnits } from 'viem' +import { formatUnits, parseUnits } from 'viem' import { useAccount, useBalance, useConfig, useReadContract } from 'wagmi' import { readContract } from 'wagmi/actions' import { z } from 'zod' import { HubLayout } from '~components/hub-layout' - -import { SNT_TOKEN, STAKING_MANAGER } from '../../config' -import { statusNetworkTestnet } from '../../config/chain' -import { formatCurrency, formatSNT } from '../../utils/currency' -import { LaunchIcon, SNTIcon } from '../_components/icons' -import { PromoModal } from '../_components/stake/promo-modal' -import { VaultSelect } from '../_components/vault-select' -import { VaultsTable } from '../_components/vaults/table' -import { useAccountVaults } from '../_hooks/useAccountVaults' -import { useExchangeRate } from '../_hooks/useExchangeRate' -import { useFaucetMutation, useFaucetQuery } from '../_hooks/useFaucet' -import { useTokenApproval } from '../_hooks/useTokenApproval' -import { useVaultMutation } from '../_hooks/useVault' -import { useVaultTokenStake } from '../_hooks/useVaultTokenStake' -import { useWeightedBoost } from '../_hooks/useWeightedBoost' -import { useVaultStateContext } from '../_hooks/vault-state-context' -import { stakingManagerAbi, tokenAbi } from '../contracts' +import { LaunchIcon, SNTIcon } from '~components/icons' +import { PromoModal } from '~components/stake/promo-modal' +import { VaultSelect } from '~components/vault-select' +import { VaultsTable } from '~components/vaults/vaults-table' +import { + SNT_TOKEN, + STAKING_MANAGER, + statusNetworkTestnet, +} from '~constants/index' +import { useApproveToken } from '~hooks/useApproveToken' +import { useCompoundMultiplierPoints } from '~hooks/useCompoundMultiplierPoints' +import { useCreateVault } from '~hooks/useCreateVault' +import { useExchangeRate } from '~hooks/useExchangeRate' +import { useFaucetMutation, useFaucetQuery } from '~hooks/useFaucet' +import { useMultiplierPointsBalance } from '~hooks/useMultiplierPoints' +import { useStakingVaults } from '~hooks/useStakingVaults' +import { useVaultStateContext } from '~hooks/useVaultStateContext' +import { useVaultTokenStake } from '~hooks/useVaultTokenStake' +import { useWeightedBoost } from '~hooks/useWeightedBoost' +import { formatCurrency, formatSNT } from '~utils/currency' import type { Address } from 'viem' -const createSkakeFormSchema = () => { +const createStakeFormSchema = () => { return z.object({ amount: z.string(), vault: z.string(), }) } -type FormValues = z.infer> +type FormValues = z.infer> type ConnectionStatus = 'uninstalled' | 'disconnected' | 'connected' -const TOKEN_TICKER = 'SNT' -// const SUCCESS_TOAST_DURATION_MS = 1500 - export default function StakePage() { - const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) + const [isPromoModalOpen, setIsPromoModalOpen] = useState(false) const { isConnected, address } = useAccount() const config = useConfig() + const { mutate: compoundMultiplierPoints } = useCompoundMultiplierPoints() + const { data: multiplierPointsData } = useMultiplierPointsBalance() - const { mutate: claimTokens } = useFaucetMutation() + const { mutate: claimTokens, isPending: isClaimingTokens } = + useFaucetMutation() const { data: faucetData } = useFaucetQuery() - const { data: vaults } = useAccountVaults() + const { data: vaults, refetch: refetchStakingVaults } = useStakingVaults() const weightedBoost = useWeightedBoost(vaults) const { data: exchangeRate } = useExchangeRate() const form = useForm({ - resolver: zodResolver(createSkakeFormSchema()), + resolver: zodResolver(createStakeFormSchema()), mode: 'onChange', defaultValues: { amount: '', @@ -89,19 +92,12 @@ export default function StakePage() { const { data: totalStaked } = useReadContract({ address: STAKING_MANAGER.address, - abi: stakingManagerAbi, + abi: STAKING_MANAGER.abi, functionName: 'totalStaked', }) as { data: bigint } - const { data: mpBalanceOfAccount } = useReadContract({ - address: STAKING_MANAGER.address, - abi: stakingManagerAbi, - functionName: 'mpBalanceOfAccount', - args: [address], - }) as { data: bigint } - - const { mutate: deployVault } = useVaultMutation() - const { mutate: approveTokens } = useTokenApproval() + const { mutate: createVault } = useCreateVault() + const { mutate: approveToken } = useApproveToken() const { mutate: stakeVault } = useVaultTokenStake() // State machine for vault operations @@ -127,6 +123,38 @@ export default function StakePage() { return 0 }, [amountValue, exchangeRate]) + const { + isDisabled: isDisabledMultiplierPoints, + message: messageMultiplierPoints, + } = useMemo(() => { + const totalUncompounded = multiplierPointsData?.totalUncompounded ?? 0n + const hasUncompoundedPoints = totalUncompounded > 0n + const formattedAmount = formatSNT( + formatUnits(totalUncompounded, SNT_TOKEN.decimals), + { + decimals: SNT_TOKEN.decimals, + } + ) + + return { + isDisabled: !hasUncompoundedPoints || !isConnected, + message: hasUncompoundedPoints + ? `${formattedAmount} points are ready to compound` + : 'No points are ready to compound', + } + }, [multiplierPointsData, isConnected]) + + const hasReachedDailyLimit = useMemo(() => { + if (!faucetData) return false + + const currentTimestamp = BigInt(Math.floor(Date.now() / 1000)) + const isWithinResetWindow = faucetData.accountResetTime > currentTimestamp + const hasExceededLimit = + faucetData.accountDailyRequests >= faucetData.dailyLimit + + return hasExceededLimit && isWithinResetWindow + }, [faucetData]) + const handleSubmit = async (data: FormValues) => { if (!data.amount || !data.vault || !address) return @@ -136,17 +164,17 @@ export default function StakePage() { // Check current allowance const currentAllowance = (await readContract(config, { address: SNT_TOKEN.address, - abi: tokenAbi, + abi: SNT_TOKEN.abi, functionName: 'allowance', args: [address, data.vault as Address], })) as bigint // Transition to increase allowance if not enough allowance if (amountWei >= currentAllowance) { - approveTokens( + approveToken( { amount: data.amount, - vaultAddress: data.vault as Address, + spenderAddress: data.vault as Address, }, { onSuccess: () => { @@ -170,9 +198,11 @@ export default function StakePage() { vaultAddress: data.vault as Address, }) } - } catch (error) { + } catch { sendVaultEvent({ type: 'REJECT' }) - console.error('Failed to check allowance:', error) + } finally { + form.reset() + refetchStakingVaults() } } @@ -230,11 +260,17 @@ export default function StakePage() { {/* @ts-expect-error - TODO: fix this */}
    @@ -270,7 +306,7 @@ export default function StakePage() {
    - {TOKEN_TICKER} + SNT
    @@ -287,7 +323,7 @@ export default function StakePage() { } className="uppercase text-neutral-100" > - {`MAX ${formatSNT(balance?.value ?? 0)} ${TOKEN_TICKER}`} + {`MAX ${formatSNT(balance?.value ?? 0)} SNT`}
    @@ -305,7 +341,7 @@ export default function StakePage() {
    - {TOKEN_TICKER} + SNT
    @@ -389,7 +425,7 @@ export default function StakePage() { // @ts-expect-error - TODO: fix this @@ -408,7 +444,9 @@ export default function StakePage() {
    - {formatSNT(totalStaked ?? 0)} {TOKEN_TICKER} + {formatSNT(totalStaked ?? 0, { + includeSymbol: true, + })}

    @@ -425,22 +463,20 @@ export default function StakePage() {

    - - x{weightedBoost.formatted} + {weightedBoost.formatted}
    - {mpBalanceOfAccount > 0n - ? `${formatSNT(mpBalanceOfAccount)} points are ready to compound` - : 'No points are ready to compound'} + {messageMultiplierPoints} {/* @ts-expect-error - TODO: fix this */} diff --git a/apps/hub/src/config/address.ts b/apps/hub/src/config/address.ts deleted file mode 100644 index 716b1ca52..000000000 --- a/apps/hub/src/config/address.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Address } from 'viem' - -export const STAKING_MANAGER = { - address: '0x785e6c5af58FB26F4a0E43e0cF254af10EaEe0f1' as Address, -} as const - -export const VAULT_FACTORY = { - address: '0xf7b6EC76aCa97b395dc48f7A2861aD810B34b52e' as Address, -} as const - -export const SNT_TOKEN = { - address: '0x1C3Ac2a186c6149Ae7Cb4D716eBbD0766E4f898a' as Address, - name: 'Status Test Token', - symbol: 'STT', - decimals: 18, -} as const - -export const FAUCET = { - address: '0x4Fb609F4a457f47B41D35Dd060447271F000120A' as Address, -} as const diff --git a/apps/hub/src/utils/vault.ts b/apps/hub/src/utils/vault.ts index 842e6668d..f37b4fc5b 100644 --- a/apps/hub/src/utils/vault.ts +++ b/apps/hub/src/utils/vault.ts @@ -1,70 +1,18 @@ import { type Address, formatUnits } from 'viem' -import { SNT_TOKEN } from '../config' +import { SNT_TOKEN } from '~constants/index' -import type { Vault } from '../app/_components/vaults/types' -import type { - VaultData, - VaultWithAddress, -} from '../app/_hooks/useAccountVaults' +import type { StakingVault } from '~hooks/useStakingVaults' /** - * Transforms raw vault data from the contract into the UI-friendly Vault type + * Calculates the boost multiplier for a vault + * + * @param vaults - The list of vaults + * @param vaultAddress - The address of the vault + * @returns The boost multiplier for the vault */ -export const transformVaultData = ( - vaultData: VaultData, - vaultAddress: string, - index: number -): Vault => { - const now = Math.floor(Date.now() / 1000) - const lockUntilTimestamp = Number(vaultData.lockUntil) - const isLocked = lockUntilTimestamp > now - const daysUntilUnlock = isLocked - ? Math.ceil((lockUntilTimestamp - now) / 86400) - : null - - // Calculate boost based on MP ratio - const boost = - vaultData.stakedBalance > 0n - ? Number(vaultData.mpAccrued) / Number(vaultData.stakedBalance) - : 0 - - const potentialBoost = - vaultData.stakedBalance > 0n && vaultData.maxMP > vaultData.mpAccrued - ? Number(vaultData.maxMP - vaultData.mpAccrued) / - Number(vaultData.stakedBalance) - : undefined - - // Calculate karma based on rewards - const karma = Number(vaultData.rewardsAccrued) / 1e18 - - return { - id: `${index}`, - address: vaultAddress as `0x${string}`, - staked: vaultData.stakedBalance, - unlocksIn: daysUntilUnlock, - boost, - potentialBoost, - karma, - locked: isLocked, - } -} - -/** - * Transforms vault data from contract format to UI format - * Filters out vaults with failed data fetches - */ -export const transformVaults = (vaultDataList: VaultWithAddress[]): Vault[] => { - return vaultDataList - .map((vault, index) => { - if (!vault.data) return null - return transformVaultData(vault.data, vault.address, index) - }) - .filter((vault): vault is Vault => vault !== null) -} - export function calculateVaultBoost( - vaults: VaultWithAddress[], + vaults: StakingVault[], vaultAddress: Address ): string { const account = vaults.find(vault => vault.address === vaultAddress) @@ -79,3 +27,29 @@ export function calculateVaultBoost( const boost = vaultMp / vaultStaked + 1 return boost.toFixed(2) } + +/** + * Checks if a vault is locked based on the lockUntil timestamp + * + * @param lockUntil - The lockUntil timestamp in seconds + * @returns True if the vault is locked, false otherwise + */ +export function isVaultLocked(lockUntil: bigint | undefined) { + if (!lockUntil) return false + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(lockUntil) + return lockUntilTimestamp > now +} + +/** + * Calculates the number of days until a vault unlocks + * + * @param lockUntil - The lockUntil timestamp in seconds + * @returns The number of days until the vault unlocks, or null if the vault is not locked + */ +export function calculateDaysUntilUnlock(lockUntil: bigint | undefined) { + if (!lockUntil) return null + const now = Math.floor(Date.now() / 1000) + const lockUntilTimestamp = Number(lockUntil) + return Math.ceil((lockUntilTimestamp - now) / 86400) +} diff --git a/apps/hub/tsconfig.json b/apps/hub/tsconfig.json index 505a9f1b6..aac604240 100644 --- a/apps/hub/tsconfig.json +++ b/apps/hub/tsconfig.json @@ -30,6 +30,9 @@ "baseUrl": ".", "paths": { "~components/*": ["./src/app/_components/*"], + "~hooks/*": ["./src/app/_hooks/*"], + "~constants/*": ["./src/app/_constants/*"], + "~utils/*": ["./src/utils/*"], "~/*": ["./src/*"] } }, diff --git a/packages/components/src/toast/toast.tsx b/packages/components/src/toast/toast.tsx index 2e44675fd..a22f03e14 100644 --- a/packages/components/src/toast/toast.tsx +++ b/packages/components/src/toast/toast.tsx @@ -46,9 +46,11 @@ const Toast = (props: Props, ref: React.Ref) => {
    )} - - - + {onAction && ( + + + + )}
    ) } From b14c3e27cede784604a44e9322943101237476cb Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Thu, 9 Oct 2025 17:59:20 +0100 Subject: [PATCH 16/17] chore: add changeset --- .changeset/pretty-wasps-build.md | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .changeset/pretty-wasps-build.md diff --git a/.changeset/pretty-wasps-build.md b/.changeset/pretty-wasps-build.md new file mode 100644 index 000000000..f53d86883 --- /dev/null +++ b/.changeset/pretty-wasps-build.md @@ -0,0 +1,62 @@ +--- +'@status-im/components': patch +'hub': major +--- + +### Staking & Vault Management System + +- **New Hooks:** + + - Staking functionality with multiplier points tracking + - Vault locking mechanisms + - Token approval and withdrawal flows + - Account vaults management + - Faucet integration for testing + - Emergency mode functionality + +- **User Experience:** + - Lock status indicators in vault selection UI + - Improved table overflow handling + - Enhanced slider configuration for vault lockup periods + +### UI Components + +- **New Modals:** + + - Withdraw modal for vault withdrawals + - Lock configuration modal for vault locking + - Compound modal for compounding rewards + - Promo modal for staking page + - Action status component (formerly progress dialog) + +- **UI Improvements:** + - New icons added + - Enhanced vault table components + - Better modal state management + +### Authentication & Web3 Integration + +- **SIWE Integration:** + - Sign-In With Ethereum (SIWE) for wallet authentication + - ConnectKit integration for wallet connection + - Wagmi configuration and providers setup + +### State Management & Data Fetching + +- **Dependencies Added:** + - `@tanstack/react-query` for server state management + - `react-table` for table data management + - Improved overall state management patterns + +### Emergency Features + +- Emergency mode functionality for vault operations +- Enhanced safety controls for vault lockup periods + +## Refactoring & Maintenance + +### Code Quality Improvements + +- Simplified vault table action handlers +- Removed unnecessary onClick logic for modal state management +- Renamed progress dialog to action status component for clarity From 3f80bdb12f3e06fc8199774560ae656648411ff9 Mon Sep 17 00:00:00 2001 From: nonuwa-oss Date: Fri, 10 Oct 2025 19:28:16 +0100 Subject: [PATCH 17/17] chore: remove unused eslint-disable comments from vault-related components and update button variants for consistency --- .../_components/stake/action-status-dialog.tsx | 1 - .../_components/stake/compound-status-content.tsx | 1 - .../hub/src/app/_components/stake/promo-modal.tsx | 2 -- apps/hub/src/app/_components/vault-select.tsx | 1 - .../vaults/modals/base-vault-modal.tsx | 1 - .../modals/lock-vault-modal/lock-vault-form.tsx | 3 --- .../vaults/modals/withdraw-vault-modal.tsx | 6 +----- .../src/app/_components/vaults/table-columns.tsx | 7 +------ .../src/app/_components/vaults/vaults-table.tsx | 4 +--- .../src/app/_hooks/useCompoundMultiplierPoints.ts | 5 ++++- apps/hub/src/app/stake/page.tsx | 15 ++++----------- .../src/components/button/index.tsx | 3 ++- 12 files changed, 13 insertions(+), 36 deletions(-) diff --git a/apps/hub/src/app/_components/stake/action-status-dialog.tsx b/apps/hub/src/app/_components/stake/action-status-dialog.tsx index 647e2e5ea..f20f46969 100644 --- a/apps/hub/src/app/_components/stake/action-status-dialog.tsx +++ b/apps/hub/src/app/_components/stake/action-status-dialog.tsx @@ -1,4 +1,3 @@ -/* eslint-disable import/no-unresolved */ import * as Dialog from '@radix-ui/react-dialog' import { CloseIcon } from '@status-im/icons/20' import { match } from 'ts-pattern' diff --git a/apps/hub/src/app/_components/stake/compound-status-content.tsx b/apps/hub/src/app/_components/stake/compound-status-content.tsx index 7426020bf..9f6a3c093 100644 --- a/apps/hub/src/app/_components/stake/compound-status-content.tsx +++ b/apps/hub/src/app/_components/stake/compound-status-content.tsx @@ -1,4 +1,3 @@ -/* eslint-disable import/no-unresolved */ import * as Dialog from '@radix-ui/react-dialog' import { InfoIcon } from '@status-im/icons/16' import { formatUnits } from 'viem' diff --git a/apps/hub/src/app/_components/stake/promo-modal.tsx b/apps/hub/src/app/_components/stake/promo-modal.tsx index afff47aaf..932f509b0 100644 --- a/apps/hub/src/app/_components/stake/promo-modal.tsx +++ b/apps/hub/src/app/_components/stake/promo-modal.tsx @@ -1,4 +1,3 @@ -/* eslint-disable import/no-unresolved */ 'use client' import * as Dialog from '@radix-ui/react-dialog' @@ -79,7 +78,6 @@ const PromoModal = (props: Props) => { {({ show, isConnected }) => ( - // @ts-expect-error - TODO: fix this - {/* @ts-expect-error - Button component is not typed */}
    - {/* @ts-expect-error - Button component is not typed */}
    - {/* @ts-expect-error - TODO: fix this */} ) : ( - // @ts-expect-error - TODO: fix this @@ -471,12 +465,11 @@ export default function StakePage() { {messageMultiplierPoints} - {/* @ts-expect-error - TODO: fix this */} @@ -509,7 +502,7 @@ const InfoTooltip = () => ( href="https://status.app/" variant="outline" className="rounded-8 px-2 py-1" - size="24" + size="32" > Learn more diff --git a/packages/status-network/src/components/button/index.tsx b/packages/status-network/src/components/button/index.tsx index 9e9d623c6..0789c5565 100644 --- a/packages/status-network/src/components/button/index.tsx +++ b/packages/status-network/src/components/button/index.tsx @@ -3,7 +3,7 @@ import { forwardRef } from 'react' import { cva, cx } from 'cva' type Props = { - variant?: 'primary' | 'secondary' | 'white' | 'outline' + variant?: 'primary' | 'secondary' | 'white' | 'outline' | 'danger' backdropFilter?: boolean children?: React.ReactNode active?: boolean @@ -24,6 +24,7 @@ const buttonStyles = cva({ 'border-neutral-30 bg-white-100 text-dark-100 hover:border-neutral-40 hover:bg-white-80', outline: 'pressed:border-neutral-50 border border-neutral-30 text-neutral-100 hover:border-neutral-40 disabled:border-neutral-20', + danger: 'border-[transparent]', }, withIcon: { true: '',