From 918bfb2354a4368e7daf2d0d519fc306307a4806 Mon Sep 17 00:00:00 2001 From: AngeloAyranji Date: Thu, 26 Feb 2026 13:27:27 +0200 Subject: [PATCH 1/2] fix(nextjs-wagmi, nextjs-core): fix SIWE chainId and ENS profile resolution --- .../components/profile-resolver.tsx | 46 +++----- .../nextjs-core/components/sign-in-button.tsx | 12 +- .../components/profile-resolver.tsx | 42 +++---- .../components/sign-in-button.tsx | 109 +++++++++--------- 4 files changed, 93 insertions(+), 116 deletions(-) diff --git a/examples/nextjs-core/components/profile-resolver.tsx b/examples/nextjs-core/components/profile-resolver.tsx index 04ef23c..6080148 100644 --- a/examples/nextjs-core/components/profile-resolver.tsx +++ b/examples/nextjs-core/components/profile-resolver.tsx @@ -14,15 +14,15 @@ interface ProfileResult { textRecords: TextRecord[]; } -let justaNameInstance: Awaited> | null = null; +let justaNameInstance: ReturnType | null = null; -async function getJustaName() { +function getJustaName() { if (!justaNameInstance) { - justaNameInstance = await JustaName.init({ + justaNameInstance = JustaName.init({ networks: [ { - chainId: 84532, - providerUrl: `https://base-sepolia.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`, + chainId: 1, + providerUrl: `https://eth-mainnet.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`, }, ], }); @@ -49,12 +49,12 @@ export function ProfileResolver() { setProfile(null); try { - const justaName = await getJustaName(); + const justaName = getJustaName(); if (isAddress(trimmed)) { const result = await justaName.subnames.reverseResolve({ address: trimmed as `0x${string}`, - chainId: 84532, + chainId: 1, }); if (!result) { @@ -62,11 +62,7 @@ export function ProfileResolver() { return; } - const resultAny = result as unknown as Record; - const name = - typeof result === "string" - ? result - : resultAny.name ?? resultAny.ens ?? String(result); + const name = result; const records = await justaName.subnames.getRecords({ ens: name }); @@ -186,29 +182,15 @@ export function ProfileResolver() { function extractTextRecords(records: unknown): TextRecord[] { if (!records || typeof records !== "object") return []; - const rec = records as Record; - - if (Array.isArray(rec.texts)) { - return (rec.texts as Array>) + if ( + rec.records && + typeof rec.records === "object" && + Array.isArray((rec.records as Record).texts) + ) { + return ((rec.records as Record).texts as Array>) .filter((t) => t.key && t.value) .map((t) => ({ key: t.key, value: t.value })); } - - if (rec.textRecords && typeof rec.textRecords === "object") { - return Object.entries(rec.textRecords as Record) - .filter(([, v]) => v) - .map(([k, v]) => ({ key: k, value: v })); - } - - if (rec.records && typeof rec.records === "object") { - const inner = rec.records as Record; - if (inner.texts && typeof inner.texts === "object") { - return Object.entries(inner.texts as Record) - .filter(([, v]) => v) - .map(([k, v]) => ({ key: k, value: v })); - } - } - return []; } diff --git a/examples/nextjs-core/components/sign-in-button.tsx b/examples/nextjs-core/components/sign-in-button.tsx index fa2330e..9e655c6 100644 --- a/examples/nextjs-core/components/sign-in-button.tsx +++ b/examples/nextjs-core/components/sign-in-button.tsx @@ -29,10 +29,18 @@ export function SignInButton() { setError(null); setIsConnecting(true); + let nonce: string; try { const nonceRes = await fetch("/api/siwe/nonce"); - const { nonce } = await nonceRes.json(); + const data = await nonceRes.json(); + nonce = data.nonce; + } catch { + setError("Failed to fetch nonce from server."); + setIsConnecting(false); + return; + } + try { // wallet_connect with SIWE capability — must use wallet_connect, not eth_requestAccounts const result = await jaw.provider.request({ method: "wallet_connect", @@ -40,7 +48,7 @@ export function SignInButton() { capabilities: { signInWithEthereum: { nonce, - chainId: "0xaa36a7", + chainId: "0x14a34", domain: window.location.host, uri: window.location.origin, statement: "Sign in to JAW SIWE Demo", diff --git a/examples/nextjs-wagmi/components/profile-resolver.tsx b/examples/nextjs-wagmi/components/profile-resolver.tsx index d3d3927..6b92088 100644 --- a/examples/nextjs-wagmi/components/profile-resolver.tsx +++ b/examples/nextjs-wagmi/components/profile-resolver.tsx @@ -14,15 +14,15 @@ interface ProfileResult { textRecords: TextRecord[]; } -let justaNameInstance: Awaited> | null = null; +let justaNameInstance: ReturnType | null = null; -async function getJustaName() { +function getJustaName() { if (!justaNameInstance) { - justaNameInstance = await JustaName.init({ + justaNameInstance = JustaName.init({ networks: [ { - chainId: 84532, - providerUrl: `https://base-sepolia.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`, + chainId: 1, + providerUrl: `https://eth-mainnet.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`, }, ], }); @@ -49,12 +49,12 @@ export function ProfileResolver() { setProfile(null); try { - const justaName = await getJustaName(); + const justaName = getJustaName(); if (isAddress(trimmed)) { const result = await justaName.subnames.reverseResolve({ address: trimmed, - chainId: 84532, + chainId: 1, }); if (!result) { @@ -62,7 +62,7 @@ export function ProfileResolver() { return; } - const name = typeof result === 'string' ? result : result.name ?? result.ens ?? String(result); + const name = result; const records = await justaName.subnames.getRecords({ ens: name, @@ -186,29 +186,15 @@ export function ProfileResolver() { function extractTextRecords(records: unknown): TextRecord[] { if (!records || typeof records !== 'object') return []; - const rec = records as Record; - - if (Array.isArray(rec.texts)) { - return (rec.texts as Array>) + if ( + rec.records && + typeof rec.records === 'object' && + Array.isArray((rec.records as Record).texts) + ) { + return ((rec.records as Record).texts as Array>) .filter((t) => t.key && t.value) .map((t) => ({ key: t.key, value: t.value })); } - - if (rec.textRecords && typeof rec.textRecords === 'object') { - return Object.entries(rec.textRecords as Record) - .filter(([, v]) => v) - .map(([k, v]) => ({ key: k, value: v })); - } - - if (rec.records && typeof rec.records === 'object') { - const inner = rec.records as Record; - if (inner.texts && typeof inner.texts === 'object') { - return Object.entries(inner.texts as Record) - .filter(([, v]) => v) - .map(([k, v]) => ({ key: k, value: v })); - } - } - return []; } diff --git a/examples/nextjs-wagmi/components/sign-in-button.tsx b/examples/nextjs-wagmi/components/sign-in-button.tsx index b939064..2ba05d2 100644 --- a/examples/nextjs-wagmi/components/sign-in-button.tsx +++ b/examples/nextjs-wagmi/components/sign-in-button.tsx @@ -12,67 +12,68 @@ export function SignInButton() { const [verifiedAddress, setVerifiedAddress] = useState(null); const [error, setError] = useState(null); - const handleSignIn = useCallback(() => { + const handleSignIn = useCallback(async () => { setError(null); - fetch("/api/siwe/nonce") - .then((res) => res.json()) - .then(({ nonce }) => { - connect( - { - connector: config.connectors[0], - capabilities: { - signInWithEthereum: { - nonce, - chainId: "0xaa36a7", - domain: window.location.host, - uri: window.location.origin, - statement: "Sign in to JAW SIWE Demo", - expirationTime: new Date( - Date.now() + 60 * 60 * 1000 - ).toISOString(), - }, - }, - }, - { - onSuccess: async (data) => { - const siweResponse = - data.accounts[0].capabilities?.signInWithEthereum; + let nonce: string; + try { + const res = await fetch("/api/siwe/nonce"); + const data = await res.json(); + nonce = data.nonce; + } catch { + setError("Failed to fetch nonce from server."); + return; + } - if (siweResponse && "message" in siweResponse) { - try { - const verifyRes = await fetch("/api/siwe/verify", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - message: siweResponse.message, - signature: siweResponse.signature, - }), - }); + connect( + { + connector: config.connectors[0], + capabilities: { + signInWithEthereum: { + nonce, + chainId: "0x14a34", + domain: window.location.host, + uri: window.location.origin, + statement: "Sign in to JAW SIWE Demo", + expirationTime: new Date(Date.now() + 60 * 60 * 1000).toISOString(), + }, + }, + }, + { + onSuccess: async (data) => { + const siweResponse = + data.accounts[0].capabilities?.signInWithEthereum; - if (!verifyRes.ok) { - setError("Server rejected the signature."); - return; - } + if (siweResponse && "message" in siweResponse) { + try { + const verifyRes = await fetch("/api/siwe/verify", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + message: siweResponse.message, + signature: siweResponse.signature, + }), + }); - const { address: verified } = await verifyRes.json(); - setVerifiedAddress(verified); - } catch { - setError("Verification request failed."); - } - } else { - setError("SIWE response missing from wallet."); + if (!verifyRes.ok) { + setError("Server rejected the signature."); + return; } - }, - onError: (err) => { - setError(err.message || "Connection failed."); - }, + + const { address: verified } = await verifyRes.json(); + setVerifiedAddress(verified); + } catch { + setError("Verification request failed."); + } + } else { + setError("SIWE response missing from wallet."); } - ); - }) - .catch(() => { - setError("Failed to fetch nonce from server."); - }); + }, + onError: (err) => { + setError(err.message || "Connection failed."); + }, + } + ); }, [connect]); const handleSignOut = useCallback(() => { From 5cb6b5481c7fca5b7295c5dd53d9230f6219677c Mon Sep 17 00:00:00 2001 From: AngeloAyranji Date: Thu, 26 Feb 2026 15:10:08 +0200 Subject: [PATCH 2/2] fix(siwe): disconnect before SIWE connect and gate UI on verifiedAddress --- .../nextjs-core/components/sign-in-button.tsx | 5 +++++ .../nextjs-wagmi/components/sign-in-button.tsx | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/nextjs-core/components/sign-in-button.tsx b/examples/nextjs-core/components/sign-in-button.tsx index 9e655c6..a61b78c 100644 --- a/examples/nextjs-core/components/sign-in-button.tsx +++ b/examples/nextjs-core/components/sign-in-button.tsx @@ -29,6 +29,11 @@ export function SignInButton() { setError(null); setIsConnecting(true); + // Clear any existing JAW session so wallet_connect goes through the + // unauthenticated path and shows the popup with the SIWE request. + // If skipped, a cached session returns cached capabilities (no SIWE data). + try { await jaw.provider.request({ method: "wallet_disconnect" }); } catch { /* ignore */ } + let nonce: string; try { const nonceRes = await fetch("/api/siwe/nonce"); diff --git a/examples/nextjs-wagmi/components/sign-in-button.tsx b/examples/nextjs-wagmi/components/sign-in-button.tsx index 2ba05d2..c8420dd 100644 --- a/examples/nextjs-wagmi/components/sign-in-button.tsx +++ b/examples/nextjs-wagmi/components/sign-in-button.tsx @@ -6,15 +6,22 @@ import { useConnect, useDisconnect } from "@jaw.id/wagmi"; import { config } from "@/lib/config"; export function SignInButton() { - const { address, isConnected } = useAccount(); + const { isConnected } = useAccount(); const { mutate: connect, isPending: isConnecting } = useConnect(); - const { mutate: disconnect, isPending: isDisconnecting } = useDisconnect(); + const { mutate: disconnect, mutateAsync: disconnectAsync, isPending: isDisconnecting } = useDisconnect(); const [verifiedAddress, setVerifiedAddress] = useState(null); const [error, setError] = useState(null); const handleSignIn = useCallback(async () => { setError(null); + // Clear any existing JAW session so wallet_connect goes through the + // unauthenticated path and shows the popup with the SIWE request. + // If skipped, a cached session returns cached capabilities (no SIWE data). + if (isConnected) { + try { await disconnectAsync({}); } catch { /* ignore */ } + } + let nonce: string; try { const res = await fetch("/api/siwe/nonce"); @@ -74,7 +81,7 @@ export function SignInButton() { }, } ); - }, [connect]); + }, [connect, disconnectAsync, isConnected]); const handleSignOut = useCallback(() => { fetch("/api/siwe/logout", { method: "POST" }).then(() => { @@ -84,7 +91,7 @@ export function SignInButton() { }); }, [disconnect]); - if (isConnected && verifiedAddress) { + if (verifiedAddress) { return (