From 3287b1288fae588f16061650553d0e07756a5b3e Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Sun, 15 Mar 2026 10:25:00 +0000 Subject: [PATCH 1/2] [#88] Replace recoverMessageAddress with verifyMessage for contract wallet support Uses publicClient.verifyMessage() which handles both EOA and EIP-1271 contract wallet signatures, enabling Smart contract wallet users (Safe, Argent, etc.) to submit ratings. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/api/ratings/route.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/api/ratings/route.ts b/src/app/api/ratings/route.ts index 44b320e9..311085e2 100644 --- a/src/app/api/ratings/route.ts +++ b/src/app/api/ratings/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; -import { recoverMessageAddress, type Address } from "viem"; +import { type Address } from "viem"; import { publicClient } from "../../../../lib/rpc"; import { createServerClient, supabase } from "../../../../lib/supabase"; import { erc20Abi } from "../../../../lib/price"; @@ -52,6 +52,7 @@ interface RatingBody { storylineId: number; rating: number; comment?: string; + address: string; signature: string; message: string; } @@ -64,7 +65,7 @@ export async function POST(req: NextRequest) { return error("Invalid JSON body"); } - const { storylineId, rating, comment, signature, message } = body; + const { storylineId, rating, comment, address, signature, message } = body; // Validate inputs if (!storylineId || typeof storylineId !== "number") { @@ -73,8 +74,8 @@ export async function POST(req: NextRequest) { if (!rating || typeof rating !== "number" || !Number.isInteger(rating) || rating < 1 || rating > 5) { return error("Rating must be an integer between 1 and 5"); } - if (!signature || !message) { - return error("Missing signature or message"); + if (!address || !signature || !message) { + return error("Missing address, signature, or message"); } // Validate signed message binds to this specific action @@ -85,13 +86,17 @@ export async function POST(req: NextRequest) { ); } - // 1. Recover rater address from signature - let raterAddress: Address; + // 1. Verify signature (supports both EOA and EIP-1271 contract wallets) + const raterAddress = address as Address; try { - raterAddress = await recoverMessageAddress({ + const valid = await publicClient.verifyMessage({ + address: raterAddress, message, signature: signature as `0x${string}`, }); + if (!valid) { + return error("Invalid signature"); + } } catch { return error("Failed to verify signature"); } From 0fba8822b0e5d3aa3113a6c7c0bbd88a9a5dfaa6 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Sun, 15 Mar 2026 10:26:57 +0000 Subject: [PATCH 2/2] [#88] Include address in RatingWidget API request body Fixes client-side regression: the caller must now send the wallet address so the API can use verifyMessage for EIP-1271 support. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/RatingWidget.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/RatingWidget.tsx b/src/components/RatingWidget.tsx index 65a9826a..67fc668c 100644 --- a/src/components/RatingWidget.tsx +++ b/src/components/RatingWidget.tsx @@ -121,6 +121,7 @@ export function RatingWidget({ storylineId, tokenAddress }: RatingWidgetProps) { storylineId, rating: selectedRating, comment: comment || undefined, + address, signature, message, }),