Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8f32a1a
Initial redesign
0x0-mico Mar 2, 2026
9ce7761
Detail page skeleton
0x0-mico Mar 2, 2026
62ca64b
para wallet update, autocomplete on swap, trading view on detail view
0x0-mico Mar 2, 2026
0cefc53
xyk pool creator
0x0-mico Mar 2, 2026
01b8873
Fixes para wallet
0x0-mico Mar 3, 2026
89c109b
Launchpad, fixes for hfamms
0x0-mico Mar 6, 2026
af8a3e7
Xyk pool wizard 2, refactor from slop
0x0-mico Mar 7, 2026
b38d828
Restyled wizard, added compiling Xyk pool and deposit note
0x0-mico Mar 8, 2026
0992699
Spawning pools in wizard
0x0-mico Mar 8, 2026
c79886d
Pool deployment & deposit working
0x0-mico Mar 9, 2026
03c0c3d
Pool details link on wizard
0x0-mico Mar 9, 2026
8fa54b4
Network acc like in tut
0x0-mico Mar 9, 2026
4506c94
init script
0x0-mico Mar 9, 2026
42f5524
Spawning xyk pools successful
0x0-mico Mar 11, 2026
d6ab887
Updated with latest masm
0x0-mico Mar 11, 2026
b8d12a9
Update swap UI design (#60)
dotsolo Mar 11, 2026
38ecbb8
Split pool details, new withdraw & swap notes for xyk
0x0-mico Mar 11, 2026
4118ec7
Debug withdraw
0x0-mico Mar 12, 2026
c580540
Local withdraw works
0x0-mico Mar 12, 2026
b9fbeea
network acc
0x0-mico Mar 12, 2026
06a1388
wip swapping xyk
0x0-mico Mar 12, 2026
31831f5
Swapping on xyk detail
0x0-mico Mar 12, 2026
bdc8bfd
Hidden tables for now
0x0-mico Mar 12, 2026
c32850a
Swapping XYK works, fix for reserves
0x0-mico Mar 14, 2026
99a47b1
Disclaimer
0x0-mico Mar 14, 2026
5006973
LiquidityPools -> Explore
0x0-mico Mar 15, 2026
2f075e8
mend
0x0-mico Mar 15, 2026
344cde8
Nicer waiting for notes
0x0-mico Mar 15, 2026
a912f12
Nicer XykPoolModal
0x0-mico Mar 15, 2026
9e23bd8
Respecting token0 and token1 by hex
0x0-mico Mar 15, 2026
d9e6821
useXykPools hook
0x0-mico Mar 17, 2026
d9c18ff
Update to midenclient 0.13.3
0x0-mico Mar 17, 2026
d2d90a9
XykPoolsTable
0x0-mico Mar 17, 2026
4736486
Registry masm
0x0-mico Mar 17, 2026
5ce8cfa
Registry working
0x0-mico Mar 19, 2026
1193d6c
Modal updates (#61)
dotsolo Mar 19, 2026
1035833
Fixes wrong tag
0x0-mico Mar 19, 2026
6e4c489
RpcClient in own web worker
0x0-mico Mar 19, 2026
d861fea
Skeletons on loading on xyk table
0x0-mico Mar 19, 2026
7e25345
pool fetching debug
0x0-mico Mar 19, 2026
0fdbf76
Waiting for tx
0x0-mico Mar 19, 2026
1100ac6
XYK pools on swap too
0x0-mico Mar 19, 2026
e6d9b85
Status banner & preventing creating pools that already exist
0x0-mico Mar 19, 2026
5983b82
Nicer launchpad
0x0-mico Mar 19, 2026
0e5ece8
Nicer launchpad 2, Manrope font for body
0x0-mico Mar 19, 2026
d00ec93
Fixed eslint issues
0x0-mico Mar 19, 2026
664f81b
Made status banner a bit smaller
0x0-mico Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@

<!-- Preload fonts and images -->
<link rel="preload" href="/fonts/CalSans/CalSans-Regular.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap"
rel="stylesheet"
>
</head>

<body>
Expand Down
407 changes: 258 additions & 149 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vite-project",
"name": "zoroswap-frontend",
"private": true,
"version": "0.3.0",
"version": "0.5.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -11,19 +11,20 @@
"postinstall": "setup-para"
},
"dependencies": {
"@demox-labs/miden-wallet-adapter": "0.10.0",
"@getpara/react-sdk-lite": "^2.2.0",
"@miden-sdk/miden-para": "^0.13.0",
"@miden-sdk/miden-sdk": "^0.13.0",
"@miden-sdk/react": "^0.13.2",
"@miden-sdk/use-miden-para-react": "^0.13.0",
"@demox-labs/miden-wallet-adapter": "^0.10.0",
"@getpara/react-sdk-lite": "^2.15.0",
"@miden-sdk/miden-para": "^0.13.3",
"@miden-sdk/miden-sdk": "^0.13.3",
"@miden-sdk/react": "^0.13.3",
"@miden-sdk/use-miden-para-react": "^0.13.3",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-slot": "^1.2.4",
"@tanstack/react-query": "^5.90.12",
"@wagmi/core": "^3.0.0",
"async-mutex": "^0.5.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lightweight-charts": "^5.1.0",
"lucide-react": "^0.556.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
Expand All @@ -35,7 +36,7 @@
"wagmi": "^3.1.0"
},
"overrides": {
"@getpara/web-sdk": "2.8.0"
"@getpara/web-sdk": "2.15.0"
},
"devDependencies": {
"@eslint/css": "^0.14.1",
Expand Down
1 change: 1 addition & 0 deletions public/banner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
High frequency pools are currently under maintenance. Swaps / deposits & withdrawals may not work.
21 changes: 16 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ import { ThemeProvider } from './providers/ThemeProvider';
import '@demox-labs/miden-wallet-adapter-reactui/styles.css';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Bounce, ToastContainer } from 'react-toastify';
import LiquidityPools from './pages/LiquidityPools';
import Launchpad from './pages/Launchpad';
import Explore from './pages/Explore';
import HfPoolDetail from './pages/HfPoolDetail';
import XykPoolDetail from './pages/XykPoolDetail';
import NewXykPool from './pages/NewXykPool';
import { DisclaimerGate } from './components/Disclaimer';
import ModalProvider from './providers/ModalProvider';
import { ZoroProvider } from './providers/ZoroProvider';
import { ParaProviderWrapper } from './providers/ParaProviderWrapper';
import { UnifiedWalletProvider } from './providers/UnifiedWalletProvider';
import { ZoroProvider } from './providers/ZoroProvider';

const queryClient = new QueryClient();

Expand All @@ -27,7 +32,11 @@ function AppRouter() {
<Routes>
<Route path='/' element={<SwapPage />} />
<Route path='/faucet' element={<FaucetPage />} />
<Route path='/pools' element={<LiquidityPools />} />
<Route path='/launchpad' element={<Launchpad />} />
<Route path='/explore' element={<Explore />} />
<Route path='/pools/hf/:poolId' element={<HfPoolDetail />} />
<Route path='/pools/xyk/:poolId' element={<XykPoolDetail />} />
<Route path='/new-xyk-pool' element={<NewXykPool />} />
<Route path='*' element={<NotFound />} />
</Routes>
</Router>
Expand All @@ -53,8 +62,9 @@ function App() {
<ThemeProvider storageKey='vite-ui-theme'>
<OracleProvider>
<ModalProvider>
<AppRouter />
<ToastContainer
<DisclaimerGate>
<AppRouter />
<ToastContainer
position='top-center'
autoClose={5000}
hideProgressBar={false}
Expand All @@ -67,6 +77,7 @@ function App() {
theme='dark'
transition={Bounce}
/>
</DisclaimerGate>
</ModalProvider>
</OracleProvider>
</ThemeProvider>
Expand Down
28 changes: 28 additions & 0 deletions src/components/AllDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { ChevronDown } from 'lucide-react';
import { Button } from './ui/button';

export function AllDropdown() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant='outline'
size='sm'
className='rounded-lg bg-muted/50 border-muted-foreground/20 gap-1'
>
All
<ChevronDown className='h-4 w-4' />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='start' className='rounded-lg'>
<DropdownMenuItem>All</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
37 changes: 29 additions & 8 deletions src/components/AssetIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,40 @@ interface AssetIconProps {
size?: 'small' | 'normal' | number;
}

/** LP tokens (zBTC, zUSDC) use the same icon as the underlying token (BTC, USDC). */
const iconSymbol = (s: string) => (s.startsWith('z') ? s.slice(1) : s);

/** Symbols that have a dedicated icon (must match .icon-* classes in CSS). */
const SYMBOLS_WITH_ICONS = new Set(['BTC', 'USDC', 'ETH', 'ANY']);

const AssetIcon = ({ symbol, size = 'normal' }: AssetIconProps) => {
const iconSize = size === 'normal'
? 32
: size === 'small'
? 24
: typeof size === 'number'
? size
: 32;
const iconSize =
size === 'normal'
? 32
: size === 'small'
? 24
: typeof size === 'number'
? size
: 32;
const symbolForIcon = iconSymbol(symbol);
const hasIcon = SYMBOLS_WITH_ICONS.has(symbolForIcon.toUpperCase());

if (hasIcon) {
return (
<span
className={`icon-any icon-${symbolForIcon} inline-block flex-shrink-0`}
style={{ width: iconSize, height: iconSize }}
/>
);
}

const letter = (symbolForIcon || '?')[0].toUpperCase();
return (
<span
className={`icon-any icon-${symbol} inline-block`}
className='inline-flex items-center justify-center rounded-full border-2 border-background bg-muted text-xs font-semibold text-foreground flex-shrink-0'
style={{ width: iconSize, height: iconSize }}
>
{letter}
</span>
);
};
Expand Down
127 changes: 127 additions & 0 deletions src/components/Disclaimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { FancyLogo } from '@/components/FancyLogo';
import { poweredByMiden } from '@/components/PoweredByMiden';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import type { ReactNode } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';

const DISCLAIMER_STORAGE_KEY = 'zoro-disclaimer-accepted';

function hasAcceptedDisclaimer(): boolean {
if (typeof window === 'undefined') return true;
return window.localStorage.getItem(DISCLAIMER_STORAGE_KEY) === 'true';
}

function acceptDisclaimer(): void {
if (typeof window === 'undefined') return;
window.localStorage.setItem(DISCLAIMER_STORAGE_KEY, 'true');
}

interface DisclaimerModalProps {
onAccept: () => void;
}

function DisclaimerModal({ onAccept }: DisclaimerModalProps) {
const [visible, setVisible] = useState(false);

useEffect(() => {
const t = requestAnimationFrame(() => {
requestAnimationFrame(() => setVisible(true));
});
return () => cancelAnimationFrame(t);
}, []);

const handleAccept = useCallback(() => {
setVisible(false);
acceptDisclaimer();
setTimeout(onAccept, 200);
}, [onAccept]);

return createPortal(
<div
className='fixed inset-0 z-[10000] flex items-center justify-center bg-black/60 backdrop-blur-sm p-6 sm:p-8'
role='dialog'
aria-modal='true'
aria-labelledby='disclaimer-title'
>
<Card
className={`relative w-full max-w-lg border-border bg-card shadow-xl transition-all duration-200 ease-out ${
visible ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'
}`}
onClick={(e) => e.stopPropagation()}
>
<CardHeader className='flex flex-col items-center text-center space-y-4 pb-2 pt-10 px-10'>
<FancyLogo size={128} />
<div className='space-y-5'>
<CardTitle
id='disclaimer-title'
className='text-2xl font-bold font-cal-sans text-foreground sm:text-3xl'
>
Open Alpha
</CardTitle>
<CardDescription className='text-xs text-muted-foreground leading-relaxed'>
ZoroSwap is under active development — features and interfaces may change
without notice, and you may run into bugs.
</CardDescription>
</div>
</CardHeader>
<CardContent className='flex flex-col items-center text-center space-y-6 px-10 pb-10 pt-2'>
<p className='text-xs text-muted-foreground leading-relaxed max-w-md'>
The app runs on the Miden testnet. All tokens and assets here are for testing
only and have no monetary value.
</p>
<Button
type='button'
size='lg'
className='w-full max-w-sm rounded-lg font-medium'
onClick={handleAccept}
>
I understand and want to continue
</Button>
<div className='flex justify-center'>
{poweredByMiden}
</div>
</CardContent>
</Card>
</div>,
document.body,
);
}

/**
* Renders the disclaimer modal when the user has not yet accepted it (once per
* device via localStorage). Mount once at app root (e.g. inside ModalProvider).
*/
export function DisclaimerGate({ children }: { children: ReactNode }) {
const [showDisclaimer, setShowDisclaimer] = useState(false);
const [mounted, setMounted] = useState(false);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true);
}, []);

useEffect(() => {
if (!mounted) return;
// eslint-disable-next-line react-hooks/set-state-in-effect
setShowDisclaimer(!hasAcceptedDisclaimer());
}, [mounted]);

const handleAccept = useCallback(() => {
setShowDisclaimer(false);
}, []);

return (
<>
{children}
{showDisclaimer && <DisclaimerModal onAccept={handleAccept} />}
</>
);
}
17 changes: 10 additions & 7 deletions src/components/ExchangeRatio.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { OracleContext } from '@/providers/OracleContext';
import type { TokenConfig } from '@/providers/ZoroProvider';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';

const ExchangeRatio = (
{ assetA, assetB }: {
{ assetA, assetB, overrideRatio }: {
assetA: TokenConfig;
assetB: TokenConfig;
overrideRatio?: string;
},
) => {
const { getWebsocketPrice } = useContext(OracleContext);
const [ratio, setRatio] = useState<number | undefined>(undefined);
const activeRatio = useRef<undefined | number>(undefined);

useEffect(() => {
if (overrideRatio != null) return;
const i = setInterval(() => {
const priceA = getWebsocketPrice(assetA.oracleId);
const priceB = getWebsocketPrice(assetB.oracleId);
Expand All @@ -25,13 +27,14 @@ const ExchangeRatio = (
}
}, 50);
return () => clearInterval(i);
}, [assetA.oracleId, assetB.oracleId, getWebsocketPrice]);
}, [assetA.oracleId, assetB.oracleId, getWebsocketPrice, overrideRatio]);

const html = useMemo(() => {
return <>{ratio?.toFixed(8)}</>;
}, [ratio]);
if (overrideRatio != null) {
return <>{overrideRatio}</>;
}

return <>{ratio?.toFixed(8)}</>;

return html;
};

export default ExchangeRatio;
24 changes: 24 additions & 0 deletions src/components/FancyLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { cn } from '@/lib/utils';

interface FancyLogoProps {
className?: string;
size?: number;
}

export function FancyLogo({ className, size = 56 }: FancyLogoProps) {
return (
<div
className={cn(
'flex items-center justify-center rounded-2xl bg-white dark:bg-black ring-1 ring-border',
className,
)}
style={{ width: size, height: size }}
>
<img
src='/zoro-logo.svg'
alt='ZoroSwap'
className='h-[70%] w-[70%] object-contain'
/>
</div>
);
}
Loading
Loading