Skip to content

Commit 1fd39a3

Browse files
authored
Vault deposit and withdraw actions (#9)
* add user display data, better error handling in file * vault actions * format files * comment * fix error * format files * split out vault page into compoennts * add use client
1 parent b01d0e5 commit 1fd39a3

20 files changed

Lines changed: 618 additions & 143 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ cache/*
66
forge-artifacts/*
77
lib/*
88
deployed-addresses.txt
9-
vaults.config.json
9+
vaults.config.json
10+
*scratch*

frontend/src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default function RootLayout({
2626
children: React.ReactNode;
2727
}>) {
2828
return (
29-
<html lang="en" className="min-h-screen px-4 md:px-6 lg:px-8">
29+
<html lang="en" className="min-h-screen px-4 sm:px-6 lg:px-8">
3030
<body
3131
className={`${geistSans.variable} ${geistMono.variable} antialiased grid-cols-1 min-h-screen justify-center `}
3232
>

frontend/src/app/page.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { fetchVaultData, VaultMeta } from "@/server";
66

77
export default async function VaultsPage() {
88
let vaultsMetadata: VaultMeta[] | undefined = undefined;
9-
let fetchError: string | undefined = undefined;
109
try {
1110
const vaultRequests = await Promise.allSettled(
1211
vaultAddresses.map(async (address: string): Promise<VaultMeta> => {
@@ -24,17 +23,22 @@ export default async function VaultsPage() {
2423
.filter((request) => request.status === "rejected")
2524
.map((request) => request.reason);
2625
if (failedRequests.length > 0) {
27-
fetchError = `Unable to fetch vault metadata for: ${failedRequests.join(", ")}`;
28-
console.error(fetchError);
26+
throw new Error(failedRequests.join(", "));
2927
}
3028
} catch (error) {
31-
console.error("Error fetching vaults metadata", error);
32-
fetchError = "Error fetching vaults. Please try again.";
29+
console.error("error fetching vaults metadata", error);
30+
return (
31+
<main>
32+
<h1 className="text-3xl font-bold py-4">Vaults</h1>
33+
<p className="text-red-500">
34+
{"Failed to fetch vaults. Please try again."}
35+
</p>
36+
</main>
37+
);
3338
}
3439
return (
3540
<main>
36-
<h1 className="text-2xl font-bold py-4">Vaults</h1>
37-
{fetchError && <p className="text-red-500">{fetchError}</p>}
41+
<h1 className="text-3xl font-bold py-4">Vaults</h1>
3842
<VaultsTable vaultsMetadata={vaultsMetadata} />
3943
</main>
4044
);

frontend/src/app/vault/[vaultAddress]/page.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,29 @@ export default async function Page({ params }: PageProps) {
2121
}
2222
vaultMeta = await fetchVaultData(wagmiConfig, vaultAddress);
2323
} catch (error) {
24+
console.error(error);
2425
fetchError = `Unable to fetch vault metadata for: ${vaultAddress}`;
25-
console.error(fetchError);
26+
return (
27+
<main>
28+
<h1 className="text-3xl font-bold py-4">
29+
{"Vault"}{" "}
30+
<span className="font-mono font-light text-base break-all">
31+
{vaultAddress}
32+
</span>
33+
</h1>
34+
<p className="text-red-500">{fetchError}</p>
35+
</main>
36+
);
2637
}
2738

2839
return (
2940
<main>
30-
<h1 className="text-2xl font-bold py-4">
41+
<h1 className="text-3xl font-bold py-4">
3142
{vaultMeta?.vault.name ?? "Vault"}{" "}
32-
<span className="font-mono font-light text-sm break-all">
43+
<span className="font-mono font-light text-base break-all">
3344
{vaultAddress}
3445
</span>
3546
</h1>
36-
{fetchError && <p className="text-red-500">{fetchError}</p>}
3747
<Suspense fallback={<p>Loading...</p>}>
3848
{<VaultPage vaultMeta={vaultMeta} vaultAddress={vaultAddress} />}
3949
</Suspense>

frontend/src/components/VaultPage.tsx

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"use client";
2+
import { Button, TextInput } from "@/components/common";
3+
import { useState, useCallback } from "react";
4+
import { parseUnits } from "viem";
5+
import { useAccount } from "wagmi";
6+
import { useUserAssetBalances, useVaultDeposit } from "@/hooks";
7+
import { QUERY_KEYS } from "@/const";
8+
import { useQueryClient } from "@tanstack/react-query";
9+
import { VaultMeta } from "@/server";
10+
11+
export const VaultDeposit = ({ vaultMeta }: { vaultMeta: VaultMeta }) => {
12+
const [depositAmount, setDepositAmount] = useState<string>("");
13+
const { address: userAddress } = useAccount();
14+
const queryClient = useQueryClient();
15+
const { data: userBalances } = useUserAssetBalances({
16+
vaultMeta,
17+
address: userAddress,
18+
});
19+
20+
const isDepositDisabled =
21+
!userAddress ||
22+
!userBalances?.userBaseAssetBalance ||
23+
userBalances.userBaseAssetBalance === BigInt(0);
24+
25+
const { mutate: deposit } = useVaultDeposit({
26+
vaultMeta,
27+
onSuccess: () => {
28+
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USER_BALANCES] });
29+
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.VAULT_DATA] });
30+
},
31+
onError: (error) => {
32+
console.error("Error depositing:", error);
33+
},
34+
});
35+
36+
const handleDeposit = useCallback(() => {
37+
if (!userAddress) {
38+
return;
39+
}
40+
const amount = parseUnits(depositAmount, vaultMeta.asset.decimals);
41+
deposit({ amount, address: userAddress });
42+
}, [userAddress, depositAmount, deposit, vaultMeta]);
43+
44+
return (
45+
<div className="border border-gray-100 p-8 flex flex-col items-start">
46+
<h2 className="text-lg font-semibold pb-4 ">Deposit</h2>
47+
<div className="flex flex-row gap-4 items-center w-full ">
48+
<TextInput
49+
value={depositAmount}
50+
onChange={(e) => setDepositAmount(e.target.value)}
51+
type="number"
52+
className="grow"
53+
/>
54+
<Button
55+
variant="secondary"
56+
onClick={() => {
57+
handleDeposit();
58+
}}
59+
isDisabled={isDepositDisabled}
60+
>
61+
Deposit
62+
</Button>
63+
</div>
64+
</div>
65+
);
66+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useUserAssetBalances, useVaultData } from "@/hooks";
2+
import { VaultMeta } from "@/server";
3+
import { formatToTwoDecimals } from "@/lib/utils";
4+
import { formatUnits } from "viem";
5+
import { useAccount } from "wagmi";
6+
7+
export const VaultMetaDisplay = ({ vaultMeta }: { vaultMeta: VaultMeta }) => {
8+
const { data: vaultData } = useVaultData({ vaultMeta });
9+
const { address: userAddress } = useAccount();
10+
11+
const vaultAssetBalance = formatToTwoDecimals(
12+
formatUnits(
13+
vaultData?.totalAssets ?? vaultMeta.vault.totalAssets,
14+
vaultMeta.vault.decimals,
15+
),
16+
);
17+
18+
const { data: userBalances } = useUserAssetBalances({
19+
vaultMeta,
20+
address: userAddress,
21+
});
22+
const userVaultBaseAssetBalance = userBalances?.userVaultBaseAssetBalance
23+
? formatToTwoDecimals(
24+
formatUnits(
25+
userBalances.userVaultBaseAssetBalance,
26+
vaultMeta.asset.decimals,
27+
),
28+
)
29+
: undefined;
30+
31+
const userBaseAssetBalance = userBalances?.userBaseAssetBalance
32+
? formatToTwoDecimals(
33+
formatUnits(
34+
userBalances.userBaseAssetBalance,
35+
vaultMeta.asset.decimals,
36+
),
37+
)
38+
: undefined;
39+
40+
return (
41+
<section className="flex flex-row gap-16 flex-wrap items-center justify-start py-8">
42+
<div className="flex flex-col items-start justify-center ">
43+
<h2 className="text-2xl font-semibold ">Vault TVL</h2>
44+
<p className="font-mono font-light text-xl pt-2">
45+
{vaultAssetBalance} {vaultMeta.asset.symbol}
46+
</p>
47+
</div>
48+
<div className="flex flex-col items-start justify-center ">
49+
<h2 className="text-2xl font-semibold ">APR</h2>
50+
<p className="font-mono font-light text-xl pt-2">
51+
{formatToTwoDecimals(vaultMeta.vault.apr * 100)}%
52+
</p>
53+
</div>
54+
{userAddress && (
55+
<>
56+
{" "}
57+
<div className="flex flex-col items-start justify-center ">
58+
<h2 className="text-2xl font-semibold ">Your Vault Position</h2>
59+
<p className="font-mono font-light text-xl pt-2">
60+
{userVaultBaseAssetBalance
61+
? `${userVaultBaseAssetBalance} ${vaultMeta.asset.symbol}`
62+
: "-"}
63+
</p>
64+
</div>
65+
<div className="flex flex-col items-start justify-center ">
66+
<h2 className="text-2xl font-semibold ">
67+
Your {vaultMeta.asset.symbol} Balance
68+
</h2>
69+
<p className="font-mono font-light text-xl pt-2">
70+
{userBaseAssetBalance ?? "-"}
71+
</p>
72+
</div>
73+
</>
74+
)}
75+
</section>
76+
);
77+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client";
2+
3+
import { VaultMeta } from "@/server";
4+
import { VaultDeposit, VaultMetaDisplay, VaultWithdraw } from "@/components/";
5+
6+
export type VaultPageProps = {
7+
vaultMeta: VaultMeta;
8+
vaultAddress: string;
9+
};
10+
11+
export const VaultPage = ({ vaultMeta }: VaultPageProps) => {
12+
return (
13+
<div>
14+
<p>
15+
<span className="font-semibold">Base: </span>
16+
{vaultMeta.asset.symbol}{" "}
17+
<span className="font-mono font-light text-base break-all">
18+
({vaultMeta.asset.address})
19+
</span>
20+
</p>
21+
<VaultMetaDisplay vaultMeta={vaultMeta} />
22+
23+
<section className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-4">
24+
<VaultDeposit vaultMeta={vaultMeta} />
25+
<VaultWithdraw vaultMeta={vaultMeta} />
26+
</section>
27+
</div>
28+
);
29+
};

0 commit comments

Comments
 (0)