From d1ec161f47f22764e0b202a073d6918cf2c2694f Mon Sep 17 00:00:00 2001 From: veganbeef Date: Fri, 18 Jul 2025 17:29:51 -0700 Subject: [PATCH] fix: sponsor signer depends on seed phrase --- bin/init.js | 89 +++++++++++++++++++++++++++++++---------------- package.json | 2 +- scripts/deploy.ts | 28 +++++++-------- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/bin/init.js b/bin/init.js index ce11093..91b2fb4 100644 --- a/bin/init.js +++ b/bin/init.js @@ -219,6 +219,22 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe let answers; if (autoAcceptDefaults) { + // Handle SIWN logic for autoAcceptDefaults + let seedPhraseValue = null; + let useSponsoredSignerValue = false; + + // Only set seed phrase and sponsored signer if explicitly provided via flags + if (seedPhrase) { + // Validate the provided seed phrase + if (!seedPhrase || seedPhrase.trim().split(' ').length < 12) { + console.error('Error: Seed phrase must be at least 12 words'); + process.exit(1); + } + seedPhraseValue = seedPhrase; + // If sponsoredSigner flag is provided, enable it; otherwise default to false + useSponsoredSignerValue = sponsoredSigner; + } + answers = { projectName: projectName || defaultMiniAppName || 'my-farcaster-mini-app', description: 'A Farcaster mini app created with Neynar', @@ -228,8 +244,8 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe useWallet: !noWallet, useTunnel: true, enableAnalytics: true, - seedPhrase: null, - useSponsoredSigner: false, + seedPhrase: seedPhraseValue, + useSponsoredSigner: useSponsoredSignerValue, }; } else { // If autoAcceptDefaults is false but we have a projectName, we still need to ask for other options @@ -353,39 +369,34 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe answers.useTunnel = hostingAnswer.useTunnel; } - // Ask about Neynar Sponsored Signers / SIWN - if (sponsoredSigner) { - answers.useSponsoredSigner = true; - if (seedPhrase) { - // Validate the provided seed phrase - if (!seedPhrase || seedPhrase.trim().split(' ').length < 12) { - console.error('Error: Seed phrase must be at least 12 words'); - process.exit(1); - } - answers.seedPhrase = seedPhrase; - } else { - console.error('Error: --sponsored-signer requires --seed-phrase to be provided'); + // Ask about Sign In With Neynar (SIWN) - requires seed phrase + if (seedPhrase) { + // If --seed-phrase flag is used, validate it + if (!seedPhrase || seedPhrase.trim().split(' ').length < 12) { + console.error('Error: Seed phrase must be at least 12 words'); process.exit(1); } + answers.seedPhrase = seedPhrase; + // If --sponsored-signer flag is also provided, enable it + answers.useSponsoredSigner = sponsoredSigner; } else { - const sponsoredSignerAnswer = await inquirer.prompt([ + const siwnAnswer = await inquirer.prompt([ { type: 'confirm', - name: 'useSponsoredSigner', + name: 'useSIWN', message: - 'Would you like to write data to Farcaster on behalf of your miniapp users? This involves using Neynar Sponsored Signers and SIWN.\n' + + 'Would you like to enable Sign In With Neynar (SIWN)? This allows your mini app to write data to Farcaster on behalf of users.\n' + '\n⚠️ A seed phrase is required for this option.\n', default: false, }, ]); - answers.useSponsoredSigner = sponsoredSignerAnswer.useSponsoredSigner; - - if (answers.useSponsoredSigner) { + + if (siwnAnswer.useSIWN) { const { seedPhrase } = await inquirer.prompt([ { type: 'password', name: 'seedPhrase', - message: 'Enter your Farcaster custody account seed phrase (required for Neynar Sponsored Signers/SIWN):', + message: 'Enter your Farcaster custody account seed phrase (required for SIWN):', validate: (input) => { if (!input || input.trim().split(' ').length < 12) { return 'Seed phrase must be at least 12 words'; @@ -395,6 +406,23 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe }, ]); answers.seedPhrase = seedPhrase; + + // Ask about sponsor signer if seed phrase is provided + const { sponsorSigner } = await inquirer.prompt([ + { + type: 'confirm', + name: 'sponsorSigner', + message: + 'You have provided a seed phrase, which enables Sign In With Neynar (SIWN).\n' + + 'Do you want to sponsor the signer? (This will be used in Sign In With Neynar)\n' + + 'Note: If you choose to sponsor the signer, Neynar will sponsor it for you and you will be charged in CUs.\n' + + 'For more information, see https://docs.neynar.com/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar#sponsor-signers', + default: false, + }, + ]); + answers.useSponsoredSigner = sponsorSigner; + } else { + answers.useSponsoredSigner = false; } } @@ -520,8 +548,8 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe packageJson.dependencies['@neynar/nodejs-sdk'] = '^2.19.0'; } - // Add auth-kit and next-auth dependencies if useSponsoredSigner is true - if (answers.useSponsoredSigner) { + // Add auth-kit and next-auth dependencies if SIWN is enabled (seed phrase is present) + if (answers.seedPhrase) { packageJson.dependencies['@farcaster/auth-kit'] = '>=0.6.0 <1.0.0'; packageJson.dependencies['next-auth'] = '^4.24.11'; } @@ -666,15 +694,16 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe } if (answers.seedPhrase) { fs.appendFileSync(envPath, `\nSEED_PHRASE="${answers.seedPhrase}"`); - } - fs.appendFileSync(envPath, `\nUSE_TUNNEL="${answers.useTunnel}"`); - if (answers.useSponsoredSigner) { - fs.appendFileSync(envPath, `\nSPONSOR_SIGNER="true"`); + // Add NextAuth secret for SIWN fs.appendFileSync( envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"` ); } + fs.appendFileSync(envPath, `\nUSE_TUNNEL="${answers.useTunnel}"`); + if (answers.useSponsoredSigner) { + fs.appendFileSync(envPath, `\nSPONSOR_SIGNER="true"`); + } fs.unlinkSync(envExamplePath); } else { @@ -718,9 +747,9 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe fs.rmSync(binPath, { recursive: true, force: true }); } - // Remove NeynarAuthButton directory, NextAuth API routes, and auth directory if useSponsoredSigner is false - if (!answers.useSponsoredSigner) { - console.log('\nRemoving NeynarAuthButton directory, NextAuth API routes, and auth directory (useSponsoredSigner is false)...'); + // Remove NeynarAuthButton directory, NextAuth API routes, and auth directory if SIWN is not enabled (no seed phrase) + if (!answers.seedPhrase) { + console.log('\nRemoving NeynarAuthButton directory, NextAuth API routes, and auth directory (SIWN not enabled)...'); const neynarAuthButtonPath = path.join(projectPath, 'src', 'components', 'ui', 'NeynarAuthButton'); if (fs.existsSync(neynarAuthButtonPath)) { fs.rmSync(neynarAuthButtonPath, { recursive: true, force: true }); diff --git a/package.json b/package.json index bbb40c4..932a683 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.7.11", + "version": "1.7.12", "type": "module", "private": false, "access": "public", diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 4fb429a..c7b3669 100755 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -116,13 +116,14 @@ async function checkRequiredEnvVars(): Promise { ); } - // Ask about sponsor signer if SEED_PHRASE is provided - if (!process.env.SPONSOR_SIGNER) { + // Ask about SIWN if SEED_PHRASE is provided + if (process.env.SEED_PHRASE && !process.env.SPONSOR_SIGNER) { const { sponsorSigner } = await inquirer.prompt([ { type: 'confirm', name: 'sponsorSigner', message: + 'You have provided a seed phrase, which enables Sign In With Neynar (SIWN).\n' + 'Do you want to sponsor the signer? (This will be used in Sign In With Neynar)\n' + 'Note: If you choose to sponsor the signer, Neynar will sponsor it for you and you will be charged in CUs.\n' + 'For more information, see https://docs.neynar.com/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar#sponsor-signers', @@ -132,13 +133,11 @@ async function checkRequiredEnvVars(): Promise { process.env.SPONSOR_SIGNER = sponsorSigner.toString(); - if (process.env.SEED_PHRASE) { - fs.appendFileSync( - '.env.local', - `\nSPONSOR_SIGNER="${sponsorSigner}"`, - ); - console.log('✅ Sponsor signer preference stored in .env.local'); - } + fs.appendFileSync( + '.env.local', + `\nSPONSOR_SIGNER="${sponsorSigner}"`, + ); + console.log('✅ Sponsor signer preference stored in .env.local'); } // Ask about required chains @@ -193,7 +192,7 @@ async function checkRequiredEnvVars(): Promise { } } - // Load SPONSOR_SIGNER from .env.local if SEED_PHRASE exists but SPONSOR_SIGNER doesn't + // Load SPONSOR_SIGNER from .env.local if SEED_PHRASE exists (SIWN enabled) but SPONSOR_SIGNER doesn't if ( process.env.SEED_PHRASE && !process.env.SPONSOR_SIGNER && @@ -732,8 +731,9 @@ async function deployToVercel(useGitHub = false): Promise { SPONSOR_SIGNER: process.env.SPONSOR_SIGNER, }), - // Include NextAuth environment variables if SEED_PHRASE is present or SPONSOR_SIGNER is true - ...((process.env.SEED_PHRASE || process.env.SPONSOR_SIGNER === 'true') && { + // Include NextAuth environment variables if SEED_PHRASE is present (SIWN enabled) + ...(process.env.SEED_PHRASE && { + SEED_PHRASE: process.env.SEED_PHRASE, NEXTAUTH_SECRET: nextAuthSecret, AUTH_SECRET: nextAuthSecret, NEXTAUTH_URL: `https://${domain}`, @@ -834,8 +834,8 @@ async function deployToVercel(useGitHub = false): Promise { NEXT_PUBLIC_URL: `https://${actualDomain}`, }; - // Include NextAuth URL if SEED_PHRASE is present or SPONSOR_SIGNER is true - if (process.env.SEED_PHRASE || process.env.SPONSOR_SIGNER === 'true') { + // Include NextAuth URL if SEED_PHRASE is present (SIWN enabled) + if (process.env.SEED_PHRASE) { updatedEnv.NEXTAUTH_URL = `https://${actualDomain}`; }