From 213395c7780d626db8386cf0e1fbb3ac43c96b15 Mon Sep 17 00:00:00 2001 From: emdevelopa Date: Thu, 7 May 2026 21:39:26 +0100 Subject: [PATCH 1/5] fix: more on the fee calculation adjustment on the withdraw form --- components/Withdraw/WithdrawForm.tsx | 61 ++++++++++++++-------------- hooks/specific/useRead.ts | 48 +++++++++++++--------- hooks/usePreviewWithdraw.ts | 1 + 3 files changed, 60 insertions(+), 50 deletions(-) diff --git a/components/Withdraw/WithdrawForm.tsx b/components/Withdraw/WithdrawForm.tsx index 7113df6..d39a64a 100644 --- a/components/Withdraw/WithdrawForm.tsx +++ b/components/Withdraw/WithdrawForm.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import { formatUnits, parseUnits } from "ethers"; import { usePreviewWithdraw } from "../../hooks/usePreviewWithdraw"; import { useWithdraw } from "../../hooks/useWithdraw"; @@ -20,24 +20,28 @@ export const WithdrawForm = ({ isConnected, }: WithdrawFormProps) => { const [withdrawAmt, setWithdrawAmt] = useState(""); - const [withdrawPreview, setWithdrawPreview] = useState(null); + const [withdrawPreview, setWithdrawPreview] = useState<{ + payout: bigint; + grossAssets: bigint; + fee: bigint; + } | null>(null); const { previewByShares, isPreviewingWithdraw } = usePreviewWithdraw(); const { submitWithdraw, isWithdrawing } = useWithdraw(); useEffect(() => { if (!withdrawAmt || !isConnected) { - // eslint-disable-next-line react-hooks/set-state-in-effect - if (withdrawPreview !== null) setWithdrawPreview(null); + // Reset preview when input cleared or wallet disconnected + setWithdrawPreview(null); return; } try { const shares = parseUnits(withdrawAmt, USDC_DECIMALS); previewByShares(shares).then(setWithdrawPreview); } catch (e) { - // Handle invalid input gracefully + // ignore invalid input } - }, [withdrawAmt, isConnected, previewByShares, withdrawPreview]); + }, [withdrawAmt, isConnected, previewByShares]); const handleWithdraw = async () => { if (!withdrawAmt) return; @@ -49,6 +53,24 @@ export const WithdrawForm = ({ } }; + // Compute payout, yield, and fee using bigint arithmetic for precision + const { payout, calcYield, calcFee } = React.useMemo(() => { + if (!withdrawPreview || !userShares || !userDeposits) { + return { payout: 0, calcYield: 0, calcFee: 0 }; + } + // shares being withdrawn as bigint + const shares = withdrawAmt ? parseUnits(withdrawAmt, USDC_DECIMALS) : BigInt(0); + // proportional principal = userDeposits * shares / userShares + const principalPortion = userShares === BigInt(0) ? BigInt(0) : (userDeposits * shares) / userShares; + const yieldAmt = withdrawPreview.grossAssets > principalPortion ? withdrawPreview.grossAssets - principalPortion : BigInt(0); + return { + payout: Number(formatUnits(withdrawPreview.payout, USDC_DECIMALS)), + calcYield: Number(formatUnits(yieldAmt, USDC_DECIMALS)), + calcFee: Number(formatUnits(withdrawPreview.fee, USDC_DECIMALS)), + }; + }, [withdrawPreview, userShares, userDeposits, withdrawAmt]); + + // fmt helper retained for other uses (if needed) const fmt = (val: bigint | null) => val ? parseFloat(formatUnits(val, USDC_DECIMALS)).toLocaleString(undefined, { @@ -57,27 +79,6 @@ export const WithdrawForm = ({ }) : "0.00"; - // Calculate yield accurately based on principal proportion - const getYieldInfo = () => { - if (!withdrawPreview || !withdrawAmt || !userShares || !userDeposits) return { yield: 0, fee: 0 }; - - const sharesToWithdraw = parseUnits(withdrawAmt, USDC_DECIMALS); - const grossAssets = parseFloat(formatUnits(withdrawPreview, USDC_DECIMALS)); - - // Proportional principal calculation - // principalForShares = (sharesToWithdraw / totalShares) * totalDeposits - const totalShares = parseFloat(formatUnits(userShares, USDC_DECIMALS)); - const totalDeposits = parseFloat(formatUnits(userDeposits, USDC_DECIMALS)); - - const principalForShares = (parseFloat(withdrawAmt) / totalShares) * totalDeposits; - const yieldAmount = Math.max(0, grossAssets - principalForShares); - const feeAmount = yieldAmount * 0.05; - - return { yield: yieldAmount, fee: feeAmount }; - }; - - const { yield: calcYield, fee: calcFee } = getYieldInfo(); - const labelCls = "text-[var(--text-muted)] text-sm"; const valueCls = "font-mono font-bold text-sm text-[var(--text-primary)]"; const infoRow = "flex justify-between items-center py-2"; @@ -124,10 +125,10 @@ export const WithdrawForm = ({
You will receive - {isPreviewingWithdraw + {withdrawPreview + ? payout.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }) + : isPreviewingWithdraw ? "…" - : withdrawPreview !== null - ? fmt(withdrawPreview) : "—"}{" "} USDC diff --git a/hooks/specific/useRead.ts b/hooks/specific/useRead.ts index cfc9df7..a9b4b65 100644 --- a/hooks/specific/useRead.ts +++ b/hooks/specific/useRead.ts @@ -49,6 +49,34 @@ export const useReadVault = () => { } }, [address, decodeError, vaultContract]); + const previewWithdraw = useCallback( + async (shares: bigint): Promise<{ payout: bigint; grossAssets: bigint; fee: bigint } | null> => { + if (!vaultContract) { + toast.error("Vault contract not found"); + return null; + } + if (!address) { + toast.error("Wallet not connected"); + return null; + } + try { + setIsLoading(true); + const result = await vaultContract.previewWithdrawFor(address, shares); + return { + payout: result[0], + grossAssets: result[1], + fee: result[2], + }; + } catch (error) { + toast.error(await decodeError(error)); + return null; + } finally { + setIsLoading(false); + } + }, + [address, decodeError, vaultContract], + ); + const getUserBalance = useCallback(async (): Promise => { if (!vaultContract) { toast.error("Vault contract not found"); @@ -84,26 +112,6 @@ export const useReadVault = () => { } }, [address, decodeError, vaultContract]); - const previewWithdraw = useCallback( - async (shares: bigint): Promise => { - if (!vaultContract) { - toast.error("Vault contract not found"); - return null; - } - - try { - setIsLoading(true); - return (await vaultContract.previewWithdraw(shares)) as bigint; - } catch (error) { - toast.error(await decodeError(error)); - return null; - } finally { - setIsLoading(false); - } - }, - [decodeError, vaultContract], - ); - const previewDeposit = useCallback( async (amount: bigint): Promise => { if (!vaultContract) { diff --git a/hooks/usePreviewWithdraw.ts b/hooks/usePreviewWithdraw.ts index 3760046..c77380c 100644 --- a/hooks/usePreviewWithdraw.ts +++ b/hooks/usePreviewWithdraw.ts @@ -6,6 +6,7 @@ export const usePreviewWithdraw = () => { const previewByShares = useCallback( async (shares: bigint) => { + // previewWithdraw now returns { payout, grossAssets, fee } or null return await previewWithdraw(shares); }, [previewWithdraw], From bae5d5d5df03b80bc6904c9f8bec56b67c7b95e0 Mon Sep 17 00:00:00 2001 From: emdevelopa Date: Thu, 7 May 2026 21:57:37 +0100 Subject: [PATCH 2/5] feat: ci error and ui updates --- components/Deposit/DepositForm.tsx | 10 +++------- components/Withdraw/WithdrawForm.tsx | 27 ++++++++++----------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/components/Deposit/DepositForm.tsx b/components/Deposit/DepositForm.tsx index 935189c..ade3e65 100644 --- a/components/Deposit/DepositForm.tsx +++ b/components/Deposit/DepositForm.tsx @@ -3,9 +3,9 @@ import React, { useState, useEffect } from "react"; import { formatUnits, parseUnits } from "ethers"; import { usePreviewDeposit } from "../../hooks/usePreviewDeposit"; import { useApprove } from "../../hooks/useApprove"; -import { useDeposit } from "../../hooks/useDeposit"; import { useAllowance } from "../../hooks/useAllowance"; + const USDC_DECIMALS = 6; interface DepositFormProps { @@ -23,7 +23,7 @@ export const DepositForm = ({ }: DepositFormProps) => { const [depositAmt, setDepositAmt] = useState(""); const [depositPreview, setDepositPreview] = useState(null); - const [currentAllowance, setCurrentAllowance] = useState(null); + const { previewByAssets, isPreviewingDeposit } = usePreviewDeposit(); const { approve } = useApprove(); @@ -40,11 +40,7 @@ export const DepositForm = ({ previewByAssets(assets).then(setDepositPreview); }, [depositAmt, isConnected, previewByAssets, depositPreview]); - useEffect(() => { - if (isConnected) { - refetchAllowance().then(setCurrentAllowance); - } - }, [isConnected, refetchAllowance]); + const [flowStatus, setFlowStatus] = useState<"idle" | "approving" | "depositing">("idle"); diff --git a/components/Withdraw/WithdrawForm.tsx b/components/Withdraw/WithdrawForm.tsx index d39a64a..6673e0b 100644 --- a/components/Withdraw/WithdrawForm.tsx +++ b/components/Withdraw/WithdrawForm.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState, useEffect, useMemo } from "react"; +import React, { useState, useEffect } from "react"; import { formatUnits, parseUnits } from "ethers"; import { usePreviewWithdraw } from "../../hooks/usePreviewWithdraw"; import { useWithdraw } from "../../hooks/useWithdraw"; @@ -30,19 +30,19 @@ export const WithdrawForm = ({ const { submitWithdraw, isWithdrawing } = useWithdraw(); useEffect(() => { - if (!withdrawAmt || !isConnected) { - // Reset preview when input cleared or wallet disconnected - setWithdrawPreview(null); - return; - } - try { + if (withdrawAmt && isConnected) { const shares = parseUnits(withdrawAmt, USDC_DECIMALS); previewByShares(shares).then(setWithdrawPreview); - } catch (e) { - // ignore invalid input } }, [withdrawAmt, isConnected, previewByShares]); + // Clear preview when input cleared or wallet disconnected + useEffect(() => { + if (!withdrawAmt || !isConnected) { + setWithdrawPreview(null); + } + }, [withdrawAmt, isConnected]); + const handleWithdraw = async () => { if (!withdrawAmt) return; const shares = parseUnits(withdrawAmt, USDC_DECIMALS); @@ -70,14 +70,7 @@ export const WithdrawForm = ({ }; }, [withdrawPreview, userShares, userDeposits, withdrawAmt]); - // fmt helper retained for other uses (if needed) - const fmt = (val: bigint | null) => - val - ? parseFloat(formatUnits(val, USDC_DECIMALS)).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 6, - }) - : "0.00"; + const labelCls = "text-[var(--text-muted)] text-sm"; const valueCls = "font-mono font-bold text-sm text-[var(--text-primary)]"; From daf282c04e689ce00b651c956215074f2e288614 Mon Sep 17 00:00:00 2001 From: emdevelopa Date: Thu, 7 May 2026 22:00:11 +0100 Subject: [PATCH 3/5] feat: ci error and ui updates --- components/Deposit/DepositForm.tsx | 1 + components/Withdraw/WithdrawForm.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/components/Deposit/DepositForm.tsx b/components/Deposit/DepositForm.tsx index ade3e65..00de4d3 100644 --- a/components/Deposit/DepositForm.tsx +++ b/components/Deposit/DepositForm.tsx @@ -4,6 +4,7 @@ import { formatUnits, parseUnits } from "ethers"; import { usePreviewDeposit } from "../../hooks/usePreviewDeposit"; import { useApprove } from "../../hooks/useApprove"; import { useAllowance } from "../../hooks/useAllowance"; +import { useDeposit } from "../../hooks/useDeposit"; const USDC_DECIMALS = 6; diff --git a/components/Withdraw/WithdrawForm.tsx b/components/Withdraw/WithdrawForm.tsx index 6673e0b..f24a660 100644 --- a/components/Withdraw/WithdrawForm.tsx +++ b/components/Withdraw/WithdrawForm.tsx @@ -39,6 +39,7 @@ export const WithdrawForm = ({ // Clear preview when input cleared or wallet disconnected useEffect(() => { if (!withdrawAmt || !isConnected) { + // eslint-disable-next-line react-hooks/set-state-in-effect setWithdrawPreview(null); } }, [withdrawAmt, isConnected]); From 545acde6411b438fdb9b6f7a1c861ff076be222a Mon Sep 17 00:00:00 2001 From: emdevelopa Date: Fri, 8 May 2026 10:55:20 +0100 Subject: [PATCH 4/5] merge: resolve stash conflicts after upstream merge --- README.md | 4 ++ app/app/page.tsx | 68 ++++++++-------------- app/globals.css | 87 +++++++++++----------------- components/Dashboard/StatsGrid.tsx | 11 +++- components/Deposit/DepositForm.tsx | 29 +++++----- components/Withdraw/WithdrawForm.tsx | 60 +++++-------------- components/ui/AppNavbar.tsx | 21 +++++++ components/ui/Icons.tsx | 4 ++ hooks/useAaveApr.ts | 79 +++++++++++++++++++++++++ package.json | 2 +- tailwind.config.ts | 12 ++-- 11 files changed, 209 insertions(+), 168 deletions(-) create mode 100644 hooks/useAaveApr.ts diff --git a/README.md b/README.md index d1d69b0..aac5cc3 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,7 @@ YieldSafe is non-custodial. Your funds are always in the vault or the underlying --- Built with ❤️ by [emdevelopa](https://github.com/emdevelopa) + + +i want to ask basaed on the screenshot, the APY 0.58% percentage in the screenshot is what is being used for the calculation right for yieldsave users also + diff --git a/app/app/page.tsx b/app/app/page.tsx index 7f0a234..c08f4c1 100644 --- a/app/app/page.tsx +++ b/app/app/page.tsx @@ -14,7 +14,7 @@ import { usePreviewDeposit } from "../../hooks/usePreviewDeposit"; // Components import { AppNavbar } from "../../components/ui/AppNavbar"; import { Sidebar, Tab } from "../../components/ui/Sidebar"; -import { Logo } from "../../components/ui/Icons"; +import { Logo, DashboardIcon, EarnIcon } from "../../components/ui/Icons"; import { Menu } from "lucide-react"; import { DepositForm } from "../../components/Deposit/DepositForm"; import { WithdrawForm } from "../../components/Withdraw/WithdrawForm"; @@ -70,12 +70,10 @@ export default function AppPage() { ]); const handleRefresh = useCallback(async () => { - await new Promise((resolve) => setTimeout(resolve, 2000)); await refreshData(); }, [refreshData]); useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect refreshData(); }, [refreshData]); @@ -87,7 +85,6 @@ export default function AppPage() { }) : "0.00"; - // Dummy analytics data based on Figma const totalYield = userBalance && userDeposits ? userBalance - userDeposits : null; if (!isConnected) { @@ -98,15 +95,19 @@ export default function AppPage() { address={address} open={open} /> -
-
-
-

Ready to Earn?

-

- Connect your wallet to access your non-custodial yield dashboard. +

+ +
+
+
+ +
+

Ready to Earn?

+

+ Securely connect your wallet to access your non-custodial yield dashboard.

- {/* Recent Activity */} + {/* Protocol Status */}
-
-

Recent Activity

- -
-
-
-
-
- ↓ -
-
-

Deposit to Vault

-

Today, 14:30

-
-
-
-

+50,000 USDC

-

Confirmed

-
-
-
-
-
- ◈ -
-
-

Yield Distribution

-

Yesterday

-
+
+

+ Protocol Status +

+
+
+ Protocol Fee + 5% (Yield Only)
-
-

+$142.50

-

Added

+
+ Withdrawal Limit + Unlimited
diff --git a/app/globals.css b/app/globals.css index 58146dc..d4f2246 100644 --- a/app/globals.css +++ b/app/globals.css @@ -25,6 +25,12 @@ --radius-md: 12px; --radius-lg: 16px; --radius-xl: 24px; + + --bg-surface: rgba(255, 255, 255, 0.01); + --glass-blur: 16px; + --glass-border: 1px solid rgba(255, 255, 255, 0.05); + --shadow-premium: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + --teal: #00F5FF; } [data-theme="dark"] { @@ -60,6 +66,7 @@ --color-input: var(--input); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); + --color-teal: var(--teal); } @layer base { @@ -91,34 +98,45 @@ } @layer components { - /* Utility for premium buttons */ - .btn-primary { - @apply bg-primary text-primary-foreground font-semibold px-6 py-3 rounded-lg shadow-sm hover:opacity-90 transition-all flex items-center justify-center gap-2; + .feature-card { + background: var(--bg-surface); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + border: var(--glass-border); + border-radius: 28px; + box-shadow: var(--shadow-premium); + @apply p-8 transition-all duration-500 hover:scale-[1.02] hover:bg-white/[0.03]; } - .btn-outline { - @apply bg-transparent text-foreground font-semibold px-6 py-3 rounded-lg border-2 border-border hover:bg-muted transition-all flex items-center justify-center gap-2; + .glass-panel { + @apply bg-white/[0.01] backdrop-blur-xl border border-white/[0.05] rounded-[32px] overflow-hidden; } - .btn-glass { - @apply bg-white/10 dark:bg-white/5 backdrop-blur-md text-foreground font-semibold px-6 py-3 rounded-lg hover:bg-white/20 transition-all flex items-center justify-center gap-2; + .mesh-bg { + background-color: var(--background); + background-image: + radial-gradient(at 0% 0%, rgba(0, 122, 255, 0.15) 0px, transparent 50%), + radial-gradient(at 100% 0%, rgba(0, 224, 112, 0.1) 0px, transparent 50%), + radial-gradient(at 100% 100%, rgba(0, 122, 255, 0.1) 0px, transparent 50%), + radial-gradient(at 0% 100%, rgba(0, 224, 112, 0.15) 0px, transparent 50%); } - /* Input fields */ .premium-input { - @apply bg-input text-foreground border border-transparent rounded-lg px-4 py-3 w-full focus:outline-none focus:border-primary transition-colors; + @apply w-full bg-white/[0.02] border border-white/[0.05] rounded-2xl px-5 py-4 text-foreground focus:outline-none focus:border-primary transition-all duration-300; } - /* Cards */ - .premium-card { - @apply bg-card text-card-foreground border border-border rounded-xl shadow-sm p-6 transition-all; + .btn-primary { + @apply bg-primary text-primary-foreground font-bold py-3 px-6 rounded-2xl transition-all hover:shadow-[0_0_20px_rgba(0,122,255,0.4)] hover:scale-[1.01] active:scale-[0.99] disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2; } - .premium-glass-nav { - @apply fixed top-0 w-full z-50 bg-background/80 backdrop-blur-xl border-b border-border; + .btn-outline { + @apply bg-transparent border-2 border-white/[0.1] text-foreground font-bold py-3 px-6 rounded-2xl transition-all hover:bg-white/[0.02] hover:border-primary/50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2; + } + + .premium-card { + @apply bg-card text-card-foreground border border-border rounded-xl shadow-sm p-6 transition-all; } - /* Premium Fintech Grid Background */ .fintech-grid { position: fixed; top: 0; @@ -142,43 +160,4 @@ mask-image: radial-gradient(circle at top center, black 10%, transparent 80%); -webkit-mask-image: radial-gradient(circle at top center, black 10%, transparent 80%); } - - .fintech-grid::after { - content: ""; - position: absolute; - inset: 0; - background-image: url('data:image/svg+xml,%3Csvg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"%3E%3Cfilter id="noiseFilter"%3E%3CfeTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/%3E%3C/filter%3E%3Crect width="100%25" height="100%25" filter="url(%23noiseFilter)"/%3E%3C/svg%3E'); - opacity: 0.02; - mix-blend-mode: overlay; - } -} - -@keyframes fadeUp { - from { - opacity: 0; - transform: translateY(2rem); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.animate-fade-up { - animation: fadeUp 1s cubic-bezier(0.16, 1, 0.3, 1) forwards; -} - -@keyframes drawPath { - 0% { - stroke-dasharray: 0, 1000; - opacity: 0; - } - 50% { - stroke-dasharray: 1000, 0; - opacity: 1; - } - 100% { - stroke-dasharray: 1000, 0; - opacity: 0; - } } \ No newline at end of file diff --git a/components/Dashboard/StatsGrid.tsx b/components/Dashboard/StatsGrid.tsx index ce705da..c4a240d 100644 --- a/components/Dashboard/StatsGrid.tsx +++ b/components/Dashboard/StatsGrid.tsx @@ -1,6 +1,7 @@ "use client"; import React from "react"; import { formatUnits } from "ethers"; +import { useAaveApr } from "../../hooks/useAaveApr"; interface StatsGridProps { vaultBalance: bigint | null; @@ -11,6 +12,8 @@ export const StatsGrid = ({ vaultBalance, isLoadingVaultBalance, }: StatsGridProps) => { + const { apr, isLoading: isLoadingApr } = useAaveApr(); + const fmt = (val: bigint | null) => val ? parseFloat(formatUnits(val, 6)).toLocaleString(undefined, { @@ -19,6 +22,8 @@ export const StatsGrid = ({ }) : "0.00"; + const aprDisplay = isLoadingApr ? "…" : (apr ?? "—"); + return (
{[ @@ -27,7 +32,11 @@ export const StatsGrid = ({ label: "Total Vault TVL", cls: "text-primary", }, - { val: "4.8%", label: "Current Variable APR", cls: "text-teal" }, + { + val: aprDisplay, + label: "Current Variable APR", + cls: "text-teal", + }, ].map((stat, i) => (
(null); - const { previewByAssets, isPreviewingDeposit } = usePreviewDeposit(); const { approve } = useApprove(); const { submitDeposit } = useDeposit(); @@ -33,17 +32,17 @@ export const DepositForm = ({ useEffect(() => { if (!depositAmt || !isConnected) { - // eslint-disable-next-line react-hooks/set-state-in-effect if (depositPreview !== null) setDepositPreview(null); return; } - const assets = parseUnits(depositAmt, USDC_DECIMALS); - previewByAssets(assets).then(setDepositPreview); + try { + const assets = parseUnits(depositAmt, USDC_DECIMALS); + previewByAssets(assets).then(setDepositPreview); + } catch (_) { + // ignore + } }, [depositAmt, isConnected, previewByAssets, depositPreview]); - - - const [flowStatus, setFlowStatus] = useState<"idle" | "approving" | "depositing">("idle"); const handleDepositFlow = async () => { @@ -51,7 +50,6 @@ export const DepositForm = ({ const assets = parseUnits(depositAmt, USDC_DECIMALS); try { - // 1. Check Allowance const allowance = await refetchAllowance(); if (allowance !== null && allowance < assets) { setFlowStatus("approving"); @@ -63,7 +61,6 @@ export const DepositForm = ({ await refetchAllowance(); } - // 2. Deposit setFlowStatus("depositing"); const depSuccess = await submitDeposit(assets); if (depSuccess) { @@ -85,14 +82,14 @@ export const DepositForm = ({ }) : "0.00"; - const labelCls = "text-muted-foreground text-sm"; - const valueCls = "font-mono font-bold text-sm text-foreground"; + const labelCls = "text-muted text-sm"; + const valueCls = "font-mono font-bold text-sm text-primary"; const infoRow = "flex justify-between items-center py-2"; return (
-
+
Balance: {usdcBalance ? parseFloat(formatUnits(usdcBalance, USDC_DECIMALS)).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : "0.00"}
@@ -103,7 +100,7 @@ export const DepositForm = ({ min="0" onWheel={(e) => (e.target as HTMLInputElement).blur()} placeholder="0.00" - className="premium-input text-2xl pr-24 py-4 font-mono font-bold" + className="premium-input text-2xl pr-20 py-4 font-mono font-bold" value={depositAmt} onChange={(e) => { const val = e.target.value; @@ -119,13 +116,13 @@ export const DepositForm = ({ > MAX - + USDC
-
+
You will receive @@ -161,7 +158,7 @@ export const DepositForm = ({ {flowStatus === "approving" ? "1/2 Approving USDC..." : flowStatus === "depositing" ? "2/2 Depositing..." : "Deposit USDC"} -

+

By depositing, you agree to the protocol terms. Your USDC will be deployed to Aave V3. You can withdraw anytime.

diff --git a/components/Withdraw/WithdrawForm.tsx b/components/Withdraw/WithdrawForm.tsx index fee73a0..5b37943 100644 --- a/components/Withdraw/WithdrawForm.tsx +++ b/components/Withdraw/WithdrawForm.tsx @@ -30,25 +30,18 @@ export const WithdrawForm = ({ const { submitWithdraw, isWithdrawing } = useWithdraw(); useEffect(() => { - if (withdrawAmt && isConnected) { + if (!withdrawAmt || !isConnected) { + setWithdrawPreview(null); + return; + } + try { const shares = parseUnits(withdrawAmt, USDC_DECIMALS); previewByShares(shares).then(setWithdrawPreview); -<<<<<<< HEAD -======= - } catch { - // Handle invalid input gracefully ->>>>>>> upstream/main + } catch (err) { + console.error("Invalid withdraw amount", err); } }, [withdrawAmt, isConnected, previewByShares]); - // Clear preview when input cleared or wallet disconnected - useEffect(() => { - if (!withdrawAmt || !isConnected) { - // eslint-disable-next-line react-hooks/set-state-in-effect - setWithdrawPreview(null); - } - }, [withdrawAmt, isConnected]); - const handleWithdraw = async () => { if (!withdrawAmt) return; const shares = parseUnits(withdrawAmt, USDC_DECIMALS); @@ -64,9 +57,7 @@ export const WithdrawForm = ({ if (!withdrawPreview || !userShares || !userDeposits) { return { payout: 0, calcYield: 0, calcFee: 0 }; } - // shares being withdrawn as bigint const shares = withdrawAmt ? parseUnits(withdrawAmt, USDC_DECIMALS) : BigInt(0); - // proportional principal = userDeposits * shares / userShares const principalPortion = userShares === BigInt(0) ? BigInt(0) : (userDeposits * shares) / userShares; const yieldAmt = withdrawPreview.grossAssets > principalPortion ? withdrawPreview.grossAssets - principalPortion : BigInt(0); return { @@ -76,37 +67,14 @@ export const WithdrawForm = ({ }; }, [withdrawPreview, userShares, userDeposits, withdrawAmt]); -<<<<<<< HEAD -======= - // Calculate yield accurately based on principal proportion - const getYieldInfo = () => { - if (!withdrawPreview || !withdrawAmt || !userShares || !userDeposits) return { yield: 0, fee: 0 }; - - - const grossAssets = parseFloat(formatUnits(withdrawPreview, USDC_DECIMALS)); - - // Proportional principal calculation - // principalForShares = (sharesToWithdraw / totalShares) * totalDeposits - const totalShares = parseFloat(formatUnits(userShares, USDC_DECIMALS)); - const totalDeposits = parseFloat(formatUnits(userDeposits, USDC_DECIMALS)); - - const principalForShares = (parseFloat(withdrawAmt) / totalShares) * totalDeposits; - const yieldAmount = Math.max(0, grossAssets - principalForShares); - const feeAmount = yieldAmount * 0.05; - - return { yield: yieldAmount, fee: feeAmount }; - }; ->>>>>>> upstream/main - - - const labelCls = "text-muted-foreground text-sm"; - const valueCls = "font-mono font-bold text-sm text-foreground"; + const labelCls = "text-muted text-sm"; + const valueCls = "font-mono font-bold text-sm text-primary"; const infoRow = "flex justify-between items-center py-2"; return (
-
+
Shares: {userShares ? formatUnits(userShares, USDC_DECIMALS) : "0"}
@@ -121,7 +89,7 @@ export const WithdrawForm = ({ value={withdrawAmt} onChange={(e) => { const val = e.target.value; - if (val === "" || parseFloat(val) >= 0) setWithdrawAmt(val); + if (val === "" || parseFloat(val) >= 0) setDepositAmt(val); }} />
@@ -133,13 +101,13 @@ export const WithdrawForm = ({ > MAX - + aUSDC
-
+
You will receive @@ -185,7 +153,7 @@ export const WithdrawForm = ({ {isWithdrawing ? "Withdrawing..." : "Withdraw aUSDC"} -

+

Withdrawals are processed instantly. Your shares will be burned and the underlying USDC + yield will be sent to your wallet.

diff --git a/components/ui/AppNavbar.tsx b/components/ui/AppNavbar.tsx index dfd656e..f4c8396 100644 --- a/components/ui/AppNavbar.tsx +++ b/components/ui/AppNavbar.tsx @@ -19,6 +19,9 @@ export const AppNavbar = ({ return (