diff --git a/frontend/app/src/app/neri/page.tsx b/frontend/app/src/app/neri/page.tsx new file mode 100644 index 000000000..a1c99d7ba --- /dev/null +++ b/frontend/app/src/app/neri/page.tsx @@ -0,0 +1,5 @@ +import { ClaimScreen } from "@/src/screens/ClaimScreen/ClaimScreen"; + +export default function Page() { + return ; +} diff --git a/frontend/app/src/comps/Screen/Screen.tsx b/frontend/app/src/comps/Screen/Screen.tsx index 0696dc40f..57acd9bec 100644 --- a/frontend/app/src/comps/Screen/Screen.tsx +++ b/frontend/app/src/comps/Screen/Screen.tsx @@ -89,49 +89,50 @@ export function Screen({ }, }); - const headingElt = typeof heading === "object" - && heading !== null - && "title" in heading - && !isValidElement(heading) - ? ( -
-

- {heading.title} -

- {heading.subtitle && ( -
- {heading.subtitle} -
- )} -
- ) - : ( -
- {heading} -
- ); + {heading.title} + + {heading.subtitle && ( +
+ {heading.subtitle} +
+ )} + + ) + : ( +
+ {heading} +
+ ); return (
; +} import Link from "next/link"; import { AccountButton } from "./AccountButton"; import { Menu } from "./Menu"; @@ -34,6 +40,7 @@ const menuItems: ComponentProps["menuItems"] = [ // [content.menu.multiply, "/multiply", IconLeverage, "multiply"], [content.menu.earn, "/earn", IconEarn, "earn", "_self"], [content.menu.ecosystem, "/ecosystem", IconEcosystem, "ecosystem", "_self"], + [content.menu.claim, "/neri", IconNeri, "neri", "_self"], [content.menu.stream, "https://app.superfluid.org/", IconStream, "stream", "_blank"], // [content.menu.stake, "/stake", IconStake, "stake"], // [content.menu.buy, buyPageUrl, IconStake, "buy", buyPageTarget], diff --git a/frontend/app/src/content.tsx b/frontend/app/src/content.tsx index b15f6092a..942ddbfac 100644 --- a/frontend/app/src/content.tsx +++ b/frontend/app/src/content.tsx @@ -17,6 +17,7 @@ export default { buy: "Buy USND", stream: "Stream", ecosystem: "Ecosystem", + claim: "NERI", }, accountButton: { diff --git a/frontend/app/src/contracts.ts b/frontend/app/src/contracts.ts index 29b16ee46..8b0a59f5b 100644 --- a/frontend/app/src/contracts.ts +++ b/frontend/app/src/contracts.ts @@ -163,6 +163,7 @@ export const CONTRACT_ADDRESSES = { WETH: CONTRACT_WETH, YUSND: CONTRACT_YUSND, ShellToken: CONTRACT_SHELL_TOKEN, + NeriToken: "0x0000000000000000000000000000000000000000", // TODO: Replace with actual NERI token address GoSlowNft: "0x6da3c02293c96dfa5747b1739ebb492619222a8a", strategies: { diff --git a/frontend/app/src/screens/ClaimScreen/ClaimScreen.css b/frontend/app/src/screens/ClaimScreen/ClaimScreen.css new file mode 100644 index 000000000..fb99aa30d --- /dev/null +++ b/frontend/app/src/screens/ClaimScreen/ClaimScreen.css @@ -0,0 +1,71 @@ +/* Floating snail animation */ +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(-3deg); + } + 50% { + transform: translateY(-15px) rotate(3deg); + } +} + +.floating-snail { + position: absolute; + animation: float ease-in-out infinite; +} + +/* Hero snail float animation */ +.hero-snail { + position: relative; + animation: float 4s ease-in-out infinite; +} + +/* Sparkle animation */ +@keyframes sparkle { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.5; + transform: scale(1.2); + } +} + +.sparkle { + position: absolute; + width: 8px; + height: 8px; + background: linear-gradient(135deg, #FFD700, #FFA500); + border-radius: 50%; + animation: sparkle ease-in-out infinite; +} + +/* Confetti falling animation */ +@keyframes confetti-fall { + 0% { + transform: translateY(-100vh) rotate(0deg); + opacity: 1; + } + 100% { + transform: translateY(100vh) rotate(720deg); + opacity: 0; + } +} + +.confetti-piece { + position: absolute; + width: 10px; + height: 10px; + border-radius: 2px; + animation: confetti-fall linear forwards; +} + +/* NERI amount gradient text */ +.neri-amount { + font-size: 36px; + font-weight: 700; + background: linear-gradient(135deg, #FFD700 0%, #FFA500 50%, #FF8C00 100%); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} diff --git a/frontend/app/src/screens/ClaimScreen/ClaimScreen.tsx b/frontend/app/src/screens/ClaimScreen/ClaimScreen.tsx new file mode 100644 index 000000000..9ec543987 --- /dev/null +++ b/frontend/app/src/screens/ClaimScreen/ClaimScreen.tsx @@ -0,0 +1,790 @@ +"use client"; + +import { Screen } from "@/src/comps/Screen/Screen"; +import { ConnectWarningBox } from "@/src/comps/ConnectWarningBox/ConnectWarningBox"; +import { useAccount } from "@/src/services/Arbitrum"; +import { css } from "@/styled-system/css"; +import { a, useSpring, useTrail } from "@react-spring/web"; +import { Button, VFlex, ShellpointIcon, TokenIcon, IconDiscord, IconX } from "@liquity2/uikit"; +import { useState, useMemo, useEffect } from "react"; +import { useBalance } from "@/src/wagmi-utils"; +import { useShellActivitiesOfHolders, useWeightedActivitySnapshots, usePrivacyPoolSnapshots, useTotalShells, useNeriTotalSupply, calculateNeriAllocation } from "@/src/shell-hooks"; +import { CONTRACT_ADDRESSES } from "@/src/contracts"; +import { getAddress, isAddress, isAddressEqual } from "viem"; +import { getEnsAddress, normalize } from "viem/ens"; +import { format } from "dnum"; +import { DNUM_0 } from "@/src/dnum-utils"; +import { getMainnetPublicClient } from "@/src/shellpoints/utils/client"; +import "./ClaimScreen.css"; +import type { Address } from "@/src/types"; + +const SNAILS = [ + { src: "/cute-snails/blue.png", name: "Blue Snail" }, + { src: "/cute-snails/green.png", name: "Green Snail" }, + { src: "/cute-snails/red.png", name: "Red Snail" }, + { src: "/cute-snails/tiger.png", name: "Tiger Snail" }, + { src: "/cute-snails/brown.png", name: "Brown Snail" }, + { src: "/cute-snails/battle.png", name: "Battle Snail" }, +]; + +const CONFETTI_COLORS = ["#FFD700", "#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"]; + +type ActivityLabel = + | "yUSND" + | "Balancer" + | "Bunni" + | "Camelot" + | "Spectra" + | "Uniswap" + | "GoSlow NFT" + | "Borrowing" + | "Stability Pool"; + +function getActivityLabel(token: string): ActivityLabel | null { + const addr = token.toLowerCase(); + if (addr === CONTRACT_ADDRESSES.YUSND.toLowerCase()) return "yUSND"; + if (addr === CONTRACT_ADDRESSES.strategies.Balancer.toLowerCase()) return "Balancer"; + if (addr === CONTRACT_ADDRESSES.strategies.Balancer2.toLowerCase()) return "Balancer"; + if (addr === CONTRACT_ADDRESSES.strategies.Balancer3.toLowerCase()) return "Balancer"; + if (addr === CONTRACT_ADDRESSES.strategies.Bunni.toLowerCase()) return "Bunni"; + if (addr === CONTRACT_ADDRESSES.strategies.Camelot.toLowerCase()) return "Camelot"; + if (addr === CONTRACT_ADDRESSES.strategies.Spectra.toLowerCase()) return "Spectra"; + if (addr === CONTRACT_ADDRESSES.GoSlowNft.toLowerCase()) return "GoSlow NFT"; + if (addr === CONTRACT_ADDRESSES.strategies.UniswapV4.toLowerCase()) return "Uniswap"; + return null; +} + +// Map subgraph activity labels to human-readable names +function formatActivityLabel(label: string): string { + if (label.startsWith("SP_")) return "Stability Pool"; + if (label.startsWith("TROVE_")) return "Borrowing"; + if (label === "yUSND" || label === "YUSND") return "yUSND"; + if (label.toLowerCase().includes("balancer")) return "Balancer"; + if (label.toLowerCase().includes("bunni")) return "Bunni"; + if (label.toLowerCase().includes("camelot")) return "Camelot"; + if (label.toLowerCase().includes("spectra")) return "Spectra"; + if (label.toLowerCase().includes("uniswap")) return "Uniswap"; + if (label.toLowerCase().includes("goslow")) return "GoSlow NFT"; + return label; +} + +export function ClaimScreen() { + const account = useAccount(); + const [revealed, setRevealed] = useState(false); + const [showConfetti, setShowConfetti] = useState(false); + const [customAddress, setCustomAddress] = useState(""); + const [resolvedEnsAddress, setResolvedEnsAddress] = useState
(null); + const [isResolvingEns, setIsResolvingEns] = useState(false); + const [ensError, setEnsError] = useState(null); + + // Check if input looks like an ENS name + const isEnsName = customAddress.includes(".") && !isAddress(customAddress); + + // Resolve ENS names + useEffect(() => { + if (!isEnsName) { + setResolvedEnsAddress(null); + setEnsError(null); + return; + } + + let cancelled = false; + setIsResolvingEns(true); + setEnsError(null); + + (async () => { + try { + const client = getMainnetPublicClient(); + const address = await getEnsAddress(client, { name: normalize(customAddress) }); + if (!cancelled) { + if (address) { + setResolvedEnsAddress(address); + setEnsError(null); + } else { + setResolvedEnsAddress(null); + setEnsError("ENS name not found"); + } + } + } catch { + if (!cancelled) { + setResolvedEnsAddress(null); + setEnsError("Invalid ENS name"); + } + } finally { + if (!cancelled) { + setIsResolvingEns(false); + } + } + })(); + + return () => { + cancelled = true; + }; + }, [customAddress, isEnsName]); + + // Use custom address if valid, otherwise connected wallet + const lookupAddress: Address | undefined = useMemo(() => { + if (customAddress && isAddress(customAddress)) { + return getAddress(customAddress); + } + if (resolvedEnsAddress) { + return resolvedEnsAddress; + } + return account.address; + }, [customAddress, resolvedEnsAddress, account.address]); + + const isCustomLookup = customAddress && (isAddress(customAddress) || resolvedEnsAddress); + + // Fetch shell balance for lookup address + const { data: shellBalance } = useBalance(lookupAddress, "SHELL"); + + // Fetch activities for lookup address + const { data: allActivities, isLoading: isLoadingActivities } = useShellActivitiesOfHolders( + lookupAddress ? [lookupAddress] : undefined + ); + + // Fetch weighted activity snapshots (bonus weights) + const { data: weightedSnapshots } = useWeightedActivitySnapshots(lookupAddress); + + // Fetch privacy pool snapshots (GoSlow NFT) + const { data: privacyPoolSnapshots } = usePrivacyPoolSnapshots(lookupAddress); + + // Fetch total shells for NERI calculation + const { data: totalShells } = useTotalShells(); + + // Fetch NERI total supply from contract + const { data: neriTotalSupply } = useNeriTotalSupply(); + + // Calculate NERI allocation based on user's shell balance, total shells, and NERI supply + const neriAllocation = useMemo(() => { + if (!shellBalance || !totalShells || totalShells === 0n || !neriTotalSupply) return 0n; + const userShells = shellBalance[0]; + return calculateNeriAllocation(userShells, totalShells, neriTotalSupply); + }, [shellBalance, totalShells, neriTotalSupply]); + + // Get aggregated stats from weighted snapshots + const activityStats = useMemo(() => { + if (!weightedSnapshots || weightedSnapshots.length === 0) return null; + + // Group by formatted activity label and sum up values + const byActivity = new Map(); + + for (const snap of weightedSnapshots as Array<{ activityLabel: string; baseValue: string; weightedValue: string; weight: number; multiplier: number }>) { + const formattedLabel = formatActivityLabel(snap.activityLabel); + const existing = byActivity.get(formattedLabel); + if (existing) { + existing.baseValue += BigInt(snap.baseValue); + existing.weightedValue += BigInt(snap.weightedValue); + existing.weight = Math.max(existing.weight, snap.weight); + existing.multiplier = Math.max(existing.multiplier, snap.multiplier); + } else { + byActivity.set(formattedLabel, { + baseValue: BigInt(snap.baseValue), + weightedValue: BigInt(snap.weightedValue), + weight: snap.weight, + multiplier: snap.multiplier, + }); + } + } + + return Array.from(byActivity.entries()).map(([label, stats]) => ({ + label, + ...stats, + })); + }, [weightedSnapshots]); + + // Check if user has GoSlow NFT multiplier + const hasGoSlowNft = privacyPoolSnapshots && privacyPoolSnapshots.length > 0; + + // Get user's activities from both sources + const userActivities = useMemo(() => { + const activities = new Set(); + + // From shell activities (strategy tokens) + if (lookupAddress && allActivities) { + allActivities + .filter((activity) => isAddressEqual(getAddress(activity.holder), lookupAddress)) + .map((activity) => getActivityLabel(activity.token)) + .filter((label): label is ActivityLabel => label !== null) + .forEach((label) => activities.add(label)); + } + + // From weighted snapshots (activity labels) + if (weightedSnapshots) { + weightedSnapshots.forEach((snap: { activityLabel: string }) => { + if (snap.activityLabel) { + activities.add(formatActivityLabel(snap.activityLabel)); + } + }); + } + + return Array.from(activities); + }, [lookupAddress, allActivities, weightedSnapshots]); + + // Generate stable confetti positions + const confettiPieces = useMemo(() => + Array.from({ length: 150 }).map((_, i) => ({ + left: `${(i * 7) % 100}%`, + color: CONFETTI_COLORS[i % CONFETTI_COLORS.length], + delay: `${(i * 0.02) % 0.8}s`, + duration: `${1.5 + (i % 4)}s`, + })), + []); + + // Animated snails floating around + const snailTrail = useTrail(SNAILS.length, { + from: { opacity: 0, transform: "scale(0) rotate(-180deg)" }, + to: { opacity: 1, transform: "scale(1) rotate(0deg)" }, + config: { mass: 1, tension: 280, friction: 20 }, + }); + + // Main card animation + const cardSpring = useSpring({ + from: { opacity: 0, transform: "scale(0.8) translateY(50px)" }, + to: { opacity: 1, transform: "scale(1) translateY(0px)" }, + delay: 300, + config: { mass: 1, tension: 200, friction: 20 }, + }); + + // Shell amount animation + const shellAmountSpring = useSpring({ + from: { number: 0 }, + to: { number: revealed && shellBalance ? Number(format(shellBalance, 0).replace(/,/g, '')) : 0 }, + delay: revealed ? 200 : 0, + config: { mass: 1, tension: 80, friction: 30 }, + }); + + // NERI amount animation + const neriAmountSpring = useSpring({ + from: { number: 0 }, + to: { number: revealed && neriAllocation ? Number(neriAllocation / 10n ** 18n) : 0 }, + delay: revealed ? 400 : 0, + config: { mass: 1, tension: 80, friction: 30 }, + }); + + const handleReveal = () => { + setRevealed(true); + setShowConfetti(true); + }; + + // Auto-hide confetti after animation + useEffect(() => { + if (showConfetti) { + const timer = setTimeout(() => setShowConfetti(false), 4000); + return () => clearTimeout(timer); + } + }, [showConfetti]); + + // Reset reveal state when address changes + useEffect(() => { + setRevealed(false); + }, [lookupAddress]); + + const hasShells = shellBalance && shellBalance[0] > 0n; + const canReveal = lookupAddress && (account.isConnected || isCustomLookup); + + return ( +
+ {/* Confetti celebration */} + {showConfetti && ( +
+ {confettiPieces.map((piece, i) => ( +
+ ))} +
+ )} + + {/* Floating snails background */} +
+ {snailTrail.map((style, index) => { + const snail = SNAILS[index]; + if (!snail) return null; + return ( + = 3 ? `${5 + (index - 3) * 3}%` : undefined, + animationDelay: `${index * 0.3}s`, + animationDuration: `${3 + index * 0.5}s`, + }} + > + {snail.name} + + ); + })} +
+ + + + + {/* Hero snail */} +
+
+ NERI Snail +
+
+ + {/* Address lookup input */} +
+ + setCustomAddress(e.target.value)} + className={css({ + width: "100%", + padding: "10px 14px", + fontSize: 13, + fontFamily: "monospace", + background: "secondary", + border: "1px solid", + borderColor: (customAddress && !isAddress(customAddress) && !isEnsName) || ensError ? "negative" : resolvedEnsAddress ? "positive" : "separator", + borderRadius: 8, + color: "content", + outline: "none", + transition: "border-color 0.2s", + _focus: { + borderColor: "accent", + }, + _placeholder: { + color: "contentAlt", + opacity: 0.6, + }, + })} + /> + {customAddress && !isAddress(customAddress) && !isEnsName && ( + + Invalid address format + + )} + {isEnsName && isResolvingEns && ( + + Resolving {customAddress}... + + )} + {isEnsName && ensError && !isResolvingEns && ( + + {ensError} + + )} + {isEnsName && resolvedEnsAddress && !isResolvingEns && ( + + {customAddress} → {resolvedEnsAddress.slice(0, 6)}...{resolvedEnsAddress.slice(-4)} + + )} + {isCustomLookup && isAddress(customAddress) && ( + + Looking up: {customAddress.slice(0, 6)}...{customAddress.slice(-4)} + + )} +
+ + {/* Main reveal card */} +
+ + {/* NERI Allocation */} +
+ + {isCustomLookup ? "NERI Allocation" : "Your NERI Allocation"} + +
+ {revealed ? "Revealed" : "Ready to reveal"} +
+
+ +
+ {revealed ? ( + <> + + {neriAmountSpring.number.to((n) => n.toLocaleString('en-US', { maximumFractionDigits: 0 }))} + + + + ) : ( + <> + + ??? + + + + )} +
+ + {canReveal && ( +
+ Click reveal to see the NERI allocation! +
+ )} + + {!lookupAddress && !account.isConnected && ( +
+ Enter an address above or connect your wallet. +
+ )} +
+
+ + {/* Shells breakdown card */} + {lookupAddress && ( +
+ +
+ + {isCustomLookup ? "Shells Earned" : "Your Shells Earned"} + +
+ {revealed ? ( + + {shellAmountSpring.number.to((n) => n.toLocaleString('en-US', { maximumFractionDigits: 0 }))} + + ) : ( + + {hasShells ? format(shellBalance ?? DNUM_0, 0) : "0"} + + )} + +
+
+ + {/* Activities breakdown */} + {userActivities.length > 0 && ( +
+
+ Earned from: +
+
+ {userActivities.map((activity) => ( +
+ {activity} +
+ ))} +
+
+ )} + + {userActivities.length === 0 && hasShells && isLoadingActivities && ( +
+ Loading activities... +
+ )} + + {/* Bonus stats */} + {(activityStats || hasGoSlowNft) && ( +
+
+ Bonus multipliers: +
+
+ {activityStats?.map((stat) => { + // Weight is in basis points: 1000 = 1x, 1500 = 1.5x + const multiplier = stat.weight / 1000; + if (multiplier <= 1) return null; + return ( +
+ + {stat.label} + + + {multiplier}x + +
+ ); + })} + {hasGoSlowNft && ( +
+ + GoSlow NFT + + + Bonus! + +
+ )} +
+
+ )} +
+
+ )} + + {!isCustomLookup && } + + {!revealed && canReveal && ( +
+ ); +} diff --git a/frontend/app/src/shell-hooks.ts b/frontend/app/src/shell-hooks.ts index 036d90a4e..5dcfca4db 100644 --- a/frontend/app/src/shell-hooks.ts +++ b/frontend/app/src/shell-hooks.ts @@ -8,7 +8,7 @@ import { BalancesByTokenQuery, BalancesForHoldersQuery, } from "./shell-queries"; -import { CONTRACT_SHELL_TOKEN } from "./env"; +import { CONTRACT_SHELL_TOKEN, SHELL_SUBGRAPH_URL } from "./env"; import { getUniswapPositionsByOwners, PoolKey } from "./uniswap-hooks"; import { readContracts } from "@wagmi/core"; import { useWagmiConfig } from "@/src/services/Arbitrum"; @@ -17,6 +17,12 @@ import { UniswapV4PositionManager } from "./abi/UniswapV4PositionManager"; import { Abi } from "abitype"; import { isAddressEqual, zeroAddress } from "viem"; + +// NERI Airdrop Configuration +export const NERI_CONFIG = { + SHELL_ALLOCATION_PERCENT: 10, +}; + type Options = { refetchInterval?: number; }; @@ -120,4 +126,139 @@ export function useShellActivitiesOfHolders( enabled: Boolean(holders && holders.length > 0), ...prepareOptions(options), }); +} + +export function useWeightedActivitySnapshots( + user?: Address, + options?: Options, +) { + return useQuery({ + queryKey: ["WeightedActivitySnapshots", user], + queryFn: async () => { + const response = await fetch(SHELL_SUBGRAPH_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: ` + query WeightedActivitySnapshots($user: Bytes!) { + weightedActivitySnapshots( + where: { user: $user } + orderBy: blockTimestamp + orderDirection: desc + ) { + id + user + activityLabel + baseValue + weight + weightedValue + multiplier + blockTimestamp + } + } + `, + variables: { user: user?.toLowerCase() }, + }), + }); + const result = await response.json(); + return result.data?.weightedActivitySnapshots ?? []; + }, + enabled: Boolean(user), + ...prepareOptions(options), + }); +} + +export function usePrivacyPoolSnapshots( + user?: Address, + options?: Options, +) { + return useQuery({ + queryKey: ["PrivacyPoolSnapshots", user], + queryFn: async () => { + const response = await fetch(SHELL_SUBGRAPH_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: ` + query PrivacyPoolSnapshots($user: Bytes!) { + privacyPoolSnapshots( + where: { user: $user } + orderBy: blockTimestamp + orderDirection: desc + ) { + id + user + depositValue + blockTimestamp + } + } + `, + variables: { user: user?.toLowerCase() }, + }), + }); + const result = await response.json(); + return result.data?.privacyPoolSnapshots ?? []; + }, + enabled: Boolean(user), + ...prepareOptions(options), + }); +} + +// Get total shells across all holders +export function useTotalShells(options?: Options) { + const { data: allBalances, ...rest } = useShellBalances(options); + + const totalShells = allBalances?.reduce((sum, balance) => { + return sum + BigInt(balance.balance); + }, 0n) ?? 0n; + + return { + data: totalShells, + ...rest, + }; +} + +// Hook to get NERI total supply from contract +export function useNeriTotalSupply(options?: Options) { + const config = useWagmiConfig(); + + return useQuery({ + queryKey: ["NeriTotalSupply"], + queryFn: async () => { + const result = await readContracts(config, { + allowFailure: false, + contracts: [{ + address: CONTRACT_ADDRESSES.NeriToken as `0x${string}`, + abi: [{ + name: "totalSupply", + type: "function", + stateMutability: "view", + inputs: [], + outputs: [{ type: "uint256" }], + }] as const, + functionName: "totalSupply", + args: [], + }], + }); + return result[0] as bigint; + }, + // Don't fetch if NERI token address is not set (zero address) + enabled: CONTRACT_ADDRESSES.NeriToken !== "0x0000000000000000000000000000000000000000", + ...prepareOptions(options), + }); +} + +// Calculate NERI amount for a given shell balance +export function calculateNeriAllocation( + userShells: bigint, + totalShells: bigint, + neriTotalSupply: bigint +): bigint { + if (totalShells === 0n || neriTotalSupply === 0n) return 0n; + + // NERI allocated to shell holders = neriTotalSupply * SHELL_ALLOCATION_PERCENT / 100 + const neriForShells = neriTotalSupply * BigInt(NERI_CONFIG.SHELL_ALLOCATION_PERCENT) / 100n; + + // User's NERI = (userShells / totalShells) * neriForShells + return (userShells * neriForShells) / totalShells; } \ No newline at end of file diff --git a/frontend/app/src/shell-queries.ts b/frontend/app/src/shell-queries.ts index b637c03f5..5267a9a12 100644 --- a/frontend/app/src/shell-queries.ts +++ b/frontend/app/src/shell-queries.ts @@ -76,4 +76,7 @@ export const BalancesForHoldersQuery = graphql(` balance } } -`) \ No newline at end of file +`) + +// Note: WeightedActivitySnapshots and PrivacyPoolSnapshots queries are +// executed via raw fetch in shell-hooks.ts since they're not in the codegen schema \ No newline at end of file diff --git a/frontend/uikit/src/token-icons/neri.svg b/frontend/uikit/src/token-icons/neri.svg index 841347b4e..baae03c85 100644 --- a/frontend/uikit/src/token-icons/neri.svg +++ b/frontend/uikit/src/token-icons/neri.svg @@ -1,74 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +