Skip to main content

Architecture Overview

The MetaVault AI frontend is a modern Web3 application built with Next.js 14 using the App Router, React 18, TypeScript, and Tailwind CSS. It provides a comprehensive dashboard for managing DeFi vault operations and interacting with an AI-powered assistant.

Tech Stack

Core Framework

  • Next.js 14.2.0 - App Router with Server Components
  • React 18.3.0 - UI library
  • TypeScript 5.5.0 - Type safety
  • Tailwind CSS 3.4.0 - Utility-first styling

Web3 Integration

  • Wagmi 2.7.0 - React Hooks for Ethereum
  • Viem 2.9.0 - TypeScript Ethereum library
  • WalletConnect 2.21.10 - Multi-wallet support

State Management

  • @tanstack/react-query 5.51.0 - Async state management
  • Wagmi’s built-in state - Wallet and contract state

Utilities

  • Ethers.js 6.13.0 - Additional Ethereum utilities
  • Lucide React 0.400.0 - Icon library
  • clsx 2.1.1 - Conditional class names

Project Structure

packages/frontend/
├── src/
│   ├── app/                    # Next.js 14 App Router
│   │   ├── api/               # API routes
│   │   │   ├── agent/         # AI agent chat endpoint
│   │   │   └── vault/         # Vault data endpoints
│   │   ├── layout.tsx         # Root layout with metadata
│   │   ├── page.tsx           # Home page (main dashboard)
│   │   ├── providers.tsx      # Wagmi & React Query providers
│   │   └── globals.css        # Global styles
│   ├── components/            # React components
│   │   ├── VaultDashboard.tsx # Main vault dashboard
│   │   ├── AgentChat.tsx      # AI assistant chat
│   │   ├── WalletConnect.tsx  # Wallet connection UI
│   │   ├── DepositModal.tsx   # Deposit workflow
│   │   ├── WithdrawModal.tsx  # Withdraw workflow
│   │   ├── StrategyCard.tsx   # Strategy display card
│   │   └── DepositorsList.tsx # Active holders list
│   ├── config/
│   │   └── wagmi.ts           # Wagmi configuration
│   └── lib/
│       ├── contracts.ts       # Contract ABIs and addresses
│       └── utils.ts           # Utility functions
├── public/                    # Static assets
├── next.config.js            # Next.js configuration
├── tailwind.config.ts        # Tailwind configuration
├── tsconfig.json             # TypeScript configuration
└── package.json              # Dependencies

App Router Structure

Root Layout (src/app/layout.tsx:1)

The root layout wraps the entire application with metadata and providers:
import { ReactNode } from "react";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { Providers } from "./providers";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "MetaVault AI",
  description: "AI-Powered DeFi Vault Management",
};

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Providers Configuration (src/app/providers.tsx:1)

Centralized provider setup for Wagmi and React Query:
"use client"

import { type ReactNode, useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, type State } from 'wagmi'
import { config } from '@/config/wagmi'

export function Providers({
  children,
  initialState
}: {
  children: ReactNode
  initialState?: State
}) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: {
      queries: {
        refetchOnWindowFocus: false,
        refetchOnReconnect: true,
        staleTime: 5 * 60 * 1000,
        gcTime: 10 * 60 * 1000,
        retry: 2,
        retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
      },
      mutations: {
        retry: 1,
      },
    },
  }))

  return (
    <WagmiProvider config={config} initialState={initialState} reconnectOnMount={true}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  )
}
Key Configuration:
  • Disabled automatic refetch on window focus to reduce RPC calls
  • 5-minute stale time and 10-minute garbage collection
  • Exponential backoff retry strategy
  • Automatic reconnection on mount

Main Page (src/app/page.tsx:1)

The home page implements a tabbed interface with sidebar navigation:
"use client";

import { VaultDashboard } from "@/components/VaultDashboard";
import { AgentChat } from "@/components/AgentChat";
import { WalletConnect } from "@/components/WalletConnect";
import { useState } from "react";
import { LayoutDashboard, Bot } from "lucide-react";

export default function Home() {
  const [activeTab, setActiveTab] = useState<"vault" | "agent">("vault");

  return (
    <div className="flex min-h-screen bg-[#05050A]">
      {/* Sidebar Navigation */}
      <div className="w-64 glass-panel border-r border-white/5 p-6">
        {/* Branding */}
        <div className="mb-10">
          <h1 className="text-xl font-bold text-white">
            MetaVault <span className="text-blue-400">AI</span>
          </h1>
        </div>

        {/* Navigation */}
        <nav className="space-y-2">
          <button onClick={() => setActiveTab("vault")}>
            Dashboard
          </button>
          <button onClick={() => setActiveTab("agent")}>
            AI Assistant
          </button>
        </nav>
      </div>

      {/* Main Content */}
      <div className="flex-1">
        <header className="p-6">
          <WalletConnect />
        </header>
        <main className="p-8">
          {activeTab === "vault" && <VaultDashboard />}
          {activeTab === "agent" && <AgentChat />}
        </main>
      </div>
    </div>
  );
}

API Routes

Agent Chat API (src/app/api/agent/route.ts:1)

Proxy endpoint for AI assistant communication:
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const { message, sessionId, wallet } = await request.json();
  
  const AGENT_URL = process.env.AGENT_API_URL || "http://localhost:3002/chat";
  
  // Forward to Agent Backend
  const agentRes = await fetch(AGENT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    cache: "no-store",
    body: JSON.stringify({ message, sessionId, wallet }),
  });
  
  const data = await agentRes.json();
  
  return NextResponse.json({
    success: true,
    reply: data.reply,
    sessionId: data.sessionId,
    unsignedTx: data.unsignedTx || null,
  });
}
Features:
  • Session management for conversation continuity
  • Wallet address forwarding for personalized responses
  • Unsigned transaction handling for AI-initiated transactions

TypeScript Configuration

Path Aliases (tsconfig.json:21)

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["dom", "dom.iterable", "esnext"],
    "strict": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
Enables clean imports like @/components/VaultDashboard instead of relative paths.

Styling Architecture

Tailwind Configuration (tailwind.config.ts:1)

import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [],
};

Custom Glass Morphism Styles

The application uses custom glass-panel effects defined in globals.css:
.glass-panel {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.glass-card {
  background: rgba(255, 255, 255, 0.03);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

Environment Variables

# Wagmi Configuration
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id
NEXT_PUBLIC_RPC_URL=https://sepolia.infura.io/v3/your_key

# Contract Addresses
NEXT_PUBLIC_VAULT_ADDRESS=0x...
NEXT_PUBLIC_ROUTER_ADDRESS=0x...
NEXT_PUBLIC_STRATEGY_AAVE_ADDRESS=0x...
NEXT_PUBLIC_STRATEGY_LEVERAGE_ADDRESS=0x...
NEXT_PUBLIC_LINK_ADDRESS=0x...

# Agent Backend
AGENT_API_URL=http://localhost:3002/chat

Build Configuration

Next.js Config (next.config.js:1)

const nextConfig = {
  webpack: (config) => {
    // Externalize Node.js-only dependencies
    config.externals.push("pino-pretty", "lokijs", "encoding");
    return config;
  },
};
Externalizes packages that cause issues in browser environments.

Development Workflow

Running Locally

cd packages/frontend
npm install
npm run dev
Runs on http://localhost:3000

Building for Production

npm run build
npm run start

Type Checking

npm run lint

Performance Optimizations

  1. React Query Caching - 5-minute stale time reduces RPC calls
  2. Selective Refetching - Disabled window focus refetch
  3. Event-Driven Updates - Real-time contract event listeners
  4. SSR Support - Cookie-based storage for server-side rendering
  5. Batch RPC Calls - Multicall enabled for multiple reads

Next Steps

React Components

Learn about the component architecture and patterns

Web3 Integration

Deep dive into Wagmi and Viem usage patterns