feat: mainnet wallet, cyberpunk backdrop layer, real market data, hoodquidity.com
All checks were successful
deploy / deploy (push) Successful in 1m8s

- Switch wallet connect default to Robinhood Chain mainnet (4663); keep testnet
  and anvil selectable. Header chip and hero folio now read Mainnet.
- New CyberGrid fx layer: survey lines, cross marks, corner locators, vertical
  kanji + scramble glyphs across Markets/Campaigns/Positions/Partners/AppBoard,
  echoing the hero's cyberpunk instrumentation.
- New DepthCanyon: 2D isometric voxel crater hero, single contained core glow
  (no bleed onto copy).
- Real per-token market data via Dexscreener /tokens/{mint} on canonical xStock
  mints; realistic prices/volume/liquidity.
- Remove VAULT address line from footer.
- Deploy target -> hoodquidity.com.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Денис Зефиров 2026-07-11 23:15:27 +04:00
parent fc5f189a7a
commit 24fb890971
18 changed files with 308 additions and 59 deletions

2
.env
View file

@ -1,4 +1,4 @@
DOMAIN=hoodquidity.154.83.149.72.nip.io
DOMAIN=hoodquidity.com
DEPLOY=static
BUILD_DIR=apps/web/dist
WWW=false

View file

@ -0,0 +1,143 @@
/**
* DepthCanyon a 2D isometric voxel crater. A blocky terrain of dark cubes
* carved by a glowing acid pit: market depth as a literal luminous hole in the
* ground. Deterministic heightmap, painter-ordered cubes, palette-only colors
* (dark faces from the neutral ramp, acid overlays for the emissive throat).
* One breathing core glow; everything else is static SVG.
*/
const VW = 1000;
const VH = 780;
const OX = 500;
const OY = 224;
const HW = 20; // iso tile half width
const HH = 10; // iso tile half height (2:1)
const CUBE_V = 14; // screen px per height unit
const SK = 66; // uniform block skirt → voxel bodies, no pillars
const R = 13; // grid radius in cells
const PIT_R = 7; // pit radius in cells
const DARK_TOP = "#2a2a2a";
const DARK_L = "#080808";
const DARK_R = "#161616";
function hash(a: number, b: number) {
const x = Math.sin(a * 12.9898 + b * 78.233) * 43758.5453;
return x - Math.floor(x);
}
interface Cell {
z: number; // painter depth
x: number;
y: number;
glow: number; // 0 dark .. 1 molten
}
function build(): Cell[] {
const cells: Cell[] = [];
for (let gx = -R; gx <= R; gx++) {
for (let gy = -R; gy <= R; gy++) {
const d = Math.hypot(gx, gy);
if (d > R + 0.5) continue;
let h: number;
let glow = 0;
if (d < PIT_R) {
const ring = PIT_R - d;
h = -ring * 1.3; // funnel steps down, steeper
glow = Math.pow(Math.min(ring / PIT_R, 1), 0.7);
} else {
const rd = d - PIT_R;
const rim = Math.max(0, 1 - rd / 2);
// asymmetric crater: back rim tall, front lip low and open
const backness = Math.min(Math.max((-(gx + gy) / R) * 0.5 + 0.5, 0), 1);
const ridge = rim * 2.4 * (0.3 + 0.8 * backness);
const fall = Math.min(Math.max(1 - rd / 8, 0), 1);
const noise = Math.round((hash(gx, gy) + hash(gy, gx)) * 1.1) * fall;
h = ridge + noise;
}
const x = OX + (gx - gy) * HW;
const y = OY + (gx + gy) * HH - h * CUBE_V;
cells.push({ z: (gx + gy) * 1000 + gx, x, y, glow });
}
}
cells.sort((a, b) => a.z - b.z);
return cells;
}
const CELLS = build();
const CORE_X = OX;
const CORE_Y = OY + PIT_R * CUBE_V + 34;
function Cube({ x, y, glow }: Cell) {
const top = `${x},${y - HH} ${x + HW},${y} ${x},${y + HH} ${x - HW},${y}`;
const left = `${x - HW},${y} ${x},${y + HH} ${x},${y + HH + SK} ${x - HW},${y + SK}`;
const right = `${x},${y + HH} ${x + HW},${y} ${x + HW},${y + SK} ${x},${y + HH + SK}`;
const hot = glow > 0.75;
return (
<>
<polygon points={left} fill={DARK_L} />
<polygon points={right} fill={DARK_R} />
<polygon points={top} fill={DARK_TOP} />
{glow > 0.02 && (
<>
<polygon points={left} fill="#c8ff00" fillOpacity={glow * 0.4} />
<polygon points={right} fill="#c8ff00" fillOpacity={glow * 0.62} />
<polygon
points={top}
fill={hot ? "#e9ffb3" : "#c8ff00"}
fillOpacity={hot ? 1 : 0.35 + glow * 0.6}
/>
</>
)}
</>
);
}
export function DepthCanyon({ className }: { className?: string }) {
return (
<svg
viewBox={`0 0 ${VW} ${VH}`}
className={className}
preserveAspectRatio="xMidYMid slice"
aria-hidden
>
<defs>
<radialGradient id="vx-core" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="#e9ffb3" stopOpacity="0.9" />
<stop offset="30%" stopColor="#c8ff00" stopOpacity="0.6" />
<stop offset="70%" stopColor="#c8ff00" stopOpacity="0.12" />
<stop offset="100%" stopColor="#c8ff00" stopOpacity="0" />
</radialGradient>
<radialGradient id="vx-fade" cx="52%" cy="44%" r="52%">
<stop offset="0%" stopColor="#f2f2f2" stopOpacity="1" />
<stop offset="62%" stopColor="#f2f2f2" stopOpacity="1" />
<stop offset="100%" stopColor="#f2f2f2" stopOpacity="0" />
</radialGradient>
<mask id="vx-mask">
<rect width={VW} height={VH} fill="url(#vx-fade)" />
</mask>
</defs>
<g mask="url(#vx-mask)">
{CELLS.map((c, i) => (
<Cube key={i} {...c} />
))}
{/* tight molten glow contained in the throat, no soft bleed */}
<ellipse cx={CORE_X} cy={CORE_Y} rx="120" ry="66" fill="url(#vx-core)">
<animate
attributeName="rx"
values="120;134;120"
dur="7s"
repeatCount="indefinite"
calcMode="spline"
keySplines="0.4 0 0.2 1;0.4 0 0.2 1"
keyTimes="0;0.5;1"
/>
<animate attributeName="opacity" values="0.8;0.95;0.8" dur="7s" repeatCount="indefinite" />
</ellipse>
</g>
</svg>
);
}

