Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/landing #3

Merged
merged 6 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
278 changes: 213 additions & 65 deletions web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import React, { useMemo, useState } from "react";
import Image from "next/image";
import { ChevronDownIcon, GithubIcon, LockIcon, ShieldIcon } from "lucide-react";
import toast from "react-hot-toast";
import { useBoolean } from "usehooks-ts";
import { parseUnits } from "viem";
import { useAccount, useWriteContract } from "wagmi";
import { ArrowsUpDownIcon, LockClosedIcon, WalletIcon } from "@heroicons/react/24/outline";
import { useWriteContract } from "wagmi";
import { ArrowsUpDownIcon, WalletIcon } from "@heroicons/react/24/outline";
import { Footer } from "~~/components/Footer";
import HiddenContent from "~~/components/HiddenContent";
import { Button } from "~~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~~/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~~/components/ui/select";
Expand Down Expand Up @@ -37,7 +40,6 @@ export default function UniswapClone() {
const wbtc = useWbtcToken();
const { writeContractAsync } = useWriteContract();
const { daiPoolLiquidity, wbtcPoolLiquidity } = useCpamm();
const { isConnected } = useAccount();
const { value: isSwapping, setValue: setIsSwapping } = useBoolean(false);
const [swapState, setSwapState] = useState<SwapState>({
from: {
Expand Down Expand Up @@ -242,65 +244,154 @@ export default function UniswapClone() {
};

return (
<div className="flex-1 flex items-center justify-center relative">
<form
className={cn(!isConnected && "opacity-60")}
onSubmit={e => {
e.preventDefault();
handleSwap();
}}
>
<h2 className="card-title mb-4 font-medium text-2xl">Swap</h2>

<SwapCard
heading="Sell"
balance={fromBalance ?? 0n}
displayAmount={swapState.from.displayAmount}
token={swapState.from.token}
selectedToken={swapState.from.token.symbol}
onAmountChange={e => handleAmountChange(e, "from")}
onTokenChange={e => handleTokenChange(e, "from")}
disabled={isSwapping}
/>

<div className="flex justify-center -my-3">
<Button variant="secondary" className="h-10 w-10" onClick={handleSwitch} disabled={isSwapping} type="button">
<ArrowsUpDownIcon className="h-5 w-5" />
</Button>
</div>
<div className="flex-1 flex relative pt-14 flex-col">
<div className="flex flex-col justify-between items-center h-[calc(100vh-74px-56px)]">
<form
onSubmit={e => {
e.preventDefault();
handleSwap();
}}
>
<h1 className="text-6xl text-center mb-8 leading-[4rem]">
Private swaps,
<br />
anytime.
</h1>

<HiddenContent>
<>
<SwapCard
heading="Sell"
balance={fromBalance ?? 0n}
displayAmount={swapState.from.displayAmount}
token={swapState.from.token}
selectedToken={swapState.from.token.symbol}
onAmountChange={e => handleAmountChange(e, "from")}
onTokenChange={e => handleTokenChange(e, "from")}
disabled={isSwapping}
daiPoolLiquidity={daiPoolLiquidity}
wbtcPoolLiquidity={wbtcPoolLiquidity}
/>

<div className="flex justify-center -my-3">
<Button
variant="secondary"
className="h-10 w-10"
onClick={handleSwitch}
disabled={isSwapping}
type="button"
>
<ArrowsUpDownIcon className="h-5 w-5" />
</Button>
</div>

<SwapCard
heading="Buy"
balance={toBalance ?? 0n}
displayAmount={swapState.to.displayAmount}
token={swapState.to.token}
selectedToken={swapState.to.token.symbol}
onAmountChange={e => handleAmountChange(e, "to")}
onTokenChange={e => handleTokenChange(e, "to")}
disabled={isSwapping}
/>

<div className="mt-4 text-sm text-muted-foreground">
Swap Price: 1 {swapState.from.token.symbol} = {formatTokenWithDecimals(swapPrice, 18)}{" "}
{swapState.to.token.symbol}
<SwapCard
heading="Buy"
balance={toBalance ?? 0n}
displayAmount={swapState.to.displayAmount}
token={swapState.to.token}
selectedToken={swapState.to.token.symbol}
onAmountChange={e => handleAmountChange(e, "to")}
onTokenChange={e => handleTokenChange(e, "to")}
disabled={isSwapping}
daiPoolLiquidity={daiPoolLiquidity}
wbtcPoolLiquidity={wbtcPoolLiquidity}
/>

<div className="mt-4 text-sm text-muted-foreground">
1 {swapState.from.token.symbol} = {formatTokenWithDecimals(swapPrice, 18)} {swapState.to.token.symbol}
</div>

<Button
className="w-full h-11 mt-4 text-lg"
type="submit"
disabled={!swapState.from.amount || !swapState.to.amount}
loading={isSwapping}
>
Swap
</Button>
</>
</HiddenContent>
</form>

<div className="flex flex-col items-center justify-center py-6 gap-1 text-muted-foreground">
<div>Scroll to learn more</div>
<ChevronDownIcon className="h-5 w-5" />
</div>
</div>

<Button
className="w-full h-11 mt-4 text-lg"
type="submit"
disabled={!swapState.from.amount || !swapState.to.amount || isSwapping}
>
Swap
</Button>
</form>
{!isConnected && (
<div className="absolute inset-0 flex items-center justify-center backdrop-blur-sm rounded-lg">
<div className="rounded-lg bg-zinc-900 border px-6 py-4 text-lg font-medium text-neutral-50 shadow-lg flex items-center gap-x-2">
<LockClosedIcon className="w-5 h-5" />
Connect your wallet to start
<div className="flex flex-col gap-y-4 min-h-screen pt-10 max-w-7xl mx-auto">
<section className="p-8 flex flex-col gap-y-10">
<h2 className="text-4xl md:text-5xl font-medium tracking-tight">What makes Double Zero swap unique?</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FeatureCard
title="Privacy"
description="Swap information can only be accessed through authentication."
icon={<ShieldIcon className="h-7 w-7" />}
className="bg-blue-500/10 text-blue-400 min-h-[250px]"
/>
<FeatureCard
title="Permissioned swaps"
description="All requests are verified by the chain permisison system."
icon={<LockIcon className="h-7 w-7" />}
className="bg-purple-500/10 text-purple-500"
/>
</div>
</div>
)}
</section>
<section className="p-8 flex flex-col gap-y-10">
<h2 className="text-4xl md:text-5xl font-medium tracking-tight">How it works?</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<FlowStep
number="1"
title="Initiate Swap"
description="Authenticated user initiates a swap between ERC20 permissioned tokens."
/>
<FlowStep
number="2"
title="Verify Permission"
description="Proxy verifies authentication and user permission, handling access control features."
/>
<FlowStep number="3" title="Confirm Swap" description="Swap is confirmed in Double Zero Validium chain." />
</div>
</section>
<section className="p-8 flex flex-col gap-y-10">
<h2 className="text-4xl md:text-5xl font-medium tracking-tight">Learn more</h2>
<ul className="list-disc pl-3 space-y-4 text-lg">
<li className="flex items-center gap-x-3">
<div className="bg-pink-500/10 rounded-full p-2">
<GithubIcon className="h-5 w-5 text-pink-400" />
</div>
<a
className="font-semibold underline underline-offset-4"
href="https://github.com/Moonsong-Labs/double-zero-dapp"
>
Double Zero Dapp
</a>{" "}
<span className="text-muted-foreground">
- Interact with the Double Zero ecosystem through our decentralized application.
</span>
</li>
<li className="flex flex-col gap-y-2">
<div className="flex items-center gap-x-3">
<div className="bg-pink-500/10 rounded-full p-2">
<GithubIcon className="h-5 w-5 text-pink-400" />
</div>
<a
className="font-semibold underline underline-offset-4"
href="https://github.com/Moonsong-Labs/double-zero"
>
Double Zero Block Explorer
</a>{" "}
<span className="text-muted-foreground">
- Explore the Double Zero blockchain with our intuitive block explorer.
</span>
</div>
</li>
</ul>
</section>

<Footer className="mt-auto" />
</div>
</div>
);
}
Expand All @@ -314,6 +405,8 @@ function SwapCard({
displayAmount,
token,
selectedToken,
daiPoolLiquidity,
wbtcPoolLiquidity,
}: {
heading: string;
balance: bigint;
Expand All @@ -323,6 +416,8 @@ function SwapCard({
selectedToken: Token["symbol"];
onTokenChange: (value: string) => void;
disabled: boolean;
daiPoolLiquidity: bigint | undefined;
wbtcPoolLiquidity: bigint | undefined;
}) {
return (
<Card>
Expand All @@ -331,13 +426,18 @@ function SwapCard({
</CardHeader>
<CardContent className="flex flex-col gap-y-2 p-4 pt-0">
<div className="flex items-center justify-between">
<input
placeholder="0"
className="bg-transparent appearance-none focus:outline-none text-3xl"
value={displayAmount}
onChange={onAmountChange}
disabled={disabled}
/>
<div className="flex flex-col flex-1">
<input
placeholder={!daiPoolLiquidity || !wbtcPoolLiquidity ? "-" : "0"}
className={cn("bg-transparent appearance-none focus:outline-none text-3xl", disabled && "opacity-50")}
value={displayAmount}
onChange={onAmountChange}
disabled={disabled || !daiPoolLiquidity || !wbtcPoolLiquidity}
/>
{!daiPoolLiquidity || !wbtcPoolLiquidity ? (
<span className="text-sm text-red-600">No liquidity available in pool</span>
) : null}
</div>
<div className="flex flex-col gap-y-2">
<Select value={selectedToken} disabled={disabled} onValueChange={onTokenChange}>
<SelectTrigger className="bg-secondary text-secondary-foreground shadow hover:bg-secondary/80 text-base h-fit">
Expand Down Expand Up @@ -368,3 +468,51 @@ function SwapCard({
</Card>
);
}

function FeatureCard({
title,
description,
icon,
className,
}: {
title: string;
description: string;
icon: React.ReactNode;
className?: string;
}) {
return (
<Card className={cn("border-none rounded-3xl justify-between flex flex-col", className)}>
<CardHeader>
<div className="flex items-center gap-x-2 bg-background w-fit py-3 px-4 rounded-3xl">
{icon}
<span className="text-2xl">{title}</span>
</div>
</CardHeader>
<CardContent className="text-xl pb-8 w-2/3">{description}</CardContent>
</Card>
);
}

function FlowStep({
number,
title,
description,
className,
}: {
number: string;
title: string;
description: string;
className?: string;
}) {
return (
<div className={cn("flex-1 p-6 rounded-3xl relative bg-neutral-200/10 text-neutral-300", className)}>
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-8 bg-background rounded-full flex items-center justify-center text-xl font-bold">
{number}
</div>
<h3 className="text-xl font-semibold">{title}</h3>
</div>
<p className="text-lg opacity-90">{description}</p>
</div>
);
}
Loading
Loading