"use client";
import { useCallback, useMemo, useState, useEffect } from "react";
import { useAccount, useSendTransaction, useSignTypedData, useWaitForTransactionReceipt, useDisconnect, useConnect, useSwitchChain, useChainId, type Connector } from "wagmi";
import { useWallet as useSolanaWallet } from '@solana/wallet-adapter-react';
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
import { Button } from "../Button";
import { truncateAddress } from "../../../lib/truncateAddress";
import { renderError } from "../../../lib/errorUtils";
import { SignEvmMessage } from "../wallet/SignEvmMessage";
import { SendEth } from "../wallet/SendEth";
import { SignSolanaMessage } from "../wallet/SignSolanaMessage";
import { SendSolana } from "../wallet/SendSolana";
import { USE_WALLET, APP_NAME } from "../../../lib/constants";
import { useMiniApp } from "@neynar/react";
/**
* WalletTab component manages wallet-related UI for both EVM and Solana chains.
*
* This component provides a comprehensive wallet interface that supports:
* - EVM wallet connections (Farcaster Frame, Coinbase Wallet, MetaMask)
* - Solana wallet integration
* - Message signing for both chains
* - Transaction sending for both chains
* - Chain switching for EVM chains
* - Auto-connection in Farcaster clients
*
* The component automatically detects when running in a Farcaster client
* and attempts to auto-connect using the Farcaster Frame connector.
*
* @example
* ```tsx
*
* ```
*/
interface WalletStatusProps {
address?: string;
chainId?: number;
}
/**
* Displays the current wallet address and chain ID.
*/
function WalletStatus({ address, chainId }: WalletStatusProps) {
return (
<>
{address && (
Address:
{truncateAddress(address)}
)}
{chainId && (
)}
>
);
}
interface ConnectionControlsProps {
isConnected: boolean;
context: {
user?: { fid?: number };
client?: unknown;
} | null;
connect: (args: { connector: Connector }) => void;
connectors: readonly Connector[];
disconnect: () => void;
}
/**
* Renders wallet connection controls based on connection state and context.
*/
function ConnectionControls({
isConnected,
context,
connect,
connectors,
disconnect,
}: ConnectionControlsProps) {
if (isConnected) {
return (
);
}
if (context) {
return (
);
}
return (
);
}
export function WalletTab() {
// --- State ---
const [evmContractTransactionHash, setEvmContractTransactionHash] = useState(null);
// --- Hooks ---
const { context } = useMiniApp();
const { address, isConnected } = useAccount();
const chainId = useChainId();
const solanaWallet = useSolanaWallet();
const { publicKey: solanaPublicKey } = solanaWallet;
// --- Wagmi Hooks ---
const {
sendTransaction,
error: evmTransactionError,
isError: isEvmTransactionError,
isPending: isEvmTransactionPending,
} = useSendTransaction();
const { isLoading: isEvmTransactionConfirming, isSuccess: isEvmTransactionConfirmed } =
useWaitForTransactionReceipt({
hash: evmContractTransactionHash as `0x${string}`,
});
const {
signTypedData,
error: evmSignTypedDataError,
isError: isEvmSignTypedDataError,
isPending: isEvmSignTypedDataPending,
} = useSignTypedData();
const { disconnect } = useDisconnect();
const { connect, connectors } = useConnect();
const {
switchChain,
error: chainSwitchError,
isError: isChainSwitchError,
isPending: isChainSwitchPending,
} = useSwitchChain();
// --- Effects ---
/**
* Auto-connect when Farcaster context is available.
*
* This effect detects when the app is running in a Farcaster client
* and automatically attempts to connect using the Farcaster Frame connector.
* It includes comprehensive logging for debugging connection issues.
*/
useEffect(() => {
// Check if we're in a Farcaster client environment
const isInFarcasterClient = typeof window !== 'undefined' &&
(window.location.href.includes('warpcast.com') ||
window.location.href.includes('farcaster') ||
window.ethereum?.isFarcaster ||
context?.client);
if (context?.user?.fid && !isConnected && connectors.length > 0 && isInFarcasterClient) {
console.log("Attempting auto-connection with Farcaster context...");
console.log("- User FID:", context.user.fid);
console.log("- Available connectors:", connectors.map((c, i) => `${i}: ${c.name}`));
console.log("- Using connector:", connectors[0].name);
console.log("- In Farcaster client:", isInFarcasterClient);
// Use the first connector (farcasterFrame) for auto-connection
try {
connect({ connector: connectors[0] });
} catch (error) {
console.error("Auto-connection failed:", error);
}
} else {
console.log("Auto-connection conditions not met:");
console.log("- Has context:", !!context?.user?.fid);
console.log("- Is connected:", isConnected);
console.log("- Has connectors:", connectors.length > 0);
console.log("- In Farcaster client:", isInFarcasterClient);
}
}, [context?.user?.fid, isConnected, connectors, connect, context?.client]);
// --- Computed Values ---
/**
* Determines the next chain to switch to based on the current chain.
* Cycles through: Base → Optimism → Degen → Mainnet → Unichain → Base
*/
const nextChain = useMemo(() => {
if (chainId === base.id) {
return optimism;
} else if (chainId === optimism.id) {
return degen;
} else if (chainId === degen.id) {
return mainnet;
} else if (chainId === mainnet.id) {
return unichain;
} else {
return base;
}
}, [chainId]);
// --- Handlers ---
/**
* Handles switching to the next chain in the rotation.
* Uses the switchChain function from wagmi to change the active chain.
*/
const handleSwitchChain = useCallback(() => {
switchChain({ chainId: nextChain.id });
}, [switchChain, nextChain.id]);
/**
* Sends a transaction to call the yoink() function on the Yoink contract.
*
* This function sends a transaction to a specific contract address with
* the encoded function call data for the yoink() function.
*/
const sendEvmContractTransaction = useCallback(() => {
sendTransaction(
{
// call yoink() on Yoink contract
to: "0x4bBFD120d9f352A0BEd7a014bd67913a2007a878",
data: "0x9846cd9efc000023c0",
},
{
onSuccess: (hash) => {
setEvmContractTransactionHash(hash);
},
}
);
}, [sendTransaction]);
/**
* Signs typed data using EIP-712 standard.
*
* This function creates a typed data structure with the app name, version,
* and chain ID, then requests the user to sign it.
*/
const signTyped = useCallback(() => {
signTypedData({
domain: {
name: APP_NAME,
version: "1",
chainId,
},
types: {
Message: [{ name: "content", type: "string" }],
},
message: {
content: `Hello from ${APP_NAME}!`,
},
primaryType: "Message",
});
}, [chainId, signTypedData]);
// --- Early Return ---
if (!USE_WALLET) {
return null;
}
// --- Render ---
return (
{/* Wallet Information Display */}
{/* Connection Controls */}
{/* EVM Wallet Components */}
{isConnected && (
<>
{isEvmTransactionError && renderError(evmTransactionError)}
{evmContractTransactionHash && (
Hash: {truncateAddress(evmContractTransactionHash)}
Status:{" "}
{isEvmTransactionConfirming
? "Confirming..."
: isEvmTransactionConfirmed
? "Confirmed!"
: "Pending"}
)}
{isEvmSignTypedDataError && renderError(evmSignTypedDataError)}
{isChainSwitchError && renderError(chainSwitchError)}
>
)}
{/* Solana Wallet Components */}
{solanaPublicKey && (
<>
>
)}
);
}