Skip to content

Latest commit

 

History

History
539 lines (422 loc) · 10.8 KB

File metadata and controls

539 lines (422 loc) · 10.8 KB

LP Dashboard Implementation Guide

🚀 Quick Start

Installation

No additional dependencies required! The LP Dashboard uses existing project dependencies:

  • React 18
  • TypeScript
  • Tailwind CSS

Usage

import LPDashboard from '@/components/LPDashboard';

function App() {
  const walletAddress = "GAXYZ..."; // or null if not connected
  
  return <LPDashboard walletAddress={walletAddress} />;
}

📁 File Structure

frontend/src/components/
├── LPDashboard.tsx                 # Main dashboard component
└── lp/
    ├── LPMetricsOverview.tsx       # 4 metric cards at top
    ├── LPPositionsView.tsx         # Active positions list
    ├── LPEarningsChart.tsx         # Analytics/earnings chart
    ├── LPDepositModal.tsx          # Deposit liquidity modal
    └── LPWithdrawModal.tsx         # Withdraw liquidity modal

🎨 Components Overview

1. LPDashboard (Main)

Purpose: Main container and state management

Props:

interface Props {
  walletAddress: string | null;
}

Features:

  • Tab navigation (Overview, Positions, Analytics)
  • Modal management
  • Pool data management
  • Wallet connection check

State:

  • activeTab: Current tab selection
  • showDepositModal: Deposit modal visibility
  • showWithdrawModal: Withdraw modal visibility
  • selectedPool: Currently selected pool

2. LPMetricsOverview

Purpose: Display 4 key metrics at dashboard top

Props:

interface Metrics {
  totalLiquidity: number;
  totalFeesEarned: number;
  averageAPY: number;
  activePositions: number;
}

Metrics Displayed:

  1. Total Liquidity Provided
  2. Total Fees Earned
  3. Average APY
  4. Active Positions

Design: 4-column grid with gradient cards

3. LPPositionsView

Purpose: Show user's active LP positions

Props:

interface Props {
  pools: LPPool[];
  onWithdraw: (pool: LPPool) => void;
}

Features:

  • Portfolio summary card
  • Individual position cards
  • Risk indicators
  • Impermanent loss warnings
  • Action buttons

Empty State: Shows when no positions

4. LPEarningsChart

Purpose: Visualize earnings over time

Props:

interface Props {
  pools: LPPool[];
}

Features:

  • Timeframe selector (7d, 30d, 90d, 1y)
  • Cumulative earnings chart
  • Daily average calculation
  • Projected annual earnings
  • Pool-by-pool breakdown
  • Earning vs Betting explanation

Chart: SVG-based line chart with gradient fill

5. LPDepositModal

Purpose: Deposit liquidity into a pool

Props:

interface Props {
  pool: LPPool;
  onClose: () => void;
  walletAddress: string;
}

Features:

  • XLM amount input
  • USDC amount input
  • MAX buttons
  • Real-time estimates
  • Pool share calculation
  • Daily/annual earnings projection
  • Stellar low fee notice

Validation:

  • Checks sufficient balance
  • Requires both token amounts
  • Shows error messages

6. LPWithdrawModal

Purpose: Withdraw liquidity from a pool

Props:

interface Props {
  pool: LPPool;
  onClose: () => void;
  walletAddress: string;
}

Features:

  • Percentage slider (0-100%)
  • Quick select buttons (25%, 50%, 75%, 100%)
  • Token breakdown display
  • Unclaimed fees calculation
  • Impermanent loss warning
  • Stellar low fee notice

Calculations:

  • XLM amount = (userLiquidity * percentage) / 200
  • USDC amount = (userLiquidity * percentage) / 200
  • Fees = (feesEarned * percentage) / 100

🎯 Data Structure

LPPool Interface

interface LPPool {
  id: string;                    // Unique pool identifier
  name: string;                  // Pool display name
  pair: string;                  // Token pair (e.g., "XLM/USDC")
  tvl: number;                   // Total Value Locked
  apy: number;                   // Annual Percentage Yield
  volume24h: number;             // 24-hour trading volume
  fees24h: number;               // 24-hour fees generated
  riskLevel: "low" | "medium" | "high";  // Risk indicator
  userLiquidity?: number;        // User's liquidity (if any)
  userShare?: number;            // User's pool share %
  feesEarned?: number;           // User's earned fees
}

Mock Data Example

const pools: LPPool[] = [
  {
    id: "1",
    name: "BTC/USDC Market Pool",
    pair: "XLM/USDC",
    tvl: 1250000,
    apy: 24.5,
    volume24h: 85000,
    fees24h: 255,
    riskLevel: "low",
    userLiquidity: 5000,
    userShare: 0.4,
    feesEarned: 125.50,
  },
  // ... more pools
];

🔌 API Integration

Required Endpoints

1. Get Available Pools

GET /api/lp/pools
Response: LPPool[]

2. Get User Positions

GET /api/lp/positions/:walletAddress
Response: LPPool[]

3. Deposit Liquidity

POST /api/lp/deposit
Body: {
  poolId: string;
  xlmAmount: number;
  usdcAmount: number;
  walletAddress: string;
}
Response: { success: boolean; position: LPPool }

4. Withdraw Liquidity

POST /api/lp/withdraw
Body: {
  poolId: string;
  percentage: number;
  walletAddress: string;
}
Response: { success: boolean; xlmAmount: number; usdcAmount: number; fees: number }

5. Get Earnings History

GET /api/lp/earnings/:walletAddress?timeframe=30d
Response: {
  data: Array<{ day: number; earnings: number; cumulative: number }>;
  total: number;
}

🎨 Customization

Colors

Update Tailwind classes to match your brand:

// Primary gradient (Deposit button)
"bg-gradient-to-r from-blue-600 to-purple-600"

// Risk levels
"text-green-400"  // Low risk
"text-yellow-400" // Medium risk
"text-red-400"    // High risk

// Backgrounds
"bg-gray-900"     // Main background
"bg-gray-800"     // Cards

Typography

Adjust font sizes in components:

"text-3xl"  // Large headings
"text-xl"   // Card titles
"text-sm"   // Body text
"text-xs"   // Small text

Spacing

Modify padding and gaps:

"p-6"       // Card padding
"gap-4"     // Element gaps
"space-y-6" // Vertical spacing

🧪 Testing

Unit Tests

// Test pool card rendering
test('renders pool card with correct data', () => {
  const pool = mockPool;
  render(<PoolCard pool={pool} />);
  expect(screen.getByText(pool.name)).toBeInTheDocument();
  expect(screen.getByText(`${pool.apy}%`)).toBeInTheDocument();
});

// Test deposit modal calculations
test('calculates estimated earnings correctly', () => {
  const pool = mockPool;
  render(<LPDepositModal pool={pool} />);
  // ... test calculations
});

Integration Tests

// Test full deposit flow
test('completes deposit flow', async () => {
  render(<LPDashboard walletAddress="GAXYZ..." />);
  
  // Click deposit button
  fireEvent.click(screen.getByText('Deposit XLM/USDC'));
  
  // Enter amounts
  fireEvent.change(screen.getByPlaceholderText('0.00'), {
    target: { value: '1000' }
  });
  
  // Submit
  fireEvent.click(screen.getByText('Deposit XLM/USDC'));
  
  // Verify success
  await waitFor(() => {
    expect(mockDepositAPI).toHaveBeenCalled();
  });
});

📊 Performance Optimization

1. Memoization

import { useMemo } from 'react';

const totalMetrics = useMemo(() => ({
  totalLiquidity: pools.reduce((sum, p) => sum + (p.userLiquidity || 0), 0),
  totalFeesEarned: pools.reduce((sum, p) => sum + (p.feesEarned || 0), 0),
  averageAPY: pools.reduce((sum, p) => sum + p.apy, 0) / pools.length,
  activePositions: pools.filter(p => p.userLiquidity).length,
}), [pools]);

2. Lazy Loading

import { lazy, Suspense } from 'react';

const LPEarningsChart = lazy(() => import('./lp/LPEarningsChart'));

<Suspense fallback={<LoadingSpinner />}>
  <LPEarningsChart pools={pools} />
</Suspense>

3. Virtual Scrolling

For large pool lists:

import { FixedSizeList } from 'react-window';

<FixedSizeList
  height={600}
  itemCount={pools.length}
  itemSize={200}
>
  {({ index, style }) => (
    <div style={style}>
      <PoolCard pool={pools[index]} />
    </div>
  )}
</FixedSizeList>

🔐 Security Considerations

1. Input Validation

// Validate amounts
if (parseFloat(xlmAmount) <= 0) {
  setError("Amount must be positive");
  return;
}

// Check balance
if (parseFloat(xlmAmount) > xlmBalance) {
  setError("Insufficient balance");
  return;
}

2. Transaction Signing

// Use Freighter wallet for signing
import { signTransaction } from '@stellar/freighter-api';

const signedTx = await signTransaction(transaction, {
  network: 'TESTNET',
  accountToSign: walletAddress,
});

3. Error Handling

try {
  await depositLiquidity(poolId, xlmAmount, usdcAmount);
} catch (err) {
  if (err.code === 'USER_REJECTED') {
    setError('Transaction rejected by user');
  } else if (err.code === 'INSUFFICIENT_BALANCE') {
    setError('Insufficient balance');
  } else {
    setError('Transaction failed. Please try again.');
  }
}

🎯 Best Practices

1. Loading States

Always show loading indicators:

{loading ? (
  <div className="flex items-center justify-center py-12">
    <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500" />
  </div>
) : (
  <PoolsList pools={pools} />
)}

2. Error Boundaries

Wrap components in error boundaries:

<ErrorBoundary fallback={<ErrorMessage />}>
  <LPDashboard walletAddress={walletAddress} />
</ErrorBoundary>

3. Accessibility

Add ARIA labels and keyboard navigation:

<button
  aria-label="Deposit liquidity"
  onClick={handleDeposit}
  onKeyPress={(e) => e.key === 'Enter' && handleDeposit()}
>
  Deposit XLM/USDC
</button>

📱 Mobile Optimization

Responsive Breakpoints

// Tailwind breakpoints
sm: 640px   // Mobile landscape
md: 768px   // Tablet
lg: 1024px  // Desktop
xl: 1280px  // Large desktop

Mobile-Specific Adjustments

// Stack metrics on mobile
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
  {/* Metrics */}
</div>

// Full-screen modals on mobile
<div className="fixed inset-0 md:inset-auto md:max-w-lg">
  {/* Modal content */}
</div>

🚀 Deployment Checklist

  • Replace mock data with API calls
  • Add error boundaries
  • Implement loading states
  • Add analytics tracking
  • Test on multiple devices
  • Optimize images/assets
  • Enable production builds
  • Set up monitoring
  • Configure CDN
  • Test wallet integration

📚 Additional Resources


Status: ✅ Ready for Integration
Components: 7
Lines of Code: 1,500+
Dependencies: None (uses existing)