From 5c281be55a705f7c71cc71deda740f934d07bf93 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Sat, 14 Mar 2026 09:12:33 +0000 Subject: [PATCH 1/2] [#199] Add wallet connection with wagmi, react-query, and ConnectWallet component - Install wagmi and @tanstack/react-query - Create lib/wagmi.ts config for Base + Base Sepolia with injected connector - Create Providers component wrapping WagmiProvider + QueryClientProvider - Wrap root layout with Providers for app-wide wallet state - Create ConnectWallet component with terminal aesthetic styling: truncated address display, disconnect button, hover states - Provider structure is extensible for future Farcaster wallet support (P7-2) Fixes #199 Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/wagmi.ts | 21 ++++ package-lock.json | 195 ++++++++++++++++++++++++++++++- package.json | 4 +- src/app/layout.tsx | 5 +- src/app/providers.tsx | 16 +++ src/components/ConnectWallet.tsx | 40 +++++++ 6 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 lib/wagmi.ts create mode 100644 src/app/providers.tsx create mode 100644 src/components/ConnectWallet.tsx diff --git a/lib/wagmi.ts b/lib/wagmi.ts new file mode 100644 index 00000000..b8bfafb2 --- /dev/null +++ b/lib/wagmi.ts @@ -0,0 +1,21 @@ +import { http, createConfig } from "wagmi"; +import { base, baseSepolia } from "wagmi/chains"; +import { injected } from "wagmi/connectors"; + +const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL || undefined; + +export const config = createConfig({ + chains: [base, baseSepolia], + connectors: [injected()], + transports: { + [base.id]: http(rpcUrl), + [baseSepolia.id]: http(rpcUrl), + }, + ssr: true, +}); + +declare module "wagmi" { + interface Register { + config: typeof config; + } +} diff --git a/package-lock.json b/package-lock.json index 7fa246b5..88d3390c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "dependencies": { "@aws-sdk/client-s3": "^3.1009.0", "@supabase/supabase-js": "^2.99.1", + "@tanstack/react-query": "^5.90.21", "next": "16.1.6", "react": "19.2.3", "react-dom": "19.2.3", - "viem": "^2.47.2" + "viem": "^2.47.2", + "wagmi": "^3.5.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -4054,6 +4056,32 @@ "tailwindcss": "4.2.1" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.21", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.21.tgz", + "integrity": "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -4123,7 +4151,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -4827,6 +4855,37 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@wagmi/core": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-3.4.0.tgz", + "integrity": "sha512-EU5gDsUp5t7+cuLv12/L8hfyWfCIKsBNiiBqpOqxZJxvAcAiQk4xFe2jMgaQPqApc3Omvxrk032M8AQ4N0cQeg==", + "license": "MIT", + "dependencies": { + "eventemitter3": "5.0.1", + "mipd": "0.0.7", + "zustand": "5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@tanstack/query-core": ">=5.0.0", + "ox": ">=0.11.1", + "typescript": ">=5.7.3", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "@tanstack/query-core": { + "optional": true + }, + "ox": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/abitype": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", @@ -5420,7 +5479,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -7863,6 +7922,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mipd": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mipd/-/mipd-0.0.7.tgz", + "integrity": "sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9561,6 +9640,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/viem": { "version": "2.47.2", "resolved": "https://registry.npmjs.org/viem/-/viem-2.47.2.tgz", @@ -9827,6 +9915,78 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/wagmi": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-3.5.0.tgz", + "integrity": "sha512-39uiY6Vkc28NiAHrxJzVTodoRgSVGG97EewwUxRf+jcFMTe8toAnaM8pJZA3Zw/6snMg4tSgWLJAtMnOacLe7w==", + "license": "MIT", + "dependencies": { + "@wagmi/connectors": "7.2.1", + "@wagmi/core": "3.4.0", + "use-sync-external-store": "1.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@tanstack/react-query": ">=5.0.0", + "react": ">=18", + "typescript": ">=5.7.3", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/wagmi/node_modules/@wagmi/connectors": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-7.2.1.tgz", + "integrity": "sha512-/tyDepUMDM8eNzNX3ofjqHNRFZ6XcZ3u0+cQp5x0/LHCpMA8tRh7A1/e7dTrYiIJeL7iLgHzfHUXCsU02OKMLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@base-org/account": "^2.5.1", + "@coinbase/wallet-sdk": "^4.3.6", + "@metamask/sdk": "~0.33.1", + "@safe-global/safe-apps-provider": "~0.18.6", + "@safe-global/safe-apps-sdk": "^9.1.0", + "@wagmi/core": "3.4.0", + "@walletconnect/ethereum-provider": "^2.21.1", + "porto": "~0.2.35", + "typescript": ">=5.7.3", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "@base-org/account": { + "optional": true + }, + "@coinbase/wallet-sdk": { + "optional": true + }, + "@metamask/sdk": { + "optional": true + }, + "@safe-global/safe-apps-provider": { + "optional": true + }, + "@safe-global/safe-apps-sdk": { + "optional": true + }, + "@walletconnect/ethereum-provider": { + "optional": true + }, + "porto": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10022,6 +10182,35 @@ "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } + }, + "node_modules/zustand": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0.tgz", + "integrity": "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 2170629a..d7ec715d 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ "dependencies": { "@aws-sdk/client-s3": "^3.1009.0", "@supabase/supabase-js": "^2.99.1", + "@tanstack/react-query": "^5.90.21", "next": "16.1.6", "react": "19.2.3", "react-dom": "19.2.3", - "viem": "^2.47.2" + "viem": "^2.47.2", + "wagmi": "^3.5.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7873e94d..95572820 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Geist_Mono } from "next/font/google"; +import { Providers } from "./providers"; import "./globals.css"; const geistMono = Geist_Mono({ @@ -19,7 +20,9 @@ export default function RootLayout({ }>) { return ( - {children} + + {children} + ); } diff --git a/src/app/providers.tsx b/src/app/providers.tsx new file mode 100644 index 00000000..987253c0 --- /dev/null +++ b/src/app/providers.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { WagmiProvider } from "wagmi"; +import { config } from "../../lib/wagmi"; +import { useState } from "react"; + +export function Providers({ children }: { children: React.ReactNode }) { + const [queryClient] = useState(() => new QueryClient()); + + return ( + + {children} + + ); +} diff --git a/src/components/ConnectWallet.tsx b/src/components/ConnectWallet.tsx new file mode 100644 index 00000000..d122e07d --- /dev/null +++ b/src/components/ConnectWallet.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useAccount, useConnect, useDisconnect } from "wagmi"; +import { injected } from "wagmi/connectors"; + +function truncateAddress(address: string): string { + return `${address.slice(0, 6)}...${address.slice(-4)}`; +} + +export function ConnectWallet() { + const { address, isConnected } = useAccount(); + const { connect, isPending } = useConnect(); + const { disconnect } = useDisconnect(); + + if (isConnected && address) { + return ( +
+ + {truncateAddress(address)} + + +
+ ); + } + + return ( + + ); +} From 161240106342779c6e7cc24bb63af57d57cd7e81 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Sat, 14 Mar 2026 09:14:10 +0000 Subject: [PATCH 2/2] [#199] Wire ConnectWallet into home page and fix transport config - Render ConnectWallet in page header so users can actually connect - Split NEXT_PUBLIC_RPC_URL to only apply to the active chain; non-active chain falls back to its default public RPC Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/wagmi.ts | 14 ++++++++++---- src/app/page.tsx | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/wagmi.ts b/lib/wagmi.ts index b8bfafb2..d2cfc4f7 100644 --- a/lib/wagmi.ts +++ b/lib/wagmi.ts @@ -2,14 +2,20 @@ import { http, createConfig } from "wagmi"; import { base, baseSepolia } from "wagmi/chains"; import { injected } from "wagmi/connectors"; -const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL || undefined; - export const config = createConfig({ chains: [base, baseSepolia], connectors: [injected()], transports: { - [base.id]: http(rpcUrl), - [baseSepolia.id]: http(rpcUrl), + [base.id]: http( + process.env.NEXT_PUBLIC_CHAIN_ID === "8453" + ? process.env.NEXT_PUBLIC_RPC_URL + : undefined, + ), + [baseSepolia.id]: http( + process.env.NEXT_PUBLIC_CHAIN_ID !== "8453" + ? process.env.NEXT_PUBLIC_RPC_URL + : undefined, + ), }, ssr: true, }); diff --git a/src/app/page.tsx b/src/app/page.tsx index 6baef83b..386bf140 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,11 @@ +import { ConnectWallet } from "@/components/ConnectWallet"; + export default function Home() { return (
+
+ +

PlotLink