- ChatWidget: remove illegal seeds, real localStorage per-handle chat, honest bot replies about market/forum/funds - ForumBoard: wire to real forumState (loadForum/addThread/vote), kill fake stats and illegal seed posts - Home page: privacy features list reflects reality, footer links real - Links: kill all alert() calls, replace fake onions with real clearnet privacy resources + internal route grid - Support: per-coin copied state, env-driven addresses, real BTC addr - Inner circle: wire to AccountContext, tier system from LUX balance, remove hardcoded admin/shadow credentials and fake trading signals - Drop box: real sealed-note localStorage system, honest about no anonymous upload capability, real file picker with receipt - Messages: fully functional per-handle localStorage chat, AI-style contextual bot replies, clear history, honest about local storage - Wallets: pivot from fake PayPal accounts to Digital Access Passes, wire Buy Now to cart via ShopProduct interface - Testimonials: wire submit form to localStorage, interactive star rating 1-10, display submitted reviews above the fold - Raffle: use real merchant BTC address, real per-handle entry storage, honest LUX-only prize disclaimer, fix 0x address - Drops/Lotto: real number picker 1-49 with Quick Pick, ticket submission, match display against drawn numbers, demo disclaimer - Sanctuary: real 4-4-6-2 breathing timer, meditation passage with timer, candle-lighting with localStorage notes - Game: full playable Void Pong with canvas physics, CPU AI, scoring, rally counter, localStorage high score - Security analysis: honest architecture breakdown with real grades, layer-by-layer analysis, practical OPSEC guide, fiction banner - Trust: compute real scores from actual localStorage data (LUX, USD, forum posts, testimonials), FAQ accordion Made-with: Cursor
188 lines
6.0 KiB
TypeScript
188 lines
6.0 KiB
TypeScript
"use client";
|
||
|
||
import { createContext, useCallback, 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 { getLedgerRow, migrateLegacyDeviceLedgerIfNeeded, setLedgerRow } from "@/lib/storeCreditStorage";
|
||
import { useAccount } from "@/contexts/AccountContext";
|
||
|
||
interface WalletContextType {
|
||
usdStoreCredit: number;
|
||
luxCredits: 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<WalletContextType | undefined>(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 { user } = useAccount();
|
||
const handle = user?.username ?? null;
|
||
|
||
const [usdStoreCredit, setUsdStoreCredit] = useState(0);
|
||
const [luxCredits, setLuxCredits] = useState(0);
|
||
const [vault, setVault] = useState<VaultState>(() => defaultVaultState());
|
||
|
||
useEffect(() => {
|
||
if (!handle) {
|
||
setUsdStoreCredit(0);
|
||
setLuxCredits(0);
|
||
return;
|
||
}
|
||
migrateLegacyDeviceLedgerIfNeeded(handle);
|
||
const row = getLedgerRow(handle);
|
||
setUsdStoreCredit(row.usd);
|
||
setLuxCredits(row.lux);
|
||
}, [handle]);
|
||
|
||
useEffect(() => {
|
||
setVault(loadVaultState(handle));
|
||
}, [handle]);
|
||
|
||
useEffect(() => {
|
||
saveVaultState(handle, vault);
|
||
}, [vault, handle]);
|
||
|
||
const spendLuxCredits = useCallback((amount: number) => {
|
||
if (!handle) return false;
|
||
const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0;
|
||
if (safeAmount <= 0) return true;
|
||
const row = getLedgerRow(handle);
|
||
if (row.lux < safeAmount) return false;
|
||
const nextLux = row.lux - safeAmount;
|
||
setLedgerRow(handle, { usd: row.usd, lux: nextLux, claimedTxids: row.claimedTxids });
|
||
setLuxCredits(nextLux);
|
||
return true;
|
||
}, [handle]);
|
||
|
||
const earnLuxCredits = useCallback(
|
||
(amount: number) => {
|
||
if (!handle) return;
|
||
const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0;
|
||
if (safeAmount <= 0) return;
|
||
const row = getLedgerRow(handle);
|
||
const nextLux = row.lux + safeAmount;
|
||
setLedgerRow(handle, { usd: row.usd, lux: nextLux, claimedTxids: row.claimedTxids });
|
||
setLuxCredits(nextLux);
|
||
},
|
||
[handle],
|
||
);
|
||
|
||
const spendUsdStoreCredit = useCallback(
|
||
(amount: number) => {
|
||
if (!handle) return false;
|
||
const safe = Number.isFinite(amount) ? Math.max(0, Math.round(amount * 100) / 100) : 0;
|
||
if (safe <= 0) return true;
|
||
const row = getLedgerRow(handle);
|
||
if (row.usd + 1e-9 < safe) return false;
|
||
const nextUsd = Math.round((row.usd - safe) * 100) / 100;
|
||
setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: row.claimedTxids });
|
||
setUsdStoreCredit(nextUsd);
|
||
return true;
|
||
},
|
||
[handle],
|
||
);
|
||
|
||
const verifyBtcDeposit = useCallback(
|
||
async (txid: string) => {
|
||
if (!handle) {
|
||
return { ok: false, error: "Sign in so deposits credit your handle’s balance." };
|
||
}
|
||
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 row = getLedgerRow(handle);
|
||
if (row.claimedTxids.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" };
|
||
}
|
||
const add = Math.round(data.creditedUsd * 100) / 100;
|
||
const nextUsd = Math.round((row.usd + add) * 100) / 100;
|
||
const claimed = [...row.claimedTxids, normalized];
|
||
setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: claimed });
|
||
setUsdStoreCredit(nextUsd);
|
||
return { ok: true, creditedUsd: add };
|
||
} catch {
|
||
return { ok: false, error: "Network error while verifying" };
|
||
}
|
||
},
|
||
[handle],
|
||
);
|
||
|
||
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(
|
||
() => ({
|
||
usdStoreCredit,
|
||
luxCredits,
|
||
spendLuxCredits,
|
||
earnLuxCredits,
|
||
spendUsdStoreCredit,
|
||
verifyBtcDeposit,
|
||
vault,
|
||
collectKey,
|
||
addVaultReceipt,
|
||
setVaultFlag,
|
||
}),
|
||
[
|
||
usdStoreCredit,
|
||
luxCredits,
|
||
spendLuxCredits,
|
||
earnLuxCredits,
|
||
spendUsdStoreCredit,
|
||
verifyBtcDeposit,
|
||
vault,
|
||
],
|
||
);
|
||
|
||
return <WalletContext.Provider value={value}>{children}</WalletContext.Provider>;
|
||
};
|