Merge branch 'main' into shreyas/neyn-5735-sign-in-connect-farcaster-using-developer-branded-signer

This commit is contained in:
Shreyaschorge
2025-07-11 03:02:00 +05:30
18 changed files with 1196 additions and 801 deletions

View File

@@ -1,4 +1,4 @@
import { notificationDetailsSchema } from "@farcaster/frame-sdk";
import { notificationDetailsSchema } from "@farcaster/miniapp-sdk";
import { NextRequest } from "next/server";
import { z } from "zod";
import { setUserNotificationDetails } from "~/lib/kv";

View File

@@ -2,7 +2,7 @@ import {
ParseWebhookEvent,
parseWebhookEvent,
verifyAppKeyWithNeynar,
} from "@farcaster/frame-node";
} from "@farcaster/miniapp-node";
import { NextRequest } from "next/server";
import { APP_NAME } from "~/lib/constants";
import {

View File

@@ -1,6 +1,6 @@
import React, { createContext, useEffect, useState } from "react";
import dynamic from "next/dynamic";
import { sdk } from '@farcaster/frame-sdk';
import { sdk } from '@farcaster/miniapp-sdk';
const FarcasterSolanaProvider = dynamic(
() => import('@farcaster/mini-app-solana').then(mod => mod.FarcasterSolanaProvider),

View File

@@ -1,7 +1,7 @@
import { createConfig, http, WagmiProvider } from "wagmi";
import { base, degen, mainnet, optimism, unichain, celo } from "wagmi/chains";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { farcasterFrame } from "@farcaster/frame-wagmi-connector";
import { farcasterFrame } from "@farcaster/miniapp-wagmi-connector";
import { coinbaseWallet, metaMask } from 'wagmi/connectors';
import { APP_NAME, APP_ICON_URL, APP_URL } from "~/lib/constants";
import { useEffect, useState } from "react";

View File

@@ -2,7 +2,7 @@
import { useState } from "react";
import { APP_NAME } from "~/lib/constants";
import sdk from "@farcaster/frame-sdk";
import sdk from "@farcaster/miniapp-sdk";
import { useMiniApp } from "@neynar/react";
type HeaderProps = {

View File

@@ -3,7 +3,7 @@
import { useCallback, useState, useEffect } from 'react';
import { Button } from './Button';
import { useMiniApp } from '@neynar/react';
import { type ComposeCast } from "@farcaster/frame-sdk";
import { type ComposeCast } from "@farcaster/miniapp-sdk";
interface EmbedConfig {
path?: string;

View File

@@ -1,11 +1,11 @@
'use client';
import { useCallback, useState } from 'react';
import { useMiniApp } from '@neynar/react';
import { ShareButton } from '../Share';
import { Button } from '../Button';
import { SignIn } from '../wallet/SignIn';
import { type Haptics } from '@farcaster/frame-sdk';
import { useCallback, useState } from "react";
import { useMiniApp } from "@neynar/react";
import { ShareButton } from "../Share";
import { Button } from "../Button";
import { SignIn } from "../wallet/SignIn";
import { type Haptics } from "@farcaster/miniapp-sdk";
import { NeynarAuthButton } from '../NeynarAuthButton/index';
/**

View File

@@ -17,7 +17,7 @@ export function HomeTab() {
<div className="flex items-center justify-center h-[calc(100vh-200px)] px-6">
<div className="text-center w-full max-w-md mx-auto">
<p className="text-lg mb-2">Put your content here!</p>
<p className="text-sm text-gray-500">Powered by Neynar 🪐</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Powered by Neynar 🪐</p>
</div>
</div>
);

View File

@@ -1,10 +1,10 @@
'use client';
import { useCallback, useState } from 'react';
import { signIn, signOut, getCsrfToken } from 'next-auth/react';
import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk';
import { useSession } from 'next-auth/react';
import { Button } from '../Button';
import { useCallback, useState } from "react";
import { signIn, signOut, getCsrfToken } from "next-auth/react";
import sdk, { SignIn as SignInCore } from "@farcaster/miniapp-sdk";
import { useSession } from "next-auth/react";
import { Button } from "../Button";
/**
* SignIn component handles Farcaster authentication using Sign-In with Farcaster (SIWF).
@@ -131,9 +131,9 @@ export function SignIn() {
{/* Session Information */}
{session && (
<div className="my-2 p-2 text-xs overflow-x-scroll rounded-lg font-mono border border-secondary-100">
<div className="font-semibold text-gray-500 mb-1">Session</div>
<div className="whitespace-pre">
<div className="my-2 p-2 text-xs overflow-x-scroll bg-gray-100 dark:bg-gray-900 rounded-lg font-mono">
<div className="font-semibold text-gray-500 dark:text-gray-300 mb-1">Session</div>
<div className="whitespace-pre text-gray-700 dark:text-gray-200">
{JSON.stringify(session, null, 2)}
</div>
</div>
@@ -141,17 +141,17 @@ export function SignIn() {
{/* Error Display */}
{signInFailure && !authState.signingIn && (
<div className="my-2 p-2 text-xs overflow-x-scroll rounded-lg font-mono border border-secondary-100">
<div className="font-semibold text-gray-500 mb-1">SIWF Result</div>
<div className="whitespace-pre">{signInFailure}</div>
<div className="my-2 p-2 text-xs overflow-x-scroll bg-gray-100 dark:bg-gray-900 rounded-lg font-mono">
<div className="font-semibold text-gray-500 dark:text-gray-300 mb-1">SIWF Result</div>
<div className="whitespace-pre text-gray-700 dark:text-gray-200">{signInFailure}</div>
</div>
)}
{/* Success Result Display */}
{signInResult && !authState.signingIn && (
<div className="my-2 p-2 text-xs overflow-x-scroll rounded-lg font-mono border border-secondary-100">
<div className="font-semibold text-gray-500 mb-1">SIWF Result</div>
<div className="whitespace-pre">
<div className="my-2 p-2 text-xs overflow-x-scroll bg-gray-100 dark:bg-gray-900 rounded-lg font-mono">
<div className="font-semibold text-gray-500 dark:text-gray-300 mb-1">SIWF Result</div>
<div className="whitespace-pre text-gray-700 dark:text-gray-200">
{JSON.stringify(signInResult, null, 2)}
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { FrameNotificationDetails } from "@farcaster/frame-sdk";
import { FrameNotificationDetails } from "@farcaster/miniapp-sdk";
import { Redis } from "@upstash/redis";
import { APP_NAME } from "./constants";

View File

@@ -1,7 +1,7 @@
import {
SendNotificationRequest,
sendNotificationResponseSchema,
} from "@farcaster/frame-sdk";
} from "@farcaster/miniapp-sdk";
import { getUserNotificationDetails } from "~/lib/kv";
import { APP_URL } from "./constants";

View File

@@ -1,8 +1,19 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { mnemonicToAccount } from 'viem/accounts';
import { APP_BUTTON_TEXT, APP_DESCRIPTION, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_PRIMARY_CATEGORY, APP_SPLASH_BACKGROUND_COLOR, APP_TAGS, APP_URL, APP_WEBHOOK_URL } from './constants';
import { APP_SPLASH_URL } from './constants';
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { mnemonicToAccount } from "viem/accounts";
import {
APP_BUTTON_TEXT,
APP_DESCRIPTION,
APP_ICON_URL,
APP_NAME,
APP_OG_IMAGE_URL,
APP_PRIMARY_CATEGORY,
APP_SPLASH_BACKGROUND_COLOR,
APP_TAGS,
APP_URL,
APP_WEBHOOK_URL,
} from "./constants";
import { APP_SPLASH_URL } from "./constants";
interface MiniAppMetadata {
version: string;
@@ -17,7 +28,7 @@ interface MiniAppMetadata {
description?: string;
primaryCategory?: string;
tags?: string[];
};
}
interface MiniAppManifest {
accountAssociation?: {
@@ -32,17 +43,6 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function getSecretEnvVars() {
const seedPhrase = process.env.SEED_PHRASE;
const fid = process.env.FID;
if (!seedPhrase || !fid) {
return null;
}
return { seedPhrase, fid };
}
export function getMiniAppEmbedMetadata(ogImageUrl?: string) {
return {
version: "next",
@@ -69,58 +69,30 @@ export async function getFarcasterMetadata(): Promise<MiniAppManifest> {
if (process.env.MINI_APP_METADATA) {
try {
const metadata = JSON.parse(process.env.MINI_APP_METADATA);
console.log('Using pre-signed mini app metadata from environment');
console.log("Using pre-signed mini app metadata from environment");
return metadata;
} catch (error) {
console.warn('Failed to parse MINI_APP_METADATA from environment:', error);
console.warn(
"Failed to parse MINI_APP_METADATA from environment:",
error
);
}
}
if (!APP_URL) {
throw new Error('NEXT_PUBLIC_URL not configured');
throw new Error("NEXT_PUBLIC_URL not configured");
}
// Get the domain from the URL (without https:// prefix)
const domain = new URL(APP_URL).hostname;
console.log('Using domain for manifest:', domain);
const secretEnvVars = getSecretEnvVars();
if (!secretEnvVars) {
console.warn('No seed phrase or FID found in environment variables -- generating unsigned metadata');
}
let accountAssociation;
if (secretEnvVars) {
// Generate account from seed phrase
const account = mnemonicToAccount(secretEnvVars.seedPhrase);
const custodyAddress = account.address;
const header = {
fid: parseInt(secretEnvVars.fid),
type: 'custody',
key: custodyAddress,
};
const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64');
const payload = {
domain
};
const encodedPayload = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64url');
const signature = await account.signMessage({
message: `${encodedHeader}.${encodedPayload}`
});
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
accountAssociation = {
header: encodedHeader,
payload: encodedPayload,
signature: encodedSignature
};
}
console.log("Using domain for manifest:", domain);
return {
accountAssociation,
accountAssociation: {
header: "",
payload: "",
signature: "",
},
frame: {
version: "1",
name: APP_NAME ?? "Neynar Starter Kit",