From f2d2e26a4a9c9628af0c91971e2e6bebd5ce8d07 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 11:58:07 -0400 Subject: [PATCH 01/18] chore: update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa2c92c..82d0450 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ This is a [NextJS](https://nextjs.org/) + TypeScript + React app. ## Guide -Check out [this Neynar docs page](https://docs.neynar.com/docs/create-v2-farcaster-frame-in-60s) for a simple guide on how to create a Farcaster Mini App in less than 60 seconds! +Check out [this Neynar docs page](https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s) for a simple guide on how to create a Farcaster Mini App in less than 60 seconds! ## Getting Started -To create a new frames project, run: +To create a new mini-app project, run: ```{bash} npx @neynar/create-farcaster-mini-app@latest ``` From a352e8d5ec8da58b773c90f0acd5f99cf4a8aa9f Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 12:18:04 -0400 Subject: [PATCH 02/18] chore: add local development section --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 82d0450..4d79dd0 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,13 @@ npm run build ``` The above command will generate a `.env` file based on the `.env.local` file and user input. Be sure to configure those environment variables on your hosting platform. + +## Developing Script Locally + +This section is only for working on the script and template, if you simply want to create a mini-app and _use_ the template this section is not for you. + +To run the script locally just run: +``` +node ./bin/index.js +``` + From 9f748076db8bea46013599d9d17b41000a3840f7 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 12:19:29 -0400 Subject: [PATCH 03/18] feat: add solana deps to init.js --- bin/init.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/init.js b/bin/init.js index a916fc5..ac4e9d1 100644 --- a/bin/init.js +++ b/bin/init.js @@ -333,7 +333,9 @@ export async function init() { "tailwindcss-animate": "^1.0.7", "viem": "^2.23.6", "wagmi": "^2.14.12", - "zod": "^3.24.2" + "zod": "^3.24.2", + "@farcaster/mini-app-solana": "^0.0.5", + "@solana/wallet-adapter-react": "^0.15.38" }; packageJson.devDependencies = { From 7f10d4e8c04e8acb82685c8876ab441fd0264183 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 14:24:47 -0400 Subject: [PATCH 04/18] feat: providers working Still need to flesh out real demo components and handle the case where there isn't a solana provider injected --- src/app/providers.tsx | 13 ++++++++++++- src/components/SolanaWalletDemo.tsx | 16 ++++++++++++++++ tester1234 | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/components/SolanaWalletDemo.tsx create mode 160000 tester1234 diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 794a016..23a56b5 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,9 +1,11 @@ "use client"; +import * as React from "react"; import dynamic from "next/dynamic"; import type { Session } from "next-auth" import { SessionProvider } from "next-auth/react" import { FrameProvider } from "~/components/providers/FrameProvider"; +import { SolanaWalletDemo } from "../components/SolanaWalletDemo"; const WagmiProvider = dynamic( () => import("~/components/providers/WagmiProvider"), @@ -12,12 +14,21 @@ const WagmiProvider = dynamic( } ); +const FarcasterSolanaProvider = dynamic( + () => import('@farcaster/mini-app-solana').then(mod => mod.FarcasterSolanaProvider), + { ssr: false } +); + export function Providers({ session, children }: { session: Session | null, children: React.ReactNode }) { + const solanaEndpoint = process.env.NEXT_PUBLIC_SOLANA_ENDPOINT || "https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY"; return ( - {children} + + + {children} + diff --git a/src/components/SolanaWalletDemo.tsx b/src/components/SolanaWalletDemo.tsx new file mode 100644 index 0000000..c68f77f --- /dev/null +++ b/src/components/SolanaWalletDemo.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import { useWallet } from "@solana/wallet-adapter-react"; + +export function SolanaWalletDemo() { + const { publicKey } = useWallet(); + const solanaAddress = publicKey?.toBase58() ?? ""; + return ( +
+

Solana Wallet Demo

+
+ Solana Address: + {solanaAddress || "Not connected"} +
+
+ ); +} diff --git a/tester1234 b/tester1234 new file mode 160000 index 0000000..fcc893a --- /dev/null +++ b/tester1234 @@ -0,0 +1 @@ +Subproject commit fcc893a97697c733e50207412f5c7346e737f812 From 880b149300ae0a507b816c1da97a91e2b46d591d Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 15:51:51 -0400 Subject: [PATCH 05/18] feat: add guards for when provider is not valid or present --- src/components/Demo.tsx | 66 ++++++++++++++ src/components/SolanaWalletDemo.tsx | 16 ---- .../providers/SafeFarcasterSolanaProvider.tsx | 86 +++++++++++++++++++ 3 files changed, 152 insertions(+), 16 deletions(-) delete mode 100644 src/components/SolanaWalletDemo.tsx create mode 100644 src/components/providers/SafeFarcasterSolanaProvider.tsx diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 1f81c58..526c57e 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -17,6 +17,11 @@ import { useSwitchChain, useChainId, } from "wagmi"; +import { + useConnection as useSolanaConnection, + useWallet as useSolanaWallet, +} from '@solana/wallet-adapter-react'; +import { useHasSolanaProvider } from "./providers/SafeFarcasterSolanaProvider"; import { config } from "~/components/providers/WagmiProvider"; import { Button } from "~/components/ui/Button"; @@ -38,6 +43,13 @@ export default function Demo( const { address, isConnected } = useAccount(); const chainId = useChainId(); + const hasSolanaProvider = useHasSolanaProvider(); + let solanaWallet, solanaPublicKey, solanaSignMessage, solanaAddress; + if (hasSolanaProvider) { + solanaWallet = useSolanaWallet(); + ({ publicKey: solanaPublicKey, signMessage: solanaSignMessage } = solanaWallet); + solanaAddress = solanaPublicKey?.toBase58(); + } useEffect(() => { console.log("isSDKLoaded", isSDKLoaded); @@ -413,11 +425,65 @@ export default function Demo( )} + + {solanaAddress && ( +
+

Solana

+
+ Address:
{truncateAddress(solanaAddress)}
+
+ +
+ )} ); } +function SignSolanaMessage({ signMessage }: { signMessage?: (message: Uint8Array) => Promise }) { + const [signature, setSignature] = useState(); + const [signError, setSignError] = useState(); + const [signPending, setSignPending] = useState(false); + + const handleSignMessage = useCallback(async () => { + setSignPending(true); + try { + if (!signMessage) { + throw new Error('no Solana signMessage'); + } + const input = Buffer.from("Hello from Solana!", "utf8"); + const signatureBytes = await signMessage(input); + const signature = Buffer.from(signatureBytes).toString("base64"); + setSignature(signature); + setSignError(undefined); + } catch (e) { + if (e instanceof Error) { + setSignError(e); + } + } finally { + setSignPending(false); + } + }, [signMessage]); + + return ( + <> + + {signError && renderError(signError)} + {signature && ( +
+
Signature: {signature}
+
+ )} + + ); +} + function SignMessage() { const { isConnected } = useAccount(); const { connectAsync } = useConnect(); diff --git a/src/components/SolanaWalletDemo.tsx b/src/components/SolanaWalletDemo.tsx deleted file mode 100644 index c68f77f..0000000 --- a/src/components/SolanaWalletDemo.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from "react"; -import { useWallet } from "@solana/wallet-adapter-react"; - -export function SolanaWalletDemo() { - const { publicKey } = useWallet(); - const solanaAddress = publicKey?.toBase58() ?? ""; - return ( -
-

Solana Wallet Demo

-
- Solana Address: - {solanaAddress || "Not connected"} -
-
- ); -} diff --git a/src/components/providers/SafeFarcasterSolanaProvider.tsx b/src/components/providers/SafeFarcasterSolanaProvider.tsx new file mode 100644 index 0000000..a63761c --- /dev/null +++ b/src/components/providers/SafeFarcasterSolanaProvider.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; +import dynamic from "next/dynamic"; +import { sdk } from '@farcaster/frame-sdk'; + +const FarcasterSolanaProvider = dynamic( + () => import('@farcaster/mini-app-solana').then(mod => mod.FarcasterSolanaProvider), + { ssr: false } +); + +type SafeFarcasterSolanaProviderProps = { + endpoint: string; + children: React.ReactNode; +}; + +const SolanaProviderContext = React.createContext<{ hasSolanaProvider: boolean }>({ hasSolanaProvider: false }); + +export function SafeFarcasterSolanaProvider({ endpoint, children }: SafeFarcasterSolanaProviderProps) { + const isClient = typeof window !== "undefined"; + const [hasSolanaProvider, setHasSolanaProvider] = React.useState(false); + const [checked, setChecked] = React.useState(false); + + React.useEffect(() => { + if (!isClient) return; + let cancelled = false; + (async () => { + try { + const provider = await sdk.wallet.getSolanaProvider(); + if (!cancelled) { + setHasSolanaProvider(!!provider); + } + } catch { + if (!cancelled) { + setHasSolanaProvider(false); + } + } finally { + if (!cancelled) { + setChecked(true); + } + } + })(); + return () => { + cancelled = true; + }; + }, [isClient]); + + React.useEffect(() => { + let errorShown = false; + const origError = console.error; + console.error = (...args) => { + if ( + typeof args[0] === "string" && + args[0].includes("WalletConnectionError: could not get Solana provider") + ) { + if (!errorShown) { + origError(...args); + errorShown = true; + } + return; + } + origError(...args); + }; + return () => { + console.error = origError; + }; + }, []); + + if (!isClient || !checked) { + return null; + } + + return ( + + {hasSolanaProvider ? ( + + {children} + + ) : ( + <>{children} + )} + + ); +} + +export function useHasSolanaProvider() { + return React.useContext(SolanaProviderContext).hasSolanaProvider; +} From d6791fe7c374350ea1d1039b5016536db789c872 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 15:58:55 -0400 Subject: [PATCH 06/18] chore: update local development readme section --- README.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4d79dd0..03dc5fc 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,36 @@ The above command will generate a `.env` file based on the `.env.local` file and ## Developing Script Locally -This section is only for working on the script and template, if you simply want to create a mini-app and _use_ the template this section is not for you. +This section is only for working on the script and template. If you simply want to create a mini-app and _use_ the template, this section is not for you. -To run the script locally just run: -``` +### Recommended: Using `npm link` for Local Development + +To iterate on the CLI and test changes in a generated app without publishing to npm: + +1. In your installer/template repo (this repo), run: + ```bash + npm link + ``` + This makes your local version globally available as a symlinked package. + + +1. Now, when you run: + ```bash + npx @neynar/create-farcaster-mini-app + ``` + ...it will use your local changes (including any edits to `init.js` or other files) instead of the published npm version. + +### Alternative: Running the Script Directly + +You can also run the script directly for quick iteration: + +```bash node ./bin/index.js ``` +However, this does not fully replicate the npx install flow and may not catch all issues that would occur in a real user environment. + +### Environment Variables and Scripts + +If you update environment variable handling, remember to replicate any changes in the `dev`, `build`, and `deploy` scripts as needed. The `build` and `deploy` scripts may need further updates and are less critical for most development workflows. + From 55755bd91ba1d1d79b9c5d460366ad17b90065d3 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:06:55 -0400 Subject: [PATCH 07/18] feat: solana send transaction Still needs cleanup but functional --- src/app/providers.tsx | 2 +- src/components/Demo.tsx | 80 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 23a56b5..29283d8 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -20,7 +20,7 @@ const FarcasterSolanaProvider = dynamic( ); export function Providers({ session, children }: { session: Session | null, children: React.ReactNode }) { - const solanaEndpoint = process.env.NEXT_PUBLIC_SOLANA_ENDPOINT || "https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY"; + const solanaEndpoint = process.env.SOLANA_RPC_ENDPOINT || "https://solana-rpc.publicnode.com"; return ( diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 526c57e..4b30a54 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -433,6 +433,9 @@ export default function Demo( Address:
{truncateAddress(solanaAddress)}
+
+ +
)} @@ -484,6 +487,83 @@ function SignSolanaMessage({ signMessage }: { signMessage?: (message: Uint8Array ); } +function SendSolana() { + const [state, setState] = useState< + | { status: 'none' } + | { status: 'pending' } + | { status: 'error'; error: Error } + | { status: 'success'; signature: string } + >({ status: 'none' }); + + const { connection: solanaConnection } = useSolanaConnection(); + const { sendTransaction, publicKey } = useSolanaWallet(); + + const ashoatsPhantomSolanaWallet = 'Ao3gLNZAsbrmnusWVqQCPMrcqNi6jdYgu8T6NCoXXQu1'; + + const handleSend = useCallback(async () => { + setState({ status: 'pending' }); + try { + if (!publicKey) { + throw new Error('no Solana publicKey'); + } + + const { blockhash } = await solanaConnection.getLatestBlockhash(); + if (!blockhash) { + throw new Error('failed to fetch latest Solana blockhash'); + } + + // Set both fromPubkey and toPubkey to constants for debugging + const fromPubkeyStr = publicKey; + const toPubkeyStr = new (await import('@solana/web3.js')).PublicKey(ashoatsPhantomSolanaWallet); // TODO: Replace with a real base58 pubkey string + console.error('Debug Solana transfer:', { fromPubkeyStr, toPubkeyStr }); + const transaction = new (await import('@solana/web3.js')).Transaction(); + transaction.add( + (await import('@solana/web3.js')).SystemProgram.transfer({ + fromPubkey: publicKey, + toPubkey: toPubkeyStr, + lamports: 0n, + }), + ); + transaction.recentBlockhash = blockhash; + transaction.feePayer = new (await import('@solana/web3.js')).PublicKey(fromPubkeyStr); + + const simulation = await solanaConnection.simulateTransaction(transaction); + if (simulation.value.err) { + // Gather logs and error details for debugging + const logs = simulation.value.logs?.join('\n') ?? 'No logs'; + const errDetail = JSON.stringify(simulation.value.err); + throw new Error(`Simulation failed: ${errDetail}\nLogs:\n${logs}`); + } + const signature = await sendTransaction(transaction, solanaConnection); + setState({ status: 'success', signature }); + } catch (e) { + if (e instanceof Error) { + setState({ status: 'error', error: e }); + } else { + setState({ status: 'none' }); + } + } + }, [sendTransaction, publicKey, solanaConnection]); + + return ( + <> + + {state.status === 'error' && renderError(state.error)} + {state.status === 'success' && ( +
+
Hash: {truncateAddress(state.signature)}
+
+ )} + + ); +} + function SignMessage() { const { isConnected } = useAccount(); const { connectAsync } = useConnect(); From 792beee05a3c431aa4e82ff23d0c27ab8af6ad2e Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:19:09 -0400 Subject: [PATCH 08/18] refactor: cleanup send sol transaction; add comments --- src/components/Demo.tsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 4b30a54..ff24947 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -31,6 +31,7 @@ import { BaseError, UserRejectedRequestError } from "viem"; import { useSession } from "next-auth/react"; import { Label } from "~/components/ui/label"; import { useFrame } from "~/components/providers/FrameProvider"; +import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js'; export default function Demo( { title }: { title?: string } = { title: "Frames v2 Demo" } @@ -443,6 +444,8 @@ export default function Demo( ); } +// Solana functions inspired by farcaster demo +// https://github.com/farcasterxyz/frames-v2-demo/blob/main/src/components/Demo.tsx function SignSolanaMessage({ signMessage }: { signMessage?: (message: Uint8Array) => Promise }) { const [signature, setSignature] = useState(); const [signError, setSignError] = useState(); @@ -474,6 +477,7 @@ function SignSolanaMessage({ signMessage }: { signMessage?: (message: Uint8Array onClick={handleSignMessage} disabled={signPending} isLoading={signPending} + className="mb-4" > Sign Message @@ -498,6 +502,8 @@ function SendSolana() { const { connection: solanaConnection } = useSolanaConnection(); const { sendTransaction, publicKey } = useSolanaWallet(); + // This should be replaced but including it from the original demo + // https://github.com/farcasterxyz/frames-v2-demo/blob/main/src/components/Demo.tsx#L718 const ashoatsPhantomSolanaWallet = 'Ao3gLNZAsbrmnusWVqQCPMrcqNi6jdYgu8T6NCoXXQu1'; const handleSend = useCallback(async () => { @@ -512,20 +518,18 @@ function SendSolana() { throw new Error('failed to fetch latest Solana blockhash'); } - // Set both fromPubkey and toPubkey to constants for debugging - const fromPubkeyStr = publicKey; - const toPubkeyStr = new (await import('@solana/web3.js')).PublicKey(ashoatsPhantomSolanaWallet); // TODO: Replace with a real base58 pubkey string - console.error('Debug Solana transfer:', { fromPubkeyStr, toPubkeyStr }); - const transaction = new (await import('@solana/web3.js')).Transaction(); + const fromPubkeyStr = publicKey.toBase58(); + const toPubkeyStr = ashoatsPhantomSolanaWallet; + const transaction = new Transaction(); transaction.add( - (await import('@solana/web3.js')).SystemProgram.transfer({ - fromPubkey: publicKey, - toPubkey: toPubkeyStr, + SystemProgram.transfer({ + fromPubkey: new PublicKey(fromPubkeyStr), + toPubkey: new PublicKey(toPubkeyStr), lamports: 0n, }), ); transaction.recentBlockhash = blockhash; - transaction.feePayer = new (await import('@solana/web3.js')).PublicKey(fromPubkeyStr); + transaction.feePayer = new PublicKey(fromPubkeyStr); const simulation = await solanaConnection.simulateTransaction(transaction); if (simulation.value.err) { @@ -551,8 +555,9 @@ function SendSolana() { onClick={handleSend} disabled={state.status === 'pending'} isLoading={state.status === 'pending'} + className="mb-4" > - Send Transaction (1 lamport) + Send Transaction (sol) {state.status === 'error' && renderError(state.error)} {state.status === 'success' && ( From 65496acc55d5c5b5873a97b09a126167340160b3 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:21:28 -0400 Subject: [PATCH 09/18] chore: cleanup test project --- tester1234 | 1 - 1 file changed, 1 deletion(-) delete mode 160000 tester1234 diff --git a/tester1234 b/tester1234 deleted file mode 160000 index fcc893a..0000000 --- a/tester1234 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fcc893a97697c733e50207412f5c7346e737f812 From 0aaec3495a1429a71395dd4915621d1137386145 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:30:01 -0400 Subject: [PATCH 10/18] refactor: text encoder over buffer Addresses comment from copilot --- src/components/Demo.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index ff24947..633d6e4 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -457,9 +457,9 @@ function SignSolanaMessage({ signMessage }: { signMessage?: (message: Uint8Array if (!signMessage) { throw new Error('no Solana signMessage'); } - const input = Buffer.from("Hello from Solana!", "utf8"); + const input = new TextEncoder().encode("Hello from Solana!"); const signatureBytes = await signMessage(input); - const signature = Buffer.from(signatureBytes).toString("base64"); + const signature = btoa(String.fromCharCode(...signatureBytes)); setSignature(signature); setSignError(undefined); } catch (e) { From 5090a76fb40062432d0bd2d4d7393e4093b0b6dd Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:33:01 -0400 Subject: [PATCH 11/18] chore: hyphenate mini-app Addresses nit from copilot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03dc5fc..f02c867 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a [NextJS](https://nextjs.org/) + TypeScript + React app. ## Guide -Check out [this Neynar docs page](https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s) for a simple guide on how to create a Farcaster Mini App in less than 60 seconds! +Check out [this Neynar docs page](https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s) for a simple guide on how to create a Farcaster Mini-App in less than 60 seconds! ## Getting Started From 6c5482bda4873fa659533e2b4bbe7943aaecdf6f Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:41:21 -0400 Subject: [PATCH 12/18] feat: update app provider --- src/app/providers.tsx | 12 +++--------- tester | 1 + 2 files changed, 4 insertions(+), 9 deletions(-) create mode 160000 tester diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 29283d8..3c754a3 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -5,7 +5,7 @@ import dynamic from "next/dynamic"; import type { Session } from "next-auth" import { SessionProvider } from "next-auth/react" import { FrameProvider } from "~/components/providers/FrameProvider"; -import { SolanaWalletDemo } from "../components/SolanaWalletDemo"; +import { SafeFarcasterSolanaProvider } from "~/components/providers/SafeFarcasterSolanaProvider"; const WagmiProvider = dynamic( () => import("~/components/providers/WagmiProvider"), @@ -14,21 +14,15 @@ const WagmiProvider = dynamic( } ); -const FarcasterSolanaProvider = dynamic( - () => import('@farcaster/mini-app-solana').then(mod => mod.FarcasterSolanaProvider), - { ssr: false } -); - export function Providers({ session, children }: { session: Session | null, children: React.ReactNode }) { const solanaEndpoint = process.env.SOLANA_RPC_ENDPOINT || "https://solana-rpc.publicnode.com"; return ( - - + {children} - + diff --git a/tester b/tester new file mode 160000 index 0000000..9348ae0 --- /dev/null +++ b/tester @@ -0,0 +1 @@ +Subproject commit 9348ae06294c4e959b60ef444b2c282720786359 From d36a6920f523a0bb0920d291cc8dc31b81dc036f Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 17:45:18 -0400 Subject: [PATCH 13/18] chore: cleanup test app --- tester | 1 - 1 file changed, 1 deletion(-) delete mode 160000 tester diff --git a/tester b/tester deleted file mode 160000 index 9348ae0..0000000 --- a/tester +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9348ae06294c4e959b60ef444b2c282720786359 From 7b754ba8fe80061905e0da2a1d836feceb02a4dd Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 18:14:58 -0400 Subject: [PATCH 14/18] chore: tweak mini app stylization Addresses comment from @veganbeef --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f02c867..fb244a3 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ This is a [NextJS](https://nextjs.org/) + TypeScript + React app. ## Guide -Check out [this Neynar docs page](https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s) for a simple guide on how to create a Farcaster Mini-App in less than 60 seconds! +Check out [this Neynar docs page](https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s) for a simple guide on how to create a Farcaster Mini App in less than 60 seconds! ## Getting Started -To create a new mini-app project, run: +To create a new mini app project, run: ```{bash} npx @neynar/create-farcaster-mini-app@latest ``` @@ -44,7 +44,7 @@ The above command will generate a `.env` file based on the `.env.local` file and ## Developing Script Locally -This section is only for working on the script and template. If you simply want to create a mini-app and _use_ the template, this section is not for you. +This section is only for working on the script and template. If you simply want to create a mini app and _use_ the template, this section is not for you. ### Recommended: Using `npm link` for Local Development From b239f0553bbf5af771ac189d14e0efcaf308e1a0 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 18:17:32 -0400 Subject: [PATCH 15/18] chore: alphabetize deps addresses comment from @veganbeef --- bin/init.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/init.js b/bin/init.js index ac4e9d1..81a4d89 100644 --- a/bin/init.js +++ b/bin/init.js @@ -317,7 +317,9 @@ export async function init() { "@farcaster/frame-node": ">=0.0.18 <1.0.0", "@farcaster/frame-sdk": ">=0.0.31 <1.0.0", "@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0", + "@farcaster/mini-app-solana": "^0.0.5", "@radix-ui/react-label": "^2.1.1", + "@solana/wallet-adapter-react": "^0.15.38", "@tanstack/react-query": "^5.61.0", "@upstash/redis": "^1.34.3", "class-variance-authority": "^0.7.1", @@ -333,9 +335,7 @@ export async function init() { "tailwindcss-animate": "^1.0.7", "viem": "^2.23.6", "wagmi": "^2.14.12", - "zod": "^3.24.2", - "@farcaster/mini-app-solana": "^0.0.5", - "@solana/wallet-adapter-react": "^0.15.38" + "zod": "^3.24.2" }; packageJson.devDependencies = { From fab3ce9a107ea115cdfa0ff3aa19c49b10f4a15c Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 18:18:09 -0400 Subject: [PATCH 16/18] chore: cleanup provider imports addresses comment from @veganbeef --- src/app/providers.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 3c754a3..e506826 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,6 +1,5 @@ "use client"; -import * as React from "react"; import dynamic from "next/dynamic"; import type { Session } from "next-auth" import { SessionProvider } from "next-auth/react" From a39cb6a0da6bed7a08b06e83169887167c107a02 Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 18:20:06 -0400 Subject: [PATCH 17/18] refactor: named imports vs default addresses comment from @veganbeef --- .../providers/SafeFarcasterSolanaProvider.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/providers/SafeFarcasterSolanaProvider.tsx b/src/components/providers/SafeFarcasterSolanaProvider.tsx index a63761c..2d6c063 100644 --- a/src/components/providers/SafeFarcasterSolanaProvider.tsx +++ b/src/components/providers/SafeFarcasterSolanaProvider.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React, { createContext, useEffect, useState } from "react"; import dynamic from "next/dynamic"; import { sdk } from '@farcaster/frame-sdk'; @@ -12,14 +12,14 @@ type SafeFarcasterSolanaProviderProps = { children: React.ReactNode; }; -const SolanaProviderContext = React.createContext<{ hasSolanaProvider: boolean }>({ hasSolanaProvider: false }); +const SolanaProviderContext = createContext<{ hasSolanaProvider: boolean }>({ hasSolanaProvider: false }); export function SafeFarcasterSolanaProvider({ endpoint, children }: SafeFarcasterSolanaProviderProps) { const isClient = typeof window !== "undefined"; - const [hasSolanaProvider, setHasSolanaProvider] = React.useState(false); - const [checked, setChecked] = React.useState(false); + const [hasSolanaProvider, setHasSolanaProvider] = useState(false); + const [checked, setChecked] = useState(false); - React.useEffect(() => { + useEffect(() => { if (!isClient) return; let cancelled = false; (async () => { @@ -43,7 +43,7 @@ export function SafeFarcasterSolanaProvider({ endpoint, children }: SafeFarcaste }; }, [isClient]); - React.useEffect(() => { + useEffect(() => { let errorShown = false; const origError = console.error; console.error = (...args) => { From 41973daea21a359634638693dd89ba4a48dfe5bf Mon Sep 17 00:00:00 2001 From: Quazia Date: Thu, 22 May 2025 18:27:40 -0400 Subject: [PATCH 18/18] refactor: SignMessage -> SignEvmMessage addresses comment from @veganbeef --- src/components/Demo.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 633d6e4..980b046 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -372,7 +372,7 @@ export default function Demo(
- +
{isConnected && ( @@ -569,7 +569,7 @@ function SendSolana() { ); } -function SignMessage() { +function SignEvmMessage() { const { isConnected } = useAccount(); const { connectAsync } = useConnect(); const {