From d6c9ceb3a4e5adfbeebf2ea4988e2aa144e57c76 Mon Sep 17 00:00:00 2001 From: Jeremy Lee Date: Fri, 3 Jul 2026 16:36:09 -0400 Subject: [PATCH 1/3] fix: only load funkit checkout on pages with Supply actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The funkit checkout host was mounted app-globally, so the @funkit/connect runtime chunk loaded and ran on every route — including the markets page, where that background activity degraded navigation. Scope the host to the two routes that actually have Supply buttons (dashboard, reserve-overview); other pages (markets, history, etc.) no longer load the SDK at all. Supply clicks fall back to the native supply modal until the checkout is available. Co-Authored-By: Claude Fable 5 --- pages/_app.page.tsx | 11 ----------- pages/dashboard.page.tsx | 13 +++++++++++++ pages/reserve-overview.page.tsx | 10 ++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index 0d86db1a93..f77d74deb4 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -54,16 +54,6 @@ const BridgeModal = dynamic(() => import('src/components/transactions/Bridge/BridgeModal').then((module) => module.BridgeModal) ); -// ssr: false (unlike the other modal hosts) because `@funkit/connect` is a -// client-only, ESM/browser package. -const FunkitCheckout = dynamic( - () => - import('src/components/transactions/FunCheckout/FunkitCheckout').then( - (module) => module.FunkitCheckout - ), - { ssr: false } -); - const BorrowModal = dynamic(() => import('src/components/transactions/Borrow/BorrowModal').then((module) => module.BorrowModal) ); @@ -177,7 +167,6 @@ export default function MyApp(props: MyAppProps) { {getLayout()} - diff --git a/pages/dashboard.page.tsx b/pages/dashboard.page.tsx index 423d673672..5defd83fe3 100644 --- a/pages/dashboard.page.tsx +++ b/pages/dashboard.page.tsx @@ -1,5 +1,6 @@ import { Trans } from '@lingui/macro'; import { Box, Typography } from '@mui/material'; +import dynamic from 'next/dynamic'; import { useEffect, useState } from 'react'; import StyledToggleButton from 'src/components/StyledToggleButton'; import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; @@ -13,6 +14,17 @@ import { useWeb3Context } from '../src/libs/hooks/useWeb3Context'; import { DashboardContentWrapper } from '../src/modules/dashboard/DashboardContentWrapper'; import { DashboardTopPanel } from '../src/modules/dashboard/DashboardTopPanel'; +// funkit checkout host, ssr:false (`@funkit/connect` is client-only). Scoped to +// this page — the only routes with Supply buttons are the dashboard lists and +// reserve-overview — so the SDK chunk never loads on markets/history/etc. +const FunkitCheckout = dynamic( + () => + import('src/components/transactions/FunCheckout/FunkitCheckout').then( + (module) => module.FunkitCheckout + ), + { ssr: false } +); + export default function Dashboard() { const { currentAccount } = useWeb3Context(); const [trackEvent, currentMarket] = useRootStore( @@ -68,6 +80,7 @@ export default function Dashboard() { )} + ); } diff --git a/pages/reserve-overview.page.tsx b/pages/reserve-overview.page.tsx index e6fca4aaf3..bda58112db 100644 --- a/pages/reserve-overview.page.tsx +++ b/pages/reserve-overview.page.tsx @@ -44,6 +44,15 @@ const UnStakeModal = dynamic(() => (module) => module.UnStakeModal ) ); +// funkit checkout host, ssr:false (`@funkit/connect` is client-only). Scoped to +// this page (see dashboard.page.tsx) so the SDK chunk stays off non-supply routes. +const FunkitCheckout = dynamic( + () => + import('src/components/transactions/FunCheckout/FunkitCheckout').then( + (module) => module.FunkitCheckout + ), + { ssr: false } +); export default function ReserveOverview() { const router = useRouter(); @@ -92,6 +101,7 @@ export default function ReserveOverview() { return ( + From a78b775e9d6b3b0f148773b02ea51b03f5dc48c9 Mon Sep 17 00:00:00 2001 From: Jeremy Lee Date: Sat, 4 Jul 2026 14:16:38 -0400 Subject: [PATCH 2/3] fix: enable wagmi ssr mode to stop reconnect firing on every render wagmi's createConfig defaults ssr:false, which makes WagmiProvider re-run reconnect() on every render. For a cold guest this ping-pongs the account status (connecting<->disconnected) and re-renders the provider tree on each navigation, causing slow guest navigation. Setting ssr:true moves reconnect to the guarded once-on-mount path (the standard Next.js wagmi setting) while preserving auto-reconnect for returning wallets. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui-config/wagmiConfig.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui-config/wagmiConfig.ts b/src/ui-config/wagmiConfig.ts index 034580be3d..83f4df5716 100644 --- a/src/ui-config/wagmiConfig.ts +++ b/src/ui-config/wagmiConfig.ts @@ -122,6 +122,11 @@ const connectors = narvalConnector ? [...(baseConnectors ?? []), narvalConnector const prodConfig = createConfig({ ...prodCkConfig, connectors, + // Next.js SSR mode: run wagmi's reconnect once on mount (guarded) instead of + // on every render. Without this, WagmiProvider re-fires reconnect() on each + // render, ping-ponging account status and re-rendering the tree on navigation. + // Auto-reconnect for returning wallets is preserved. + ssr: true, }); const isCypressEnabled = process.env.NEXT_PUBLIC_IS_CYPRESS_ENABLED === 'true'; From d6ad8a92a76f6658937c3bb217d58d9a5f7b0b0c Mon Sep 17 00:00:00 2001 From: Jeremy Lee Date: Sat, 4 Jul 2026 16:50:22 -0400 Subject: [PATCH 3/3] refactor: mount funkit checkout globally in _app (revert page-scoping) The page-scoped dynamic import (loading FunkitCheckout only on the Supply pages) was a workaround for a navigation re-render storm. With `ssr: true` on the wagmi config the guest reconnect no longer churns the wallet status, so funkit's provider subtree no longer thrashes on navigation and the workaround is unnecessary. Mount funkit once globally in _app, matching the standard integration pattern. Co-Authored-By: Claude Opus 4.8 (1M context) --- pages/_app.page.tsx | 12 ++++++++++++ pages/dashboard.page.tsx | 13 ------------- pages/reserve-overview.page.tsx | 10 ---------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index f77d74deb4..63e429f2ee 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -74,6 +74,17 @@ const RepayModal = dynamic(() => const SupplyModal = dynamic(() => import('src/components/transactions/Supply/SupplyModal').then((module) => module.SupplyModal) ); +// funkit checkout host, mounted once globally alongside the other modal hosts as an +// `ssr: false` island (`@funkit/connect` is client-only). Reuses the interface's +// wagmi + react-query. With `ssr: true` on the wagmi config the guest reconnect no +// longer churns, so a single global mount doesn't thrash — no page-scoping needed. +const FunkitCheckout = dynamic( + () => + import('src/components/transactions/FunCheckout/FunkitCheckout').then( + (module) => module.FunkitCheckout + ), + { ssr: false } +); const WithdrawModal = dynamic(() => import('src/components/transactions/Withdraw/WithdrawModal').then( (module) => module.WithdrawModal @@ -166,6 +177,7 @@ export default function MyApp(props: MyAppProps) { {getLayout()} + diff --git a/pages/dashboard.page.tsx b/pages/dashboard.page.tsx index 5defd83fe3..423d673672 100644 --- a/pages/dashboard.page.tsx +++ b/pages/dashboard.page.tsx @@ -1,6 +1,5 @@ import { Trans } from '@lingui/macro'; import { Box, Typography } from '@mui/material'; -import dynamic from 'next/dynamic'; import { useEffect, useState } from 'react'; import StyledToggleButton from 'src/components/StyledToggleButton'; import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup'; @@ -14,17 +13,6 @@ import { useWeb3Context } from '../src/libs/hooks/useWeb3Context'; import { DashboardContentWrapper } from '../src/modules/dashboard/DashboardContentWrapper'; import { DashboardTopPanel } from '../src/modules/dashboard/DashboardTopPanel'; -// funkit checkout host, ssr:false (`@funkit/connect` is client-only). Scoped to -// this page — the only routes with Supply buttons are the dashboard lists and -// reserve-overview — so the SDK chunk never loads on markets/history/etc. -const FunkitCheckout = dynamic( - () => - import('src/components/transactions/FunCheckout/FunkitCheckout').then( - (module) => module.FunkitCheckout - ), - { ssr: false } -); - export default function Dashboard() { const { currentAccount } = useWeb3Context(); const [trackEvent, currentMarket] = useRootStore( @@ -80,7 +68,6 @@ export default function Dashboard() { )} - ); } diff --git a/pages/reserve-overview.page.tsx b/pages/reserve-overview.page.tsx index bda58112db..e6fca4aaf3 100644 --- a/pages/reserve-overview.page.tsx +++ b/pages/reserve-overview.page.tsx @@ -44,15 +44,6 @@ const UnStakeModal = dynamic(() => (module) => module.UnStakeModal ) ); -// funkit checkout host, ssr:false (`@funkit/connect` is client-only). Scoped to -// this page (see dashboard.page.tsx) so the SDK chunk stays off non-supply routes. -const FunkitCheckout = dynamic( - () => - import('src/components/transactions/FunCheckout/FunkitCheckout').then( - (module) => module.FunkitCheckout - ), - { ssr: false } -); export default function ReserveOverview() { const router = useRouter(); @@ -101,7 +92,6 @@ export default function ReserveOverview() { return ( -