From ace806fcb976422e1172c17dc54c6269246fcfc4 Mon Sep 17 00:00:00 2001 From: lucas-neynar Date: Thu, 13 Mar 2025 15:37:41 -0700 Subject: [PATCH] add icon url to input and fix splash image url default --- bin/index.js | 18 +++++++++-- bin/manifest.js | 77 ------------------------------------------------ src/app/page.tsx | 2 ++ src/lib/utils.ts | 2 +- 4 files changed, 18 insertions(+), 81 deletions(-) delete mode 100644 bin/manifest.js diff --git a/bin/index.js b/bin/index.js index ff3581a..dad1280 100755 --- a/bin/index.js +++ b/bin/index.js @@ -7,7 +7,6 @@ import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; import { mnemonicToAccount } from 'viem/accounts'; -import { generateManifest } from './manifest.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -84,6 +83,12 @@ async function init() { message: 'Enter the URL for your splash image\n(optional -- leave blank to use the default public/splash.png image or replace public/splash.png with your own)\n\nExternal splash image URL:', default: null }, + { + type: 'input', + name: 'iconImageUrl', + message: 'Enter the URL for your app icon\n(optional -- leave blank to use the default public/icon.png image or replace public/icon.png with your own)\n\nExternal app icon URL:', + default: null + }, { type: 'password', name: 'seedPhrase', @@ -180,11 +185,18 @@ async function init() { fs.appendFileSync(envPath, `\nFID="${fid}"`); } - // Append all environment variables + if (answers.splashImageUrl) { + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_SPLASH_IMAGE_URL="${answers.splashImageUrl}"`); + } + + if (answers.iconImageUrl) { + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_ICON_IMAGE_URL="${answers.iconImageUrl}"`); + } + + // Append all remaining environment variables fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`); fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`); fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`); - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_SPLASH_IMAGE_URL="${answers.splashImageUrl}"`); fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${answers.useNeynar ? answers.neynarApiKey : 'FARCASTER_V2_FRAMES_DEMO'}"`); fs.unlinkSync(envExamplePath); diff --git a/bin/manifest.js b/bin/manifest.js deleted file mode 100644 index abef9d6..0000000 --- a/bin/manifest.js +++ /dev/null @@ -1,77 +0,0 @@ -// utils to generate a manifest.json file for a frames v2 app -import { mnemonicToAccount } from 'viem/accounts'; -import dotenv from 'dotenv'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -async function lookupFidByCustodyAddress(custodyAddress, projectPath) { - // Load environment variables from the project's .env file - dotenv.config({ path: join(projectPath, '.env') }); - - const apiKey = process.env.NEYNAR_API_KEY; - if (!apiKey) { - throw new Error('Neynar API key is required. Please set NEYNAR_API_KEY in your .env file'); - } - - const response = await fetch( - `https://api.neynar.com/v2/farcaster/user/custody-address?custody_address=${custodyAddress}`, - { - headers: { - 'accept': 'application/json', - 'x-api-key': apiKey - } - } - ); - - if (!response.ok) { - throw new Error(`Failed to lookup FID: ${response.statusText}`); - } - - const data = await response.json(); - if (!data.user?.fid) { - throw new Error('No FID found for this custody address'); - } - - return data.user.fid; -} - -export async function generateManifest(seedPhrase, projectPath) { - let account; - try { - account = mnemonicToAccount(seedPhrase); - } catch (error) { - throw new Error('Invalid seed phrase'); - } - const custodyAddress = account.address; - - // Look up FID using custody address - const fid = await lookupFidByCustodyAddress(custodyAddress, projectPath); - - const header = { - fid, - type: 'custody', // question: do we want to support type of 'app_key', which indicates the signature is from a registered App Key for the FID - key: custodyAddress, - }; - const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); - - const payload = { - domain: 'warpcast.com' - }; - 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'); - - const jsonJfs = { - header: encodedHeader, - payload: encodedPayload, - signature: encodedSignature - }; - - return jsonJfs; -} diff --git a/src/app/page.tsx b/src/app/page.tsx index 5065aab..9cb8d85 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -7,6 +7,7 @@ const appUrl = process.env.NEXT_PUBLIC_URL; // question: do we need metadata both in this file and in the .well-known/farcaster.json file? const appName = process.env.NEXT_PUBLIC_FRAME_NAME || "Frames v2 Demo"; const splashImageUrl = process.env.NEXT_PUBLIC_FRAME_SPLASH_IMAGE_URL || `${appUrl}/splash.png`; +const iconUrl = process.env.NEXT_PUBLIC_FRAME_ICON_IMAGE_URL || `${appUrl}/icon.png`; const frame = { version: "next", @@ -18,6 +19,7 @@ const frame = { name: appName, url: appUrl, splashImageUrl, + iconUrl, splashBackgroundColor: "#f7f7f7", }, }, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 8498631..848bc55 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -67,7 +67,7 @@ export async function generateFarcasterMetadata() { frame: { version: "1", name: process.env.NEXT_PUBLIC_FRAME_NAME || "Frames v2 Demo", - iconUrl: `${appUrl}/icon.png`, + iconUrl: process.env.NEXT_PUBLIC_FRAME_ICON_IMAGE_URL || `${appUrl}/icon.png`, homeUrl: appUrl, imageUrl: `${appUrl}/opengraph-image`, buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT || "Launch Frame",