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;
+}