"use client"; import { createContext, useContext, useEffect, useMemo, useState, ReactNode } from "react"; import type { VaultReceipt, VaultState } from "@/lib/metaGame"; import { addKey, addReceipt, defaultVaultState, loadVaultState, saveVaultState, setFlag } from "@/lib/metaGame"; import { loadClaimedTxids, loadUsdStoreCredit, saveClaimedTxids, saveUsdStoreCredit, } from "@/lib/storeCreditStorage"; interface WalletContextType { isConnected: boolean; address: string | null; chainId: number | null; connect: () => Promise; disconnect: () => void; balance: string; luxCredits: number; usdStoreCredit: number; spendLuxCredits: (amount: number) => boolean; earnLuxCredits: (amount: number) => void; spendUsdStoreCredit: (amount: number) => boolean; verifyBtcDeposit: (txid: string) => Promise<{ ok: boolean; error?: string; creditedUsd?: number }>; vault: VaultState; collectKey: (key: string) => boolean; addVaultReceipt: (receipt: VaultReceipt) => void; setVaultFlag: (flag: string, value: boolean) => void; } const WalletContext = createContext(undefined); export const useWallet = () => { const context = useContext(WalletContext); if (!context) { throw new Error("useWallet must be used within WalletProvider"); } return context; }; interface WalletProviderProps { children: ReactNode; } export const WalletProvider = ({ children }: WalletProviderProps) => { const [isConnected, setIsConnected] = useState(false); const [address, setAddress] = useState(null); const [chainId, setChainId] = useState(null); const [balance, setBalance] = useState("0.0"); const [luxCredits, setLuxCredits] = useState(0); const [usdStoreCredit, setUsdStoreCredit] = useState(0); const [usdHydrated, setUsdHydrated] = useState(false); const [vault, setVault] = useState(() => defaultVaultState()); const mockAddress = "0x71C7...e4a2"; const mockChainId = 1; const mockBalance = "2.45"; const startingLuxCredits = 5000; const connect = async () => { // Simulate wallet connection delay await new Promise((resolve) => setTimeout(resolve, 800)); setIsConnected(true); setAddress(mockAddress); setChainId(mockChainId); setBalance(mockBalance); setLuxCredits(startingLuxCredits); }; const disconnect = () => { setIsConnected(false); setAddress(null); setChainId(null); setBalance("0.0"); setLuxCredits(0); }; useEffect(() => { setVault(loadVaultState()); setUsdStoreCredit(loadUsdStoreCredit()); setUsdHydrated(true); }, []); useEffect(() => { saveVaultState(vault); }, [vault]); useEffect(() => { if (!usdHydrated) return; saveUsdStoreCredit(usdStoreCredit); }, [usdStoreCredit, usdHydrated]); const spendLuxCredits = (amount: number) => { const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0; if (safeAmount <= 0) return true; if (luxCredits < safeAmount) return false; setLuxCredits((c) => c - safeAmount); return true; }; const earnLuxCredits = (amount: number) => { const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0; if (safeAmount <= 0) return; setLuxCredits((c) => c + safeAmount); }; const spendUsdStoreCredit = (amount: number) => { const safe = Number.isFinite(amount) ? Math.max(0, Math.round(amount * 100) / 100) : 0; if (safe <= 0) return true; if (usdStoreCredit + 1e-9 < safe) return false; setUsdStoreCredit((u) => Math.round((u - safe) * 100) / 100); return true; }; const verifyBtcDeposit = async (txid: string) => { const normalized = String(txid || "").trim(); if (!/^[a-fA-F0-9]{64}$/.test(normalized)) { return { ok: false, error: "Enter a valid 64-character transaction id" }; } const claimed = loadClaimedTxids(); if (claimed.includes(normalized)) { return { ok: false, error: "This transaction was already used to add credit" }; } try { const res = await fetch("/api/btc/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ txid: normalized }), }); const data = (await res.json()) as { ok?: boolean; error?: string; creditedUsd?: number; }; if (!data.ok || typeof data.creditedUsd !== "number") { return { ok: false, error: data.error || "Verification failed" }; } claimed.push(normalized); saveClaimedTxids(claimed); const add = Math.round(data.creditedUsd * 100) / 100; setUsdStoreCredit((u) => Math.round((u + add) * 100) / 100); return { ok: true, creditedUsd: add }; } catch { return { ok: false, error: "Network error while verifying" }; } }; const collectKey = (key: string) => { const trimmed = String(key || "").trim(); if (!trimmed) return false; if (vault.keys.includes(trimmed)) return false; setVault((v) => addKey(v, trimmed)); return true; }; const addVaultReceipt = (receipt: VaultReceipt) => { setVault((v) => addReceipt(v, receipt)); }; const setVaultFlag = (flag: string, value: boolean) => { const f = String(flag || "").trim(); if (!f) return; setVault((v) => setFlag(v, f, Boolean(value))); }; const value = useMemo( () => ({ isConnected, address, chainId, connect, disconnect, balance, luxCredits, usdStoreCredit, spendLuxCredits, earnLuxCredits, spendUsdStoreCredit, verifyBtcDeposit, vault, collectKey, addVaultReceipt, setVaultFlag, }), [isConnected, address, chainId, balance, luxCredits, usdStoreCredit, vault], ); return ( {children} ); };