From 1d5a8270492579ec40a073d1864c9bf56030e955 Mon Sep 17 00:00:00 2001 From: lucas-neynar Date: Fri, 21 Mar 2025 15:12:53 -0700 Subject: [PATCH] fix: add fid to header --- package.json | 2 +- scripts/build.js | 34 ++++++++++++++++++++++++++++++++-- scripts/deploy.js | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 08a9617..c59af18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-neynar-farcaster-frame", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "files": [ "bin/index.js" diff --git a/scripts/build.js b/scripts/build.js index b787721..256fbcf 100755 --- a/scripts/build.js +++ b/scripts/build.js @@ -11,6 +11,33 @@ import crypto from 'crypto'; // First load .env for main config dotenv.config({ path: '.env' }); +async function lookupFidByCustodyAddress(custodyAddress, apiKey) { + if (!apiKey) { + throw new Error('Neynar API key is required'); + } + + 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; +} + async function loadEnvLocal() { try { if (fs.existsSync('.env.local')) { @@ -109,10 +136,11 @@ async function validateSeedPhrase(seedPhrase) { } } -async function generateFarcasterMetadata(domain, accountAddress, seedPhrase, webhookUrl) { +async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl) { const header = { type: 'custody', key: accountAddress, + fid, }; const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); @@ -285,6 +313,8 @@ async function main() { const accountAddress = await validateSeedPhrase(seedPhrase); console.log('✅ Generated account address from seed phrase'); + const fid = await lookupFidByCustodyAddress(accountAddress, neynarApiKey ?? 'FARCASTER_V2_FRAMES_DEMO'); + // Generate and sign manifest console.log('\n🔨 Generating frame manifest...'); @@ -293,7 +323,7 @@ async function main() { ? `https://api.neynar.com/f/app/${neynarClientId}/event` : `${domain}/api/webhook`; - const metadata = await generateFarcasterMetadata(domain, accountAddress, seedPhrase, webhookUrl); + const metadata = await generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl); console.log('\n✅ Frame manifest generated' + (seedPhrase ? ' and signed' : '')); // Read existing .env file or create new one diff --git a/scripts/deploy.js b/scripts/deploy.js index 039963c..f44f2d0 100755 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -24,11 +24,39 @@ async function validateSeedPhrase(seedPhrase) { } } -async function generateFarcasterMetadata(domain, accountAddress, seedPhrase, webhookUrl) { +async function lookupFidByCustodyAddress(custodyAddress, apiKey) { + if (!apiKey) { + throw new Error('Neynar API key is required'); + } + + 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; +} + +async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl) { const trimmedDomain = domain.trim(); const header = { type: 'custody', key: accountAddress, + fid, }; const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); @@ -390,16 +418,18 @@ async function deployToVercel(useGitHub = false) { // Generate frame metadata if we have a seed phrase let frameMetadata; + let fid; if (process.env.SEED_PHRASE) { console.log('\n🔨 Generating frame metadata...'); const accountAddress = await validateSeedPhrase(process.env.SEED_PHRASE); + fid = await lookupFidByCustodyAddress(accountAddress, process.env.NEYNAR_API_KEY ?? 'FARCASTER_V2_FRAMES_DEMO'); // Determine webhook URL based on Neynar configuration const webhookUrl = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` : `https://${domain}/api/webhook`; - frameMetadata = await generateFarcasterMetadata(domain, accountAddress, process.env.SEED_PHRASE, webhookUrl); + frameMetadata = await generateFarcasterMetadata(domain, fid, accountAddress, process.env.SEED_PHRASE, webhookUrl); console.log('✅ Frame metadata generated and signed'); } @@ -493,7 +523,7 @@ async function deployToVercel(useGitHub = false) { : `https://${actualDomain}/api/webhook`; if (frameMetadata) { - frameMetadata = await generateFarcasterMetadata(actualDomain, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl); + frameMetadata = await generateFarcasterMetadata(actualDomain, fid, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl); // Update FRAME_METADATA env var using the new function await setVercelEnvVar('FRAME_METADATA', frameMetadata, projectRoot); }