fix: sponsor signer depends on seed phrase

This commit is contained in:
veganbeef 2025-07-18 17:29:51 -07:00
parent 572ab9aa44
commit d1ec161f47
No known key found for this signature in database
3 changed files with 74 additions and 45 deletions

View File

@ -219,6 +219,22 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe
let answers; let answers;
if (autoAcceptDefaults) { 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 = { answers = {
projectName: projectName || defaultMiniAppName || 'my-farcaster-mini-app', projectName: projectName || defaultMiniAppName || 'my-farcaster-mini-app',
description: 'A Farcaster mini app created with Neynar', description: 'A Farcaster mini app created with Neynar',
@ -228,8 +244,8 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe
useWallet: !noWallet, useWallet: !noWallet,
useTunnel: true, useTunnel: true,
enableAnalytics: true, enableAnalytics: true,
seedPhrase: null, seedPhrase: seedPhraseValue,
useSponsoredSigner: false, useSponsoredSigner: useSponsoredSignerValue,
}; };
} else { } else {
// If autoAcceptDefaults is false but we have a projectName, we still need to ask for other options // 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; answers.useTunnel = hostingAnswer.useTunnel;
} }
// Ask about Neynar Sponsored Signers / SIWN // Ask about Sign In With Neynar (SIWN) - requires seed phrase
if (sponsoredSigner) { if (seedPhrase) {
answers.useSponsoredSigner = true; // If --seed-phrase flag is used, validate it
if (seedPhrase) { if (!seedPhrase || seedPhrase.trim().split(' ').length < 12) {
// Validate the provided seed phrase console.error('Error: Seed phrase must be at least 12 words');
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');
process.exit(1); process.exit(1);
} }
answers.seedPhrase = seedPhrase;
// If --sponsored-signer flag is also provided, enable it
answers.useSponsoredSigner = sponsoredSigner;
} else { } else {
const sponsoredSignerAnswer = await inquirer.prompt([ const siwnAnswer = await inquirer.prompt([
{ {
type: 'confirm', type: 'confirm',
name: 'useSponsoredSigner', name: 'useSIWN',
message: 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', '\n⚠ A seed phrase is required for this option.\n',
default: false, default: false,
}, },
]); ]);
answers.useSponsoredSigner = sponsoredSignerAnswer.useSponsoredSigner;
if (answers.useSponsoredSigner) { if (siwnAnswer.useSIWN) {
const { seedPhrase } = await inquirer.prompt([ const { seedPhrase } = await inquirer.prompt([
{ {
type: 'password', type: 'password',
name: 'seedPhrase', 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) => { validate: (input) => {
if (!input || input.trim().split(' ').length < 12) { if (!input || input.trim().split(' ').length < 12) {
return 'Seed phrase must be at least 12 words'; 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; 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'; packageJson.dependencies['@neynar/nodejs-sdk'] = '^2.19.0';
} }
// Add auth-kit and next-auth dependencies if useSponsoredSigner is true // Add auth-kit and next-auth dependencies if SIWN is enabled (seed phrase is present)
if (answers.useSponsoredSigner) { if (answers.seedPhrase) {
packageJson.dependencies['@farcaster/auth-kit'] = '>=0.6.0 <1.0.0'; packageJson.dependencies['@farcaster/auth-kit'] = '>=0.6.0 <1.0.0';
packageJson.dependencies['next-auth'] = '^4.24.11'; packageJson.dependencies['next-auth'] = '^4.24.11';
} }
@ -666,15 +694,16 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe
} }
if (answers.seedPhrase) { if (answers.seedPhrase) {
fs.appendFileSync(envPath, `\nSEED_PHRASE="${answers.seedPhrase}"`); fs.appendFileSync(envPath, `\nSEED_PHRASE="${answers.seedPhrase}"`);
} // Add NextAuth secret for SIWN
fs.appendFileSync(envPath, `\nUSE_TUNNEL="${answers.useTunnel}"`);
if (answers.useSponsoredSigner) {
fs.appendFileSync(envPath, `\nSPONSOR_SIGNER="true"`);
fs.appendFileSync( fs.appendFileSync(
envPath, envPath,
`\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"` `\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); fs.unlinkSync(envExamplePath);
} else { } else {
@ -718,9 +747,9 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe
fs.rmSync(binPath, { recursive: true, force: true }); fs.rmSync(binPath, { recursive: true, force: true });
} }
// Remove NeynarAuthButton directory, NextAuth API routes, and auth directory if useSponsoredSigner is false // Remove NeynarAuthButton directory, NextAuth API routes, and auth directory if SIWN is not enabled (no seed phrase)
if (!answers.useSponsoredSigner) { if (!answers.seedPhrase) {
console.log('\nRemoving NeynarAuthButton directory, NextAuth API routes, and auth directory (useSponsoredSigner is false)...'); console.log('\nRemoving NeynarAuthButton directory, NextAuth API routes, and auth directory (SIWN not enabled)...');
const neynarAuthButtonPath = path.join(projectPath, 'src', 'components', 'ui', 'NeynarAuthButton'); const neynarAuthButtonPath = path.join(projectPath, 'src', 'components', 'ui', 'NeynarAuthButton');
if (fs.existsSync(neynarAuthButtonPath)) { if (fs.existsSync(neynarAuthButtonPath)) {
fs.rmSync(neynarAuthButtonPath, { recursive: true, force: true }); fs.rmSync(neynarAuthButtonPath, { recursive: true, force: true });

View File

@ -1,6 +1,6 @@
{ {
"name": "@neynar/create-farcaster-mini-app", "name": "@neynar/create-farcaster-mini-app",
"version": "1.7.11", "version": "1.7.12",
"type": "module", "type": "module",
"private": false, "private": false,
"access": "public", "access": "public",

View File

@ -116,13 +116,14 @@ async function checkRequiredEnvVars(): Promise<void> {
); );
} }
// Ask about sponsor signer if SEED_PHRASE is provided // Ask about SIWN if SEED_PHRASE is provided
if (!process.env.SPONSOR_SIGNER) { if (process.env.SEED_PHRASE && !process.env.SPONSOR_SIGNER) {
const { sponsorSigner } = await inquirer.prompt([ const { sponsorSigner } = await inquirer.prompt([
{ {
type: 'confirm', type: 'confirm',
name: 'sponsorSigner', name: 'sponsorSigner',
message: 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' + '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' + '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', '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<void> {
process.env.SPONSOR_SIGNER = sponsorSigner.toString(); process.env.SPONSOR_SIGNER = sponsorSigner.toString();
if (process.env.SEED_PHRASE) { fs.appendFileSync(
fs.appendFileSync( '.env.local',
'.env.local', `\nSPONSOR_SIGNER="${sponsorSigner}"`,
`\nSPONSOR_SIGNER="${sponsorSigner}"`, );
); console.log('✅ Sponsor signer preference stored in .env.local');
console.log('✅ Sponsor signer preference stored in .env.local');
}
} }
// Ask about required chains // Ask about required chains
@ -193,7 +192,7 @@ async function checkRequiredEnvVars(): Promise<void> {
} }
} }
// 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 ( if (
process.env.SEED_PHRASE && process.env.SEED_PHRASE &&
!process.env.SPONSOR_SIGNER && !process.env.SPONSOR_SIGNER &&
@ -732,8 +731,9 @@ async function deployToVercel(useGitHub = false): Promise<void> {
SPONSOR_SIGNER: process.env.SPONSOR_SIGNER, SPONSOR_SIGNER: process.env.SPONSOR_SIGNER,
}), }),
// Include NextAuth environment variables if SEED_PHRASE is present or SPONSOR_SIGNER is true // Include NextAuth environment variables if SEED_PHRASE is present (SIWN enabled)
...((process.env.SEED_PHRASE || process.env.SPONSOR_SIGNER === 'true') && { ...(process.env.SEED_PHRASE && {
SEED_PHRASE: process.env.SEED_PHRASE,
NEXTAUTH_SECRET: nextAuthSecret, NEXTAUTH_SECRET: nextAuthSecret,
AUTH_SECRET: nextAuthSecret, AUTH_SECRET: nextAuthSecret,
NEXTAUTH_URL: `https://${domain}`, NEXTAUTH_URL: `https://${domain}`,
@ -834,8 +834,8 @@ async function deployToVercel(useGitHub = false): Promise<void> {
NEXT_PUBLIC_URL: `https://${actualDomain}`, NEXT_PUBLIC_URL: `https://${actualDomain}`,
}; };
// Include NextAuth URL if SEED_PHRASE is present or SPONSOR_SIGNER is true // Include NextAuth URL if SEED_PHRASE is present (SIWN enabled)
if (process.env.SEED_PHRASE || process.env.SPONSOR_SIGNER === 'true') { if (process.env.SEED_PHRASE) {
updatedEnv.NEXTAUTH_URL = `https://${actualDomain}`; updatedEnv.NEXTAUTH_URL = `https://${actualDomain}`;
} }