diff --git a/app/page.tsx b/app/page.tsx index 1e438c5..40bcc8b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -182,6 +182,30 @@ export default function Home() { publicKey ); + // Check user's token account status and balance + console.log('Checking user token account status...'); + const userTokenAccountInfo = await connection.getAccountInfo(userTokenAccount); + if (!userTokenAccountInfo) { + throw new Error('DINO token account not found. Please ensure you have DINO tokens in your wallet.'); + } + + // Get token balance + const userTokenAccounts = await connection.getParsedTokenAccountsByOwner( + publicKey, + { mint: dinoMint } + ); + + if (userTokenAccounts.value.length === 0) { + throw new Error('No DINO tokens found in your wallet.'); + } + + const userBalance = userTokenAccounts.value[0].account.data.parsed.info.tokenAmount.uiAmount; + console.log('User DINO balance:', userBalance); + + if (userBalance < ENTRY_FEE_DINO) { + throw new Error(`Insufficient DINO tokens. You have ${userBalance} DINO, but need ${ENTRY_FEE_DINO} DINO to play.`); + } + // Get or create the fee collector's DINO token account const feeCollectorTokenAccount = await getAssociatedTokenAddress( dinoMint, @@ -193,34 +217,50 @@ export default function Home() { // Check if fee collector token account exists, if not create it const feeCollectorAccountInfo = await connection.getAccountInfo(feeCollectorTokenAccount); if (!feeCollectorAccountInfo) { - transaction.add( - createAssociatedTokenAccountInstruction( - publicKey, - feeCollectorTokenAccount, - FEE_COLLECTOR, - dinoMint - ) + console.log('Creating fee collector token account...'); + const createAccountIx = createAssociatedTokenAccountInstruction( + publicKey, + feeCollectorTokenAccount, + FEE_COLLECTOR, + dinoMint ); + transaction.add(createAccountIx); + console.log('Added create account instruction'); } // Add the transfer instruction (10 DINO tokens = 10,000,000,000 lamports for 9 decimals) const transferAmount = ENTRY_FEE_DINO * 1_000_000_000; // 10 DINO tokens + console.log('Adding transfer instruction for', ENTRY_FEE_DINO, 'DINO tokens (', transferAmount, 'lamports)'); - transaction.add( - createTransferInstruction( - userTokenAccount, - feeCollectorTokenAccount, - publicKey, - transferAmount - ) + const transferIx = createTransferInstruction( + userTokenAccount, + feeCollectorTokenAccount, + publicKey, + transferAmount ); + + // Validate the transfer instruction + if (!transferIx.programId.equals(dinoMint)) { + console.warn('Transfer instruction program ID mismatch'); + } + + transaction.add(transferIx); + console.log('Added transfer instruction'); // Get recent blockhash with higher commitment for mobile wallets - const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized'); + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed'); transaction.recentBlockhash = blockhash; transaction.lastValidBlockHeight = lastValidBlockHeight; transaction.feePayer = publicKey; + // Ensure the transaction is properly formatted for mobile wallets + console.log('Transaction details before preflight:'); + console.log('- Recent blockhash:', transaction.recentBlockhash); + console.log('- Last valid block height:', transaction.lastValidBlockHeight); + console.log('- Fee payer:', transaction.feePayer?.toString()); + console.log('- Number of instructions:', transaction.instructions.length); + console.log('- Instructions:', transaction.instructions.map((ix, i) => `${i}: ${ix.programId.toString()}`)); + // Validate transaction for mobile wallets const validationErrors = validateTransactionForMobile(transaction); if (validationErrors.length > 0) { @@ -233,30 +273,89 @@ export default function Home() { // Preflight check for mobile wallets try { - const { value: { err } } = await connection.simulateTransaction(transaction); + console.log('Running preflight check for transaction...'); + const { value: { err, logs } } = await connection.simulateTransaction(transaction); + if (err) { - throw new Error(`Transaction simulation failed: ${err}`); + console.warn('Preflight check failed:', err); + console.log('Preflight logs:', logs); + + // For mobile wallets, we'll try to fix common issues and retry + if (isMobileDevice()) { + console.log('Attempting to fix transaction for mobile wallet...'); + + // Refresh blockhash and retry preflight + const { blockhash: newBlockhash, lastValidBlockHeight: newLastValidBlockHeight } = await connection.getLatestBlockhash('confirmed'); + transaction.recentBlockhash = newBlockhash; + transaction.lastValidBlockHeight = newLastValidBlockHeight; + + // Retry preflight with fresh blockhash + const retryResult = await connection.simulateTransaction(transaction); + if (retryResult.value.err) { + console.warn('Preflight retry also failed:', retryResult.value.err); + // On mobile, we might want to proceed anyway as some wallets handle this differently + console.log('Proceeding with transaction despite preflight failure (mobile wallet behavior)'); + } else { + console.log('Preflight retry successful!'); + } + } else { + // On desktop, be more strict about preflight failures + throw new Error(`Transaction simulation failed: ${err}`); + } + } else { + console.log('Preflight check passed successfully'); } } catch (simError) { - console.warn('Preflight check failed, proceeding anyway:', simError); - // On mobile, we might want to be more strict about preflight failures + console.warn('Preflight simulation failed:', simError); + + // On mobile, we might want to proceed anyway as some wallets handle this differently if (isMobileDevice()) { + console.log('Proceeding with transaction despite preflight failure (mobile wallet behavior)'); + } else { throw new Error('Transaction preflight check failed. Please try again.'); } } // Send the transaction with mobile-optimized options - const signature = await sendTransactionWithMobileOptimization( - transaction, - connection, - wallet, - { - skipPreflight: false, - preflightCommitment: 'confirmed', - maxRetries: 3, - commitment: getMobileCommitmentLevel() + let signature: string; + + try { + signature = await sendTransactionWithMobileOptimization( + transaction, + connection, + wallet, + { + skipPreflight: false, // Try with preflight first + preflightCommitment: 'confirmed', + maxRetries: 3, + commitment: getMobileCommitmentLevel() + } + ); + } catch (error) { + console.warn('Mobile-optimized transaction failed, trying with preflight disabled...'); + + // If mobile-optimized fails, try with preflight disabled for mobile wallets + if (isMobileDevice()) { + try { + signature = await sendTransactionWithMobileOptimization( + transaction, + connection, + wallet, + { + skipPreflight: true, // Disable preflight as fallback + preflightCommitment: 'confirmed', + maxRetries: 2, + commitment: getMobileCommitmentLevel() + } + ); + } catch (fallbackError) { + console.error('Fallback transaction also failed:', fallbackError); + throw fallbackError; + } + } else { + throw error; } - ); + } // Remove the old confirmation logic since it's handled in the utility setTxHash(signature); @@ -340,6 +439,12 @@ export default function Home() { errorMessage = 'Transaction validation failed. Please check your wallet connection and try again.'; } else if (err.message.includes('blockhash')) { errorMessage = 'Transaction expired. Please try again.'; + } else if (err.message.includes('preflight')) { + errorMessage = 'Transaction preflight check failed. This usually means there\'s an issue with your wallet connection or token balance. Try the Debug Wallet button to diagnose the issue.'; + } else if (err.message.includes('insufficient lamports')) { + errorMessage = 'Insufficient SOL for transaction fees. Please ensure you have some SOL in your wallet for gas fees.'; + } else if (err.message.includes('invalid account')) { + errorMessage = 'Invalid account detected. Please refresh the page and reconnect your wallet.'; } else { errorMessage = err.message; } @@ -351,6 +456,69 @@ export default function Home() { } }; + const handleDebugWallet = async () => { + if (!publicKey) { + setError('Please connect your wallet first'); + return; + } + + console.log('=== WALLET DEBUG INFO ==='); + console.log('Public key:', publicKey.toString()); + console.log('Is mobile device:', isMobileDevice()); + + try { + const connection = new Connection(CLUSTER_URL, 'confirmed'); + const dinoMint = new PublicKey(DINO_TOKEN_ADDRESS); + + // Test basic connection + console.log('Testing Solana connection...'); + const version = await connection.getVersion(); + console.log('Solana version:', version); + + // Test token account + console.log('Testing token account...'); + const userTokenAccount = await getAssociatedTokenAddress(dinoMint, publicKey); + console.log('User token account:', userTokenAccount.toString()); + + const userTokenAccountInfo = await connection.getAccountInfo(userTokenAccount); + console.log('Token account exists:', !!userTokenAccountInfo); + + if (userTokenAccountInfo) { + const userTokenAccounts = await connection.getParsedTokenAccountsByOwner( + publicKey, + { mint: dinoMint } + ); + + if (userTokenAccounts.value.length > 0) { + const balance = userTokenAccounts.value[0].account.data.parsed.info.tokenAmount.uiAmount; + console.log('DINO balance:', balance); + } else { + console.log('No DINO tokens found'); + } + } + + // Test simple transaction simulation + console.log('Testing simple transaction simulation...'); + const testTransaction = new Transaction(); + testTransaction.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash; + testTransaction.feePayer = publicKey; + + try { + const simResult = await connection.simulateTransaction(testTransaction); + console.log('Simple transaction simulation result:', simResult); + } catch (simError) { + console.error('Simple transaction simulation failed:', simError); + } + + console.log('=== END DEBUG INFO ==='); + setError('Debug info logged to console. Check browser console for details.'); + + } catch (error) { + console.error('Debug failed:', error); + setError(`Debug failed: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }; + return (