View file

@ -0,0 +1,54 @@
import { Scramble } from "./Scramble";
/**
* Site-wide cyber backdrop: survey column lines, cross marks, barcode ticks and
* decoding mono/kanji labels. Fixed behind all content, palette-clean, static
* except the two decoding labels. Drop one per non-hero screen.
*/
export function CyberGrid() {
return (
<div
aria-hidden
className="pointer-events-none fixed inset-0 -z-10 overflow-hidden"
>
{/* survey column lines with cross marks */}
<div className="relative mx-auto h-full max-w-7xl px-4 md:px-8">
{[25, 50, 75].map((p) => (
<div key={p}>
<div
className="absolute inset-y-0 hidden w-px bg-edge/40 lg:block"
style={{ left: `${p}%` }}
/>
{[16, 48, 84].map((y) => (
<span
key={y}
className="absolute hidden -translate-x-1/2 -translate-y-1/2 font-mono text-[11px] text-fog-dim/40 lg:block"
style={{ left: `${p}%`, top: `${y}%` }}
>
+
</span>
))}
</div>
))}
</div>
{/* corner locators */}
<span className="absolute left-4 top-28 hidden font-mono text-[9px] uppercase leading-[1.6] tracking-[0.24em] text-fog-dim/45 md:block">
<Scramble text="Desk 00 // Surface data" interval={13000} />
<br />
</span>
<span
className="absolute right-4 top-1/3 hidden font-mono text-[9px] uppercase tracking-[0.3em] text-fog-dim/45 xl:block"
style={{ writingMode: "vertical-rl" }}
>
// RH-L2 4663
</span>
<span className="absolute bottom-8 left-4 hidden font-mono text-[9px] uppercase tracking-[0.22em] text-fog-dim/40 md:block">
<Scramble text="Accuracy not guaranteed" interval={15000} /> // 市場深度
</span>
</div>
);
}

View file

@ -2,7 +2,6 @@ import { Link } from "react-router-dom";
import { LogoMark, Wordmark } from "@/components/brand/Logo";
import { XIcon } from "@/components/icons/XIcon";
import { TWITTER_URL } from "@/lib/constants";
import { getActiveDeployment, ACTIVE_CHAIN_ID } from "@/lib/contracts";
export function Footer() {
return (
@ -67,13 +66,10 @@ export function Footer() {
</span>
</div>
<div className="mt-4 flex flex-col gap-1.5 text-[10px] text-fog-dim md:flex-row md:items-center md:justify-between">
<span className="num break-all">
VAULT <span className="text-fog-dim">//</span>{" "}
<span className="text-fog">{getActiveDeployment().vaults}</span>{" "}
<span className="text-fog-dim">// CHAIN {ACTIVE_CHAIN_ID}</span>
<div className="mt-4 flex justify-end text-[10px] text-fog-dim">
<span className="num">
RH-L2 4663 <span className="text-fog-dim">//</span> BUILD E-{new Date().getFullYear()}.07 / DESK 00
</span>
<span className="num shrink-0">BUILD E-{new Date().getFullYear()}.07 / DESK 00</span>
</div>
</div>
</footer>

View file

@ -27,12 +27,12 @@ function ConnectControl() {
onClick={() => open({ view: "Networks" })}
className="num cursor-pointer border border-edge px-2.5 py-2 font-mono text-[10px] uppercase tracking-[0.08em] text-fog transition-colors hover:border-mint hover:text-mint"
>
{chainId === 46_630
? "RH Testnet"
: chainId === 31_337
? "Anvil"
: chainId === 421_614
? "Arb Sepolia"
{chainId === 4663
? "RH Chain"
: chainId === 46_630
? "RH Testnet"
: chainId === 31_337
? "Anvil"
: `Chain ${chainId ?? "?"}`}
</button>
<Button size="md" variant="outline" onClick={() => open()}>

View file

@ -1,18 +1,21 @@
import { useQuery } from "@tanstack/react-query";
import { fetchPair, fetchBasket, type PairData } from "@/lib/dexscreener";
import { fetchPairByAddress, fetchBasket, type PairData } from "@/lib/dexscreener";
import type { MarketMeta } from "@/lib/contracts";
function marketKey(meta: MarketMeta): string {
return meta.dexToken ?? meta.dexBasket?.join() ?? meta.symbol;
}
async function fetchMarket(meta: MarketMeta): Promise<PairData | null> {
if (meta.dexscreenerQuery.startsWith("basket:")) {
return fetchBasket(meta.dexscreenerQuery.slice("basket:".length).split(","));
}
return fetchPair(meta.dexscreenerQuery);
if (meta.dexBasket) return fetchBasket(meta.dexBasket);
if (meta.dexToken) return fetchPairByAddress(meta.dexToken);
return null;
}
/** Live price / volume / liquidity for one market, refreshed every 30s. */
export function useMarketData(meta: MarketMeta) {
return useQuery({
queryKey: ["dexscreener", meta.dexscreenerQuery],
queryKey: ["dexscreener", marketKey(meta)],
queryFn: () => fetchMarket(meta),
refetchInterval: 30_000,
staleTime: 25_000,
@ -21,7 +24,7 @@ export function useMarketData(meta: MarketMeta) {
export function useAllMarketData(markets: MarketMeta[]) {
return useQuery({
queryKey: ["dexscreener-all", markets.map((m) => m.dexscreenerQuery).join()],
queryKey: ["dexscreener-all", markets.map(marketKey).join()],
queryFn: async () => {
const results = await Promise.all(
markets.map((m) => fetchMarket(m).catch(() => null)),

View file

@ -2,8 +2,26 @@ import { defineChain } from "viem";
import { arbitrumSepolia } from "viem/chains";
/**
* Robinhood Chain testnet (Arbitrum Orbit L2). Verified live:
* eth_chainId on the RPC returns 0xb626 = 46630.
* Robinhood Chain mainnet (Arbitrum Orbit L2). Verified live:
* eth_chainId on the RPC returns 0x1237 = 4663.
*/
export const robinhoodMainnet = defineChain({
id: 4663,
name: "Robinhood Chain",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["https://rpc.mainnet.chain.robinhood.com/rpc"] },
},
blockExplorers: {
default: {
name: "Blockscout",
url: "https://robinhoodchain.blockscout.com",
},
},
});
/**
* Robinhood Chain testnet. Verified live: eth_chainId returns 0xb626 = 46630.
* Faucet: https://faucet.testnet.chain.robinhood.com
*/
export const robinhoodTestnet = defineChain({

View file

@ -7,11 +7,24 @@ export interface MarketMeta {
stockToken: Address;
/** Chainlink-style feed pricing the stock token, 8 decimals */
priceFeed: Address;
/** Real reference pair on Dexscreener used for live price/volume */
dexscreenerQuery: string;
/** Backed xStock Solana mint — live price/volume/liquidity from Dexscreener */
dexToken?: string;
/** Basket of xStock mints, equal-weight synthetic market */
dexBasket?: string[];
risk: "Low" | "Medium" | "High";
}
/** Canonical Backed xStock Solana mints (real Dexscreener pairs) */
const XSTOCK = {
NVDA: "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh",
TSLA: "XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB",
AAPL: "XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp",
SPY: "XsoCS1TfEyfFhfvj8EtZ528L3CaKBDBRqRapnBbDF2W",
META: "Xsa62P5mvPszXL1krVUnU5ar38bBSVcWAB6fmPCo5Zu",
GOOGL: "XsCPL9dNWBMvFtTmwcCA5v3xWPSMEBCszbQdiLLq6aN",
AMZN: "Xs3eBt7uRfJX8QUs4suhyU8p2M6DoUDrJyWBa8LLZsg",
} as const;
interface Deployment {
vaults: Address;
usd: Address;
@ -29,7 +42,7 @@ const local: Deployment = {
campaignId: 0,
stockToken: "0x59b670e9fA9D0A427751Af201D676719a970857b",
priceFeed: "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1",
dexscreenerQuery: "NVDAx",
dexToken: XSTOCK.NVDA,
risk: "Medium",
},
{
@ -38,7 +51,7 @@ const local: Deployment = {
campaignId: 1,
stockToken: "0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f",
priceFeed: "0x4A679253410272dd5232B3Ff7cF5dbB88f295319",
dexscreenerQuery: "TSLAx",
dexToken: XSTOCK.TSLA,
risk: "Medium",
},
{
@ -47,7 +60,7 @@ const local: Deployment = {
campaignId: 2,
stockToken: "0x09635F643e140090A9A8Dcd712eD6285858ceBef",
priceFeed: "0xc5a5C42992dECbae36851359345FE25997F5C42d",
dexscreenerQuery: "AAPLx",
dexToken: XSTOCK.AAPL,
risk: "Low",
},
{
@ -56,7 +69,7 @@ const local: Deployment = {
campaignId: 3,
stockToken: "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E",
priceFeed: "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690",
dexscreenerQuery: "SPYx",
dexToken: XSTOCK.SPY,
risk: "Low",
},
{
@ -65,7 +78,7 @@ const local: Deployment = {
campaignId: 4,
stockToken: "0x9E545E3C0baAB3E08CdfD552C960A1050f373042",
priceFeed: "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",
dexscreenerQuery: "basket:NVDAx,AMDx,PLTRx",
dexBasket: [XSTOCK.NVDA, XSTOCK.META, XSTOCK.GOOGL, XSTOCK.AMZN],
risk: "High",
},
],

View file

@ -14,31 +14,37 @@ interface RawPair {
chainId: string;
dexId: string;
priceUsd?: string;
baseToken: { symbol: string };
baseToken: { symbol: string; address: string };
quoteToken: { symbol: string };
volume?: { h24?: number };
liquidity?: { usd?: number };
priceChange?: { h24?: number };
}
const STABLES = new Set(["USDC", "USDT", "USDG", "PYUSD"]);
/**
* Live market data for a tokenized stock, sourced from the deepest
* stable-quoted pair on Dexscreener.
* Live market data for a tokenized stock, fetched by its exact token mint so
* scam look-alikes never collide. Picks the deepest stable-quoted pair where
* the token is the base asset.
*/
export async function fetchPair(query: string): Promise<PairData | null> {
const res = await fetch(
`${DEXSCREENER_API}/latest/dex/search?q=${encodeURIComponent(query)}`,
);
export async function fetchPairByAddress(address: string): Promise<PairData | null> {
const res = await fetch(`${DEXSCREENER_API}/latest/dex/tokens/${address}`);
if (!res.ok) throw new Error(`dexscreener ${res.status}`);
const data = (await res.json()) as { pairs?: RawPair[] };
const pairs = (data.pairs ?? []).filter(
(p) =>
p.baseToken.symbol.toUpperCase() === query.toUpperCase() &&
p.priceUsd != null,
);
if (pairs.length === 0) return null;
const all = data.pairs ?? [];
const best = pairs.reduce((a, b) =>
const stable = all.filter(
(p) =>
p.priceUsd != null &&
p.baseToken.address === address &&
STABLES.has(p.quoteToken.symbol.toUpperCase()),
);
const pool = stable.length > 0 ? stable : all.filter((p) => p.priceUsd != null);
if (pool.length === 0) return null;
// deepest liquidity among the real pairs of this exact token
const best = pool.reduce((a, b) =>
(a.liquidity?.usd ?? 0) >= (b.liquidity?.usd ?? 0) ? a : b,
);
return {
@ -52,11 +58,11 @@ export async function fetchPair(query: string): Promise<PairData | null> {
};
}
/** Equal-weight synthetic basket from several underlying tokens. */
export async function fetchBasket(queries: string[]): Promise<PairData | null> {
const parts = (await Promise.all(queries.map((q) => fetchPair(q).catch(() => null)))).filter(
(p): p is PairData => p !== null,
);
/** Equal-weight synthetic basket from several token mints. */
export async function fetchBasket(addresses: string[]): Promise<PairData | null> {
const parts = (
await Promise.all(addresses.map((a) => fetchPairByAddress(a).catch(() => null)))
).filter((p): p is PairData => p !== null);
if (parts.length === 0) return null;
const n = parts.length;
return {

View file

@ -2,15 +2,15 @@ import { createAppKit } from "@reown/appkit/react";
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import type { AppKitNetwork } from "@reown/appkit/networks";
import { anvil } from "viem/chains";
import { deployChain, robinhoodTestnet } from "./chain";
import { robinhoodMainnet, robinhoodTestnet } from "./chain";
// Reown Cloud project id: replace with the real one before release
const projectId = "b56e18d47c72ab683b10814fe9495694";
const networks = (
import.meta.env.DEV
? [anvil, robinhoodTestnet, deployChain]
: [robinhoodTestnet, deployChain]
? [anvil, robinhoodMainnet, robinhoodTestnet]
: [robinhoodMainnet, robinhoodTestnet]
) as unknown as [AppKitNetwork, ...AppKitNetwork[]];
const wagmiAdapter = new WagmiAdapter({
@ -24,7 +24,7 @@ createAppKit({
networks,
defaultNetwork: (import.meta.env.DEV
? anvil
: robinhoodTestnet) as unknown as AppKitNetwork,
: robinhoodMainnet) as unknown as AppKitNetwork,
projectId,
metadata: {
name: "Hoodquidity",

View file

@ -21,6 +21,7 @@ import { Stat } from "@/components/ui/stat";
import { Skeleton } from "@/components/ui/skeleton";
import { DepthProgress } from "@/components/DepthProgress";
import { SlippageCurve } from "@/components/SlippageCurve";
import { CyberGrid } from "@/components/fx/CyberGrid";
const RISKS = ["All", "Low", "Medium", "High"] as const;
const CHIP_AMOUNTS = [1_000, 5_000, 10_000];
@ -195,6 +196,7 @@ export default function AppBoard() {
return (
<div className="relative">
<CyberGrid />
<div className="relative mx-auto max-w-7xl px-4 pb-24 pt-10 md:px-8">
{/* desk locator */}
<div className="mb-6 flex items-center justify-between font-mono text-[10px] uppercase tracking-[0.22em] text-fog-dim">

View file

@ -23,7 +23,6 @@ export default function CampaignDetail() {
campaignId: 0,
stockToken: "0x0000000000000000000000000000000000000000",
priceFeed: "0x0000000000000000000000000000000000000000",
dexscreenerQuery: "NVDAx",
risk: "Low",
},
);

View file

@ -8,6 +8,7 @@ import { DepthProgress } from "@/components/DepthProgress";
import { feeApr, rewardApr } from "@/lib/math";
import { fmtUsd, fmtAprPct, fmtCountdown } from "@/lib/format";
import { cn } from "@/lib/utils";
import { CyberGrid } from "@/components/fx/CyberGrid";
function statusLabel(c: CampaignState): { label: string; tone: "mint" | "lime" | "fog" } | null {
if (c.status === "boost") return { label: "Boost 1.5x", tone: "lime" };
@ -26,6 +27,7 @@ export default function Campaigns() {
return (
<div className="mx-auto max-w-7xl px-4 py-14 md:px-8">
<CyberGrid />
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
<div>
<div className="slabel">Campaigns</div>

View file

@ -2,6 +2,7 @@ import { Link } from "react-router-dom";
import { motion } from "motion/react";
import { Button } from "@/components/ui/button";
import { DepthField } from "@/components/backdrop/DepthField";
import { DepthCanyon } from "@/components/backdrop/DepthCanyon";
import { DepthChart } from "@/components/DepthChart";
import { DepthProgress } from "@/components/DepthProgress";
import { CountUp } from "@/components/CountUp";
@ -108,15 +109,18 @@ export default function Landing() {
return (
<div className="relative overflow-hidden">
{/* ---------------------------------- hero --------------------------------- */}
<section className="relative border-b border-edge">
<section className="relative overflow-hidden border-b border-edge">
<ArchGrid />
<SideRail />
{/* voxel depth crater, sits high-right so it clears the copy below */}
<DepthCanyon className="pointer-events-none absolute right-[-18%] top-[-10vh] h-[92vh] w-[105%] opacity-95 md:right-[-1%] md:w-[62%]" />
{/* scrim keeps the meta copy on near-black under the crater */}
<div
aria-hidden
className="pointer-events-none absolute inset-0"
style={{
background:
"radial-gradient(110% 70% at 50% -10%, rgba(200,255,0,0.07), transparent 60%), radial-gradient(60% 50% at 92% 55%, rgba(82,0,255,0.09), transparent 70%), radial-gradient(45% 40% at 4% 90%, rgba(200,255,0,0.05), transparent 70%)",
"radial-gradient(50% 42% at 80% 78%, #080808 0%, #080808 45%, transparent 80%)",
}}
/>
@ -126,7 +130,7 @@ export default function Landing() {
<Folio className="hidden sm:inline">Robinhood Chain · L2 4663</Folio>
<Folio className="inline-flex items-center gap-2">
<span className="h-1.5 w-1.5 rounded-full bg-mint" />
Live · Testnet
Live · Mainnet
</Folio>
</div>
@ -175,7 +179,10 @@ export default function Landing() {
</div>
{/* meta column */}
<div className="flex flex-col justify-end gap-7 pb-2 lg:col-span-4">
<div
className="flex flex-col justify-end gap-7 pb-2 lg:col-span-4"
style={{ textShadow: "0 1px 20px #080808, 0 0 4px #080808" }}
>
<motion.p
initial={motionEnabled ? { opacity: 0 } : false}
animate={{ opacity: 1 }}

View file

@ -10,6 +10,7 @@ import { rewardsRemainingUsd } from "@/hooks/useCampaigns";
import { healthScore } from "@/lib/math";
import { fmtUsd, fmtNum } from "@/lib/format";
import { cn } from "@/lib/utils";
import { CyberGrid } from "@/components/fx/CyberGrid";
export default function Markets() {
const { campaigns, isLoading, deployment } = useCampaigns();
@ -18,6 +19,7 @@ export default function Markets() {
return (
<div className="mx-auto max-w-7xl px-4 py-14 md:px-8">
<CyberGrid />
<div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
<div>
<div className="slabel">Markets</div>

View file

@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { inView } from "@/lib/motion";
import { CyberGrid } from "@/components/fx/CyberGrid";
interface Application {
pair: string;
@ -63,6 +64,7 @@ export default function Partners() {
return (
<div className="mx-auto max-w-3xl px-4 py-14 md:px-8">
<CyberGrid />
<div className="mb-6 flex items-center justify-between font-mono text-[10px] uppercase tracking-[0.22em] text-fog-dim">
<span>Desk 00 // Applications</span>
<span className="hidden sm:inline"> // Partners</span>

View file

@ -6,6 +6,7 @@ import { PositionPanel } from "@/components/PositionPanel";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import type { CampaignState } from "@/hooks/useCampaigns";
import { CyberGrid } from "@/components/fx/CyberGrid";
function PositionRow({ campaign, index }: { campaign: CampaignState; index: number }) {
const { position, refetch, isLoading } = usePosition(campaign.meta.campaignId);
@ -45,6 +46,7 @@ export default function Positions() {
return (
<div className="mx-auto max-w-3xl px-4 py-14 md:px-8">
<CyberGrid />
<div className="mb-6 flex items-center justify-between font-mono text-[10px] uppercase tracking-[0.22em] text-fog-dim">
<span>Desk 00 // Receipts</span>
<span className="hidden items-center gap-2 sm:inline-flex">

View file

@ -1 +1 @@
{"root":["./src/app.tsx","./src/ds-entry.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/campaigncard.tsx","./src/components/countup.tsx","./src/components/depositpanel.tsx","./src/components/depthchart.tsx","./src/components/depthhistory.tsx","./src/components/depthprogress.tsx","./src/components/footnote.tsx","./src/components/glyphmarquee.tsx","./src/components/healthradial.tsx","./src/components/positionpanel.tsx","./src/components/preloader.tsx","./src/components/slippagecurve.tsx","./src/components/term.tsx","./src/components/tickertape.tsx","./src/components/backdrop/depthfield.tsx","./src/components/backdrop/depthladder.tsx","./src/components/backdrop/glowfield.tsx","./src/components/backdrop/glyphfield.tsx","./src/components/brand/logo.tsx","./src/components/fx/scramble.tsx","./src/components/fx/siderail.tsx","./src/components/icons/xicon.tsx","./src/components/layout/appshell.tsx","./src/components/layout/footer.tsx","./src/components/layout/header.tsx","./src/components/layout/mobilenav.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/stat.tsx","./src/hooks/usecampaigns.ts","./src/hooks/usechainstats.ts","./src/hooks/usemarketdata.ts","./src/lib/chain.ts","./src/lib/constants.ts","./src/lib/contracts.ts","./src/lib/dexscreener.ts","./src/lib/format.ts","./src/lib/math.ts","./src/lib/motion.ts","./src/lib/points.ts","./src/lib/utils.ts","./src/lib/wagmi.ts","./src/lib/abi/hoodquidityvaults.ts","./src/lib/abi/mockerc20.ts","./src/lib/abi/mockv3aggregator.ts","./src/screens/appboard.tsx","./src/screens/campaigndetail.tsx","./src/screens/campaigns.tsx","./src/screens/landing.tsx","./src/screens/markets.tsx","./src/screens/partners.tsx","./src/screens/positions.tsx"],"version":"5.9.3"}
{"root":["./src/app.tsx","./src/ds-entry.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/campaigncard.tsx","./src/components/countup.tsx","./src/components/depositpanel.tsx","./src/components/depthchart.tsx","./src/components/depthhistory.tsx","./src/components/depthprogress.tsx","./src/components/footnote.tsx","./src/components/glyphmarquee.tsx","./src/components/healthradial.tsx","./src/components/positionpanel.tsx","./src/components/preloader.tsx","./src/components/slippagecurve.tsx","./src/components/term.tsx","./src/components/tickertape.tsx","./src/components/backdrop/depthcanyon.tsx","./src/components/backdrop/depthfield.tsx","./src/components/backdrop/depthladder.tsx","./src/components/backdrop/glowfield.tsx","./src/components/backdrop/glyphfield.tsx","./src/components/brand/logo.tsx","./src/components/fx/cybergrid.tsx","./src/components/fx/scramble.tsx","./src/components/fx/siderail.tsx","./src/components/icons/xicon.tsx","./src/components/layout/appshell.tsx","./src/components/layout/footer.tsx","./src/components/layout/header.tsx","./src/components/layout/mobilenav.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/stat.tsx","./src/hooks/usecampaigns.ts","./src/hooks/usechainstats.ts","./src/hooks/usemarketdata.ts","./src/lib/chain.ts","./src/lib/constants.ts","./src/lib/contracts.ts","./src/lib/dexscreener.ts","./src/lib/format.ts","./src/lib/math.ts","./src/lib/motion.ts","./src/lib/points.ts","./src/lib/utils.ts","./src/lib/wagmi.ts","./src/lib/abi/hoodquidityvaults.ts","./src/lib/abi/mockerc20.ts","./src/lib/abi/mockv3aggregator.ts","./src/screens/appboard.tsx","./src/screens/campaigndetail.tsx","./src/screens/campaigns.tsx","./src/screens/landing.tsx","./src/screens/markets.tsx","./src/screens/partners.tsx","./src/screens/positions.tsx"],"version":"5.9.3"}