Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
174 lines
7.5 KiB
TypeScript
174 lines
7.5 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
import ThemedLayout from "@/components/layouts/ThemedLayout";
|
|
import { useWallet } from "@/contexts/WalletContext";
|
|
|
|
type MixLog = { id: string; text: string };
|
|
|
|
function id() {
|
|
return Math.random().toString(16).slice(2, 10).toUpperCase();
|
|
}
|
|
|
|
export default function MixerPage() {
|
|
const { isConnected, luxCredits, spendLuxCredits, setVaultFlag, addVaultReceipt } = useWallet();
|
|
const [amount, setAmount] = useState(500);
|
|
const [hops, setHops] = useState(3);
|
|
const [noise, setNoise] = useState(65);
|
|
const [log, setLog] = useState<MixLog[]>([]);
|
|
|
|
const fee = useMemo(() => {
|
|
const base = Math.floor(amount * 0.04);
|
|
const hopFee = hops * 25;
|
|
const noiseFee = Math.floor(noise * 2);
|
|
return base + hopFee + noiseFee;
|
|
}, [amount, hops, noise]);
|
|
|
|
const total = amount + fee;
|
|
|
|
const push = (text: string) => setLog((l) => [{ id: id(), text }, ...l].slice(0, 10));
|
|
|
|
return (
|
|
<ThemedLayout theme="terminal" siteTitle="COIN BLENDER" siteSubtitle="Parody mixer simulator">
|
|
<section className="container mx-auto max-w-6xl px-4 py-12">
|
|
<div className="text-center">
|
|
<h1 className="text-4xl font-bold md:text-5xl">
|
|
COIN <span className="theme-accent">BLENDER</span>
|
|
</h1>
|
|
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
|
|
All the bells, all the whistles, zero real-world functionality.
|
|
</p>
|
|
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
|
|
Parody simulator: this does not mix real crypto, does not provide laundering tools, and never accepts deposits.
|
|
It only burns your in-game LUX credits in a dramatic way.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-12 grid grid-cols-1 gap-8 lg:grid-cols-3">
|
|
<div className="theme-card rounded-3xl border border-white/10 p-8 lg:col-span-2">
|
|
<div className="mb-6 flex items-end justify-between gap-4">
|
|
<div>
|
|
<div className="text-xs text-foreground/40">available</div>
|
|
<div className="font-orbitron text-4xl font-bold text-neon-green">
|
|
{luxCredits.toLocaleString()} LUX
|
|
</div>
|
|
</div>
|
|
<div className="text-right text-sm text-foreground/60">
|
|
{isConnected ? "Wallet connected (mock)" : "Connect wallet to enable the blender"}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
<div className="theme-card rounded-2xl border border-white/10 p-6">
|
|
<div className="font-bold mb-2">Amount (LUX)</div>
|
|
<input
|
|
type="range"
|
|
min={50}
|
|
max={2500}
|
|
value={amount}
|
|
onChange={(e) => setAmount(parseInt(e.target.value, 10))}
|
|
className="w-full"
|
|
disabled={!isConnected}
|
|
/>
|
|
<div className="mt-2 text-foreground/70">{amount.toLocaleString()} LUX</div>
|
|
|
|
<div className="mt-6 font-bold mb-2">Route hops</div>
|
|
<div className="grid grid-cols-5 gap-2">
|
|
{[1, 2, 3, 4, 5].map((n) => (
|
|
<button
|
|
key={n}
|
|
disabled={!isConnected}
|
|
onClick={() => setHops(n)}
|
|
className={`rounded-xl border px-3 py-2 text-sm font-bold ${
|
|
hops === n ? "border-neon-cyan bg-neon-cyan/10" : "border-white/10 bg-white/5"
|
|
}`}
|
|
>
|
|
{n}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-6 font-bold mb-2">Plausible deniability noise</div>
|
|
<input
|
|
type="range"
|
|
min={0}
|
|
max={100}
|
|
value={noise}
|
|
onChange={(e) => setNoise(parseInt(e.target.value, 10))}
|
|
className="w-full"
|
|
disabled={!isConnected}
|
|
/>
|
|
<div className="mt-2 text-foreground/70">{noise}% nonsense</div>
|
|
</div>
|
|
|
|
<div className="theme-card rounded-2xl border border-white/10 p-6">
|
|
<div className="font-orbitron text-2xl font-bold mb-4">Receipt</div>
|
|
<div className="space-y-3 text-sm text-foreground/70">
|
|
<div className="flex justify-between">
|
|
<span>Blend amount</span>
|
|
<span className="font-bold">{amount.toLocaleString()} LUX</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>Obfuscation fee</span>
|
|
<span className="font-bold text-neon-pink">{fee.toLocaleString()} LUX</span>
|
|
</div>
|
|
<div className="flex justify-between border-t border-white/10 pt-3">
|
|
<span>Total burn</span>
|
|
<span className="font-orbitron font-bold text-neon-pink">{total.toLocaleString()} LUX</span>
|
|
</div>
|
|
<div className="mt-4 rounded-xl bg-white/5 p-4 text-xs text-foreground/60">
|
|
Advanced features: onion routing animation, entropy fog, decoy receipts, and a fake compliance dashboard.
|
|
The only real output is regret.
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
disabled={!isConnected}
|
|
className="mt-6 w-full rounded-full bg-gradient-to-r from-neon-purple to-neon-pink px-6 py-3 font-bold text-background disabled:opacity-40"
|
|
onClick={() => {
|
|
const ok = spendLuxCredits(total);
|
|
if (!ok) {
|
|
push("Blend failed: insufficient LUX. The blender demands tribute.");
|
|
return;
|
|
}
|
|
setVaultFlag("blendedOnce", true);
|
|
addVaultReceipt({
|
|
id: `MX-${id()}`,
|
|
title: "Blender Session Log",
|
|
desc: `Receipt generated: ${amount.toLocaleString()} LUX blended with ${noise}% noise across ${hops} hops. Output: 0 LUX. Privacy achieved. Regret maximized.`,
|
|
date: new Date().toISOString().slice(0, 10),
|
|
severity: "redacted",
|
|
});
|
|
push(`Session ${id()}: accepted. Injecting ${noise}% noise…`);
|
|
push(`Session ${id()}: routing through ${hops} hops…`);
|
|
push(`Session ${id()}: generating decoy receipts…`);
|
|
push(`Session ${id()}: complete. Output: 0 LUX (privacy achieved).`);
|
|
}}
|
|
>
|
|
BLEND NOW 🌀
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="theme-card rounded-3xl border border-white/10 p-8">
|
|
<div className="font-orbitron text-2xl font-bold">Console</div>
|
|
<div className="mt-6 space-y-3">
|
|
{log.length === 0 ? (
|
|
<div className="text-foreground/60">No sessions yet. The blender is bored.</div>
|
|
) : (
|
|
log.map((e) => (
|
|
<div key={e.id} className="rounded-xl bg-black/40 border border-white/10 p-4 font-mono text-xs text-neon-green">
|
|
{e.text}
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</ThemedLayout>
|
|
);
|
|
}
|
|
|