Skip to content
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
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 🛡️ YieldSafe

**The Next-Generation Yield Aggregator for USDC on Base.**

YieldSafe is a professional, non-custodial yield vault that allows users to maximize their USDC returns by routing liquidity directly into top-tier DeFi protocols like Aave V3. Built on Base Sepolia for high speed and low fees.

![YieldSafe Logo](public/logo.svg)

## 🚀 Features

- **USDC Optimized**: Specifically designed for USDC liquidity on the Base Network.
- **Auto-Compounding**: Yield is harvested and compounded every block via interest-bearing receipt tokens (aUSDC).
- **One-Click Deposits**: Combined Approve + Deposit transaction flow for a seamless user experience.
- **Glassmorphism UI**: A futuristic, high-end DeFi dashboard designed for professional traders.
- **Institutional-Grade Security**: Built on the battle-tested Aave V3 protocol architecture.

## 🛠️ Tech Stack

- **Frontend**: Next.js 16 (App Router), Tailwind CSS
- **Blockchain**: Wagmi, Viem, Reown (AppKit)
- **Styling**: Vanilla CSS Variables + Tailwind Utility Classes
- **Network**: Base Sepolia

## 📖 Documentation

Detailed documentation can be found in the `/doc` directory:

- [Architecture Overview](doc/architecture.md)
- [Setup & Installation](doc/setup.md)
- [Smart Contract Integration](doc/smart-contracts.md)
- [Component Library](doc/components.md)
- [Transaction Flows](doc/flows/deposit.md)

## 🏗️ Getting Started

1. **Clone the repository**:
```bash
git clone https://github.com/emdevelopa/ys-frontend.git
```

2. **Install dependencies**:
```bash
npm install
```

3. **Configure Environment**:
Create a `.env` file based on the provided template and add your project ID from Reown Cloud.

4. **Run Development Server**:
```bash
npm run dev
```

## 🔐 Security

YieldSafe is non-custodial. Your funds are always in the vault or the underlying liquidity pool (Aave). The smart contract interactions are verified on Base Sepolia.

---

Built with ❤️ by [emdevelopa](https://github.com/emdevelopa)
10 changes: 8 additions & 2 deletions app/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ export default function AppPage() {
previewByAssets,
]);

const handleRefresh = useCallback(async () => {
// Add a small delay to allow the chain/indexer to catch up
await new Promise((resolve) => setTimeout(resolve, 2000));
await refreshData();
}, [refreshData]);

useEffect(() => {
const init = async () => {
await refreshData();
Expand Down Expand Up @@ -184,14 +190,14 @@ export default function AppPage() {
<DepositForm
usdcBalance={usdcBalance}
exchangeRate={exchangeRate}
onSuccess={refreshData}
onSuccess={handleRefresh}
isConnected={isConnected}
/>
)}
{tab === "withdraw" && (
<WithdrawForm
userShares={userShares}
onSuccess={refreshData}
onSuccess={handleRefresh}
isConnected={isConnected}
/>
)}
Expand Down
13 changes: 12 additions & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,25 @@ export default function HomePage() {
Next-Gen Liquidity Aggregator
</div>

<div className="flex flex-wrap gap-4 mb-10">
<div className="px-4 py-2 bg-teal/10 border border-teal/20 rounded-full text-teal text-xs font-black uppercase tracking-widest flex items-center gap-2">
<span className="w-2 h-2 bg-teal rounded-full animate-pulse" />
Live on Base Sepolia
</div>
<div className="px-4 py-2 bg-white/5 border border-white/10 rounded-full text-secondary text-xs font-black uppercase tracking-widest">
USDC Native
</div>
</div>

<h1 className="text-7xl md:text-9xl font-black leading-[0.85] mb-10 tracking-tighter">
DEPOSIT.<br />
EARN.<br />
<span className="text-teal text-glow">SAFE.</span>
</h1>

<p className="text-xl md:text-2xl text-secondary mb-12 max-w-xl leading-relaxed font-medium">
Maximize your USDC yield with institutional-grade security. Non-custodial, audited, and powered by Aave V3.
The next-generation vault for your <span className="text-white font-bold">USDC on Base</span>.
Maximize your yield with institutional-grade security. Non-custodial, audited, and powered by Aave V3.
</p>

<div className="flex flex-col sm:flex-row gap-6">
Expand Down
5 changes: 4 additions & 1 deletion components/Dashboard/BalanceCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ export const BalanceCards = ({
{card.icon}
</div>
<div>
<div className="text-3xl font-black mb-1 font-display tracking-tight uppercase">
<div className="text-3xl font-black mb-1 font-display tracking-tight uppercase flex items-baseline gap-2">
{card.val}
<span className="text-sm font-bold opacity-40">
{card.label.includes("Shares") ? "aUSDC" : "USDC"}
</span>
</div>
<div className="text-[10px] uppercase tracking-[0.2em] font-black text-muted">
{card.label}
Expand Down
4 changes: 2 additions & 2 deletions components/Dashboard/RewardsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export const RewardsTab = ({
<div className="text-[10px] uppercase tracking-[0.3em] font-black text-muted mb-4">
Accrued Yield (Live Profit)
</div>
<div className="text-5xl font-black text-teal mb-2 font-display tracking-tight">
${fmt(accruedYield)}
<div className="text-5xl font-black text-teal mb-2 font-display tracking-tight flex items-baseline gap-2">
${fmt(accruedYield)} <span className="text-sm opacity-40">USDC</span>
</div>
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-green-500/10 text-green-500 text-[10px] font-black uppercase tracking-wider">
<span className="relative flex h-2 w-2">
Expand Down
2 changes: 1 addition & 1 deletion components/Dashboard/StatsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const StatsGrid = ({
{stat.label}
</div>
<div className={`text-xl font-black font-display uppercase tracking-tight ${stat.cls}`}>
{stat.val}
{stat.val} {stat.label.includes("TVL") ? "USDC" : ""}
</div>
</div>
))}
Expand Down
44 changes: 24 additions & 20 deletions doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,35 +129,39 @@ Wallet (EIP-1193 provider) │
## Frontend-to-Blockchain Communication Diagram

```
User clicks "Deposit"
User clicks "Deposit USDC"
DepositForm.tsx
handleDeposit()
handleDepositFlow()
├─[if allowance < amount]──► useApprove.approve(amount)
├─[Step 1: Check Allowance]
│ │
│ ├─[if allowance < amount]──► useApprove.approve(amount)
│ │ │
│ │ usdcContract.approve(
│ │ YieldSaveVault, amount
│ │ )
│ │ │
│ │ wallet signs tx → tx.wait()
│ │
│ └─[allowance sufficient]───► Skip to Step 2
├─[Step 2: Deposit]──────────► useDeposit.submitDeposit(amount)
│ │
│ usdcContract.approve(
│ YieldSaveVault, amount
│ )
│ vaultContract.deposit(amount)
│ │
│ wallet signs tx
│ │
│ tx.wait() → confirmed
│ tx.wait() + receipt.status === 1
└─[allowance sufficient]──► useDeposit.submitDeposit(amount)
vaultContract.deposit(amount)
wallet signs tx
tx.wait() + receipt.status === 1
onSuccess() → refreshData()
re-fetch all balances
UI updates with new state
├─[Step 3: Refresh]──────────► handleRefresh()
│ │
│ wait(2000ms) → re-fetch all
│ │
│ UI updates with new balances
```

---
Expand Down
108 changes: 25 additions & 83 deletions doc/flows/deposit.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

## Overview

Depositing USDC into YieldSafe is a two-step process:
1. **Approve** — grant the vault permission to spend USDC (if not already approved)
2. **Deposit** — transfer USDC to the vault and receive share tokens in return
Depositing USDC into YieldSafe is a seamless, combined flow:
1. **One-Click Action** — The user clicks "Deposit USDC" once.
2. **Sequential Execution** — The app automatically checks allowance, triggers an **Approve** transaction if needed, and then immediately triggers the **Deposit** transaction.
3. **Receipt & Minting** — USDC is transferred to the vault, and interest-bearing share tokens (aUSDC) are minted to the user.

Share tokens represent the user's proportional ownership of the vault. As the vault earns yield via Aave, each share becomes worth more USDC over time.

Expand All @@ -15,101 +16,42 @@ Share tokens represent the user's proportional ownership of the vault. As the va
```
┌─────────────────────────────────────────────────────────────────────┐
│ USER ACTION │
│ Navigates to Deposit tab, enters "100" USDC
│ Navigates to Deposit tab, enters "100" USDC, clicks "Deposit USDC"
└──────────────────────────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
UI COMPONENT: DepositForm
STEP 1: ALLOWANCE CHECK
│ │
│ onChange → setDepositAmt("100") │
│ → previewByAssets(100_000_000n) [live preview call] │
│ → setDepositPreview(sharesResult) │
│ │
│ Display: "You will receive X shares" │
│ Display: "Exchange rate: 1 USDC = Y shares" │
└──────────────────────────────┬──────────────────────────────────────┘
│ currentAllowance < 100_000_000n ? │
└──────────────┬───────────────────────────────┬──────────────────────┘
│ │
[Needs Approval] [Already Approved]
▼ │
┌──────────────────────────┐ │
│ SUB-FLOW A: APPROVE │ │
│ │ │
│ useApprove.approve() │ │
│ → Wallet sign → tx.wait() │
└──────────────┬───────────┘ │
│ │
└───────────────┬───────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ ALLOWANCE CHECK │
│ │
│ useAllowance.refetchAllowance() │
│ → usdcContract.allowance(userAddr, vaultAddr) │
│ → currentAllowance: bigint │
│ │
│ needsApproval = currentAllowance < 100_000_000n │
└──────────────────────────────┬──────────────────────────────────────┘
┌───────────────┴───────────────┐
│ needs approval │ already approved
▼ ▼
┌──────────────────────────┐ ┌───────────────────────────────────┐
│ STEP 1: APPROVE │ │ STEP 2: DEPOSIT (skip to this) │
│ │ └───────────────────────────────────┘
│ User clicks │
│ "Approve USDC" │
│ │ │
│ ▼ │
│ useApprove.approve( │
│ 100_000_000n │
│ ) │
│ │ │
│ ▼ │
│ USDC.approve( │
│ vault, │
│ 100_000_000n │
│ ) │
│ │ │
│ ▼ │
│ Wallet signs tx ──────► Base Sepolia blockchain
│ │ │
│ ▼ │
│ tx.wait() confirmed │
│ │ │
│ ▼ │
│ refetchAllowance() │
│ → UI shows Deposit btn │
└──────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ STEP 2: DEPOSIT │
│ │
│ User clicks "Deposit" │
│ │ │
│ ▼ │
│ useDeposit.submitDeposit(100_000_000n) │
│ │ │
│ ▼ │
│ YieldSaveVault.deposit(100_000_000n) │
│ [vault internally calls USDC.transferFrom(user, vault, amount)] │
│ [vault deposits USDC into Aave V3, receives aUSDC] │
│ [vault mints share tokens to user] │
│ │ │
│ ▼ │
│ Wallet signs tx ──────────────────────────► Base Sepolia │
│ │ │
│ ▼ │
│ receipt = tx.wait() │
│ receipt.status === 1 → success │
│ │ │
│ ▼ │
│ onSuccess() called │
│ → Wallet sign → tx.wait() │
└──────────────────────────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ UI UPDATE: refreshData() │
│ │
│ Parallel refetch: │
│ ├── refetchUsdcBalance() → wallet USDC balance (decreased) │
│ ├── refetchUserShares() → user shares (increased) │
│ ├── refetchUserBalance() → vault position value (increased) │
│ ├── refetchUserDeposits() → principal recorded │
│ └── refetchVaultBalance() → TVL (increased) │
│ STEP 3: REFRESH DATA (with Delay) │
│ │
│ All state updates → React re-render → UI reflects new values │
│ 1. Wait 2000ms (allow block confirmation & indexing) │
│ 2. Parallel refetch of all balances │
│ 3. UI updates with new state │
└─────────────────────────────────────────────────────────────────────┘
```

Expand Down Expand Up @@ -159,7 +101,7 @@ This means early depositors are rewarded — their shares are proportionally mor

| File | Role in Deposit Flow |
|------|---------------------|
| [components/Deposit/DepositForm.tsx](../../components/Deposit/DepositForm.tsx) | UI, form state, orchestrates approve + deposit |
| [components/Deposit/DepositForm.tsx](../../components/Deposit/DepositForm.tsx) | UI, form state, orchestrates combined flow |
| [hooks/useAllowance.ts](../../hooks/useAllowance.ts) | Reads current USDC approval |
| [hooks/useApprove.ts](../../hooks/useApprove.ts) | Sends approve transaction |
| [hooks/useDeposit.ts](../../hooks/useDeposit.ts) | Sends deposit transaction |
Expand Down
Loading