Skip to content

Commit b01d0e5

Browse files
authored
Import Valence Domain Modal into app (#8)
* fix button and input styles * import modal * show connected wallet, add back turbopack * fix imports, style * lint files
1 parent 9e91c05 commit b01d0e5

21 files changed

Lines changed: 9868 additions & 1492 deletions

frontend/package-lock.json

Lines changed: 9577 additions & 1322 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@
1010
"format": "biome format --write"
1111
},
1212
"dependencies": {
13+
"@tanstack/react-query": "^5.90.2",
1314
"@tanstack/react-table": "^8.21.3",
15+
"@valence-protocol/domain-clients-core": "^0.5.0",
16+
"@valence-protocol/domain-clients-react": "^0.5.0",
17+
"@valence-protocol/domain-modal-react": "^0.5.0",
1418
"class-variance-authority": "^0.7.1",
1519
"clsx": "^2.1.1",
1620
"decimal.js": "^10.6.0",
1721
"next": "15.5.4",
18-
"react": "19.1.0",
19-
"react-dom": "19.1.0",
22+
"react": "18.3.1",
23+
"react-dom": "18.3.1",
2024
"tailwind-merge": "^3.3.1",
25+
"viem": "2.37.8",
2126
"wagmi": "^2.17.4"
2227
},
2328
"devDependencies": {

frontend/src/app/layout.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
33
import { Header } from "@/components";
4+
import { AppProviders, WalletStateSync } from "@/context";
45
import "./globals.css";
6+
import "@valence-protocol/domain-modal-react/styles.css";
57

68
const geistSans = Geist({
79
variable: "--font-geist-sans",
@@ -24,12 +26,18 @@ export default function RootLayout({
2426
children: React.ReactNode;
2527
}>) {
2628
return (
27-
<html lang="en">
29+
<html lang="en" className="min-h-screen px-4 md:px-6 lg:px-8">
2830
<body
29-
className={`${geistSans.variable} ${geistMono.variable} antialiased max-w-7xl mx-auto px-4 sm:px-6 lg:px-8`}
31+
className={`${geistSans.variable} ${geistMono.variable} antialiased grid-cols-1 min-h-screen justify-center `}
3032
>
31-
<Header />
32-
{children}
33+
<AppProviders>
34+
{/* this is a temporary hack to keep the wallet state synced when use leaves the page */}
35+
<WalletStateSync />
36+
<div className="max-w-7xl mx-auto">
37+
<Header />
38+
{children}
39+
</div>
40+
</AppProviders>
3341
</body>
3442
</html>
3543
);

frontend/src/app/page.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { type Address, isAddress } from "viem";
1+
import { isAddress } from "viem";
22
import { wagmiConfig } from "@/config";
33
import vaultAddresses from "../../vaults.config.json";
44
import { VaultsTable } from "@/components";
55
import { fetchVaultData, VaultMeta } from "@/server";
66

7-
87
export default async function VaultsPage() {
98
let vaultsMetadata: VaultMeta[] | undefined = undefined;
109
let fetchError: string | undefined = undefined;
@@ -28,7 +27,6 @@ export default async function VaultsPage() {
2827
fetchError = `Unable to fetch vault metadata for: ${failedRequests.join(", ")}`;
2928
console.error(fetchError);
3029
}
31-
3230
} catch (error) {
3331
console.error("Error fetching vaults metadata", error);
3432
fetchError = "Error fetching vaults. Please try again.";
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { fetchVaultData, VaultMeta } from "@/server";
22
import { wagmiConfig } from "@/config";
3-
import { isAddress, type Address } from "viem";
43
import { VaultPage } from "@/components";
4+
import { isAddress } from "viem";
55
import { Suspense } from "react";
66

77
export type PageProps = {
@@ -11,27 +11,32 @@ export type PageProps = {
1111
};
1212

1313
export default async function Page({ params }: PageProps) {
14-
const { vaultAddress } = await params;
14+
const { vaultAddress } = await params;
1515

16-
let vaultMeta: VaultMeta | undefined = undefined;
17-
let fetchError: string | undefined = undefined;
18-
try {
19-
if (!isAddress(vaultAddress)) {
20-
throw new Error(`Invalid address: ${vaultAddress}`);
21-
}
22-
vaultMeta = await fetchVaultData(wagmiConfig, vaultAddress);
23-
} catch (error) {
24-
fetchError = `Unable to fetch vault metadata for: ${vaultAddress}`;
25-
console.error(fetchError);
16+
let vaultMeta: VaultMeta | undefined = undefined;
17+
let fetchError: string | undefined = undefined;
18+
try {
19+
if (!isAddress(vaultAddress)) {
20+
throw new Error(`Invalid address: ${vaultAddress}`);
2621
}
22+
vaultMeta = await fetchVaultData(wagmiConfig, vaultAddress);
23+
} catch (error) {
24+
fetchError = `Unable to fetch vault metadata for: ${vaultAddress}`;
25+
console.error(fetchError);
26+
}
2727

2828
return (
2929
<main>
30-
<h1 className="text-2xl font-bold py-4">{vaultMeta?.vault.name ?? 'Vault'} <span className="font-mono font-light text-sm break-all">{vaultAddress}</span></h1>
30+
<h1 className="text-2xl font-bold py-4">
31+
{vaultMeta?.vault.name ?? "Vault"}{" "}
32+
<span className="font-mono font-light text-sm break-all">
33+
{vaultAddress}
34+
</span>
35+
</h1>
3136
{fetchError && <p className="text-red-500">{fetchError}</p>}
3237
<Suspense fallback={<p>Loading...</p>}>
33-
{<VaultPage vaultMeta={vaultMeta} vaultAddress={vaultAddress} />}
38+
{<VaultPage vaultMeta={vaultMeta} vaultAddress={vaultAddress} />}
3439
</Suspense>
3540
</main>
3641
);
37-
}
42+
}

frontend/src/components/Header.tsx

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
"use client";
22
import React from "react";
33
import Link from "next/link";
4+
import { Button } from "@/components";
5+
import {
6+
shortenAddress,
7+
useDomainModal,
8+
} from "@valence-protocol/domain-modal-react";
9+
import { useAccount } from "wagmi";
410

511
interface HeaderProps {}
612

713
export const Header: React.FC<HeaderProps> = ({}) => {
8-
const onMainButtonClick = () => {
9-
alert("Not implemented");
10-
};
14+
const { showModal } = useDomainModal();
15+
const { address, isConnected } = useAccount();
16+
1117
return (
12-
<header>
13-
<div className="flex flex-wrap gap-4 justify-between items-center py-4 mb-4">
14-
{/* Logo and App Name */}
15-
<Link href="/" className="flex items-center space-x-3">
16-
<div className="flex-shrink-0">
17-
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-500 rounded-md flex items-center justify-center shadow-lg">
18-
<span className="text-white font-bold text-sm">V</span>
19-
</div>
20-
</div>
21-
<div className="flex flex-col">
22-
<h1 className="text-xl font-bold text-white ">Valence ZK Vaults</h1>
18+
<header className="w-full flex flex-wrap gap-4 justify-between items-center py-4 mb-4">
19+
{/* Logo and App Name */}
20+
<Link href="/" className="flex items-center space-x-3">
21+
<div className="flex-shrink-0">
22+
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-500 rounded-md flex items-center justify-center shadow-lg">
23+
<span className="text-white font-bold text-sm">V</span>
2324
</div>
24-
</Link>
25+
</div>
26+
<div className="flex flex-col">
27+
<h1 className="text-xl font-bold text-white ">Valence ZK Vaults</h1>
28+
</div>
29+
</Link>
2530

26-
{/* Main Button */}
27-
<div className="flex items-center">
28-
<button
29-
onClick={onMainButtonClick}
30-
className="inline-flex items-center px-4 py-2 text-sm font-semibold rounded-md bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 transition-all duration-200 shadow-sm hover:shadow-md"
31-
>
31+
{/* Main Button */}
32+
<div className="flex items-center">
33+
{isConnected && address ? (
34+
<Button variant="secondary" onClick={() => showModal()}>
35+
{shortenAddress(address)}
36+
</Button>
37+
) : (
38+
<Button variant="primary" onClick={() => showModal()}>
3239
Wallet
33-
</button>
34-
</div>
40+
</Button>
41+
)}
3542
</div>
3643
</header>
3744
);

frontend/src/components/VaultPage.tsx

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,55 @@ export type VaultPageProps = {
1414
export const VaultPage = ({ vaultMeta, vaultAddress }: VaultPageProps) => {
1515
const [depositAmount, setDepositAmount] = useState<string>("");
1616
const [withdrawAmount, setWithdrawAmount] = useState<string>("");
17-
if (!vaultMeta) {
18-
return <div>Unable to fetch vault data for vault at <span className="font-mono font-light">{vaultAddress}</span></div>;
19-
}
17+
if (!vaultMeta) {
18+
return (
19+
<div>
20+
Unable to fetch vault data for vault at{" "}
21+
<span className="font-mono font-light">{vaultAddress}</span>
22+
</div>
23+
);
24+
}
2025
return (
2126
<div>
22-
<section className="flex flex-col items-center justify-center py-4 border border-gray-200 pb-4 ">
23-
<h2 className="text-2xl font-semibold font-mono ">Total Assets</h2>
24-
<p className="font-mono font-light text-4xl pt-2">{formatToTwoDecimals(formatUnits(vaultMeta.vault.totalAssets, vaultMeta.vault.decimals))} {vaultMeta.asset.symbol}</p>
27+
<section className="flex flex-col items-center justify-center p-8 border border-gray-200 ">
28+
<h2 className="text-2xl font-semibold ">Total Assets</h2>
29+
<p className="font-mono font-light text-4xl pt-2">
30+
{formatToTwoDecimals(
31+
formatUnits(vaultMeta.vault.totalAssets, vaultMeta.vault.decimals),
32+
)}{" "}
33+
{vaultMeta.asset.symbol}
34+
</p>
2535
</section>
26-
<section className="flex flex-col md:grid md:grid-cols-2 gap-4 pt-4">
27-
<div className="border border-gray-100 py-4 flex flex-col items-center justify-center">
28-
<h2 className="text-lg font-semibold pb-4 ">Deposit</h2>
29-
<div className="flex flex-row gap-2 items-center w-full px-4">
36+
<section className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-4">
37+
<div className="border border-gray-100 p-8 flex flex-col items-start">
38+
<h2 className="text-lg font-semibold pb-4 ">Deposit</h2>
39+
<div className="flex flex-row gap-4 items-center w-full ">
3040
<TextInput
3141
value={depositAmount}
3242
onChange={(e) => setDepositAmount(e.target.value)}
33-
type="number" className="grow" />
34-
<Button
35-
isDisabled
36-
variant="secondary"
37-
onClick={() => {}}>Deposit</Button>
38-
</div>
43+
type="number"
44+
className="grow"
45+
/>
46+
<Button isDisabled variant="secondary" onClick={() => {}}>
47+
Deposit
48+
</Button>
49+
</div>
3950
</div>
40-
<div className="border border-gray-100 py-4 flex flex-col items-center justify-center">
41-
<h2 className="text-lg font-semibold pb-4 ">Withdraw</h2>
42-
<div className="flex flex-row gap-2 items-center w-full px-4">
51+
<div className="border border-gray-100 p-8 flex flex-col items-start">
52+
<h2 className="text-lg font-semibold pb-4 ">Withdraw</h2>
53+
<div className="flex flex-row gap-4 items-center w-full">
4354
<TextInput
4455
value={withdrawAmount}
4556
onChange={(e) => setWithdrawAmount(e.target.value)}
46-
type="number" className="grow" />
47-
<Button
48-
isDisabled
49-
variant="secondary"
50-
onClick={() => {}}>Withdraw</Button>
51-
</div>
57+
type="number"
58+
className="grow"
59+
/>
60+
<Button variant="secondary" onClick={() => {}}>
61+
Withdraw
62+
</Button>
63+
</div>
5264
</div>
53-
5465
</section>
5566
</div>
5667
);
57-
};
68+
};

frontend/src/components/VaultsTable.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ export const VaultsTable = ({ vaultsMetadata }: VaultsTableProps) => {
3535
name: vault.vault.name,
3636
symbol: vault.asset.symbol,
3737
vaultAddress: vault.vault.address,
38-
totalAssets: formatToTwoDecimals(formatUnits(vault.vault.totalAssets, vault.vault.decimals)),
38+
totalAssets: formatToTwoDecimals(
39+
formatUnits(vault.vault.totalAssets, vault.vault.decimals),
40+
),
3941
})) ?? [];
4042

4143
const table = useReactTable({
@@ -66,17 +68,13 @@ export const VaultsTable = ({ vaultsMetadata }: VaultsTableProps) => {
6668
</TableHeader>
6769
<TableBody>
6870
{table.getRowModel().rows.map((row) => (
69-
7071
<TableRow
71-
onClick={() => {
72-
router.push(`/vault/${row.original.vaultAddress}`);
73-
}}
74-
key={row.id}
72+
onClick={() => {
73+
router.push(`/vault/${row.original.vaultAddress}`);
74+
}}
75+
key={row.id}
7576
className="hover:scale-[1.01] transition-all duration-200 ease-in-out group cursor-pointer"
76-
7777
>
78-
79-
8078
{row.getVisibleCells().map((cell) => (
8179
<TableCell
8280
key={cell.id}
@@ -85,9 +83,7 @@ export const VaultsTable = ({ vaultsMetadata }: VaultsTableProps) => {
8583
{flexRender(cell.column.columnDef.cell, cell.getContext())}
8684
</TableCell>
8785
))}
88-
8986
</TableRow>
90-
9187
))}
9288
</TableBody>
9389
</Table>
Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,42 @@
11
import { cn } from "@/lib/utils";
22
import { cva, VariantProps } from "class-variance-authority";
33

4-
const buttonVariants = cva("inline-flex items-center px-4 py-2 text-sm font-semibold rounded-md bg-gradient-to-r focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 transition-all duration-200 shadow-sm hover:shadow-md", {
4+
const buttonVariants = cva(
5+
"inline-flex items-center px-4 py-2 text-sm font-semibold rounded-md bg-gradient-to-r hover:ring-2 hover:ring-offset-2 hover:ring-offset-gray-900 transition-all duration-200 shadow-sm hover:shadow-md",
6+
{
57
variants: {
6-
variant: {
7-
primary: "from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600",
8-
secondary: "from-gray-500 to-gray-600 hover:from-gray-600 hover:to-gray-700",
9-
},
10-
isDisabled: {
11-
true: "opacity-50 cursor-not-allowed",
12-
},
8+
variant: {
9+
primary:
10+
"from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 ",
11+
secondary:
12+
"from-gray-500 to-gray-600 hover:from-gray-600 hover:to-gray-700 ",
13+
},
14+
isDisabled: {
15+
true: "opacity-50 cursor-not-allowed hover:ring-0 hover:ring-offset-0",
16+
},
1317
},
1418
defaultVariants: {
15-
variant: "primary",
19+
variant: "primary",
1620
},
17-
});
21+
},
22+
);
1823

1924
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
2025
VariantProps<typeof buttonVariants>;
2126

22-
export const Button = ({ children, onClick, variant, isDisabled }: ButtonProps) => {
23-
return (
24-
<button
25-
onClick={onClick}
26-
disabled={isDisabled ?? false}
27-
className={cn(buttonVariants({ variant, isDisabled }))}
28-
>
29-
{children}
30-
</button>
31-
);
32-
};
27+
export const Button = ({
28+
children,
29+
onClick,
30+
variant,
31+
isDisabled,
32+
}: ButtonProps) => {
33+
return (
34+
<button
35+
onClick={onClick}
36+
disabled={isDisabled ?? false}
37+
className={cn(buttonVariants({ variant, isDisabled }))}
38+
>
39+
{children}
40+
</button>
41+
);
42+
};

0 commit comments

Comments
 (0)