Welcome to the TradeFlow Web codebase! This document serves as a guide for new contributors and maintainers. It outlines our directory structure, rendering paradigms, Web3 architecture, and guidelines for writing code in this repository.
TradeFlow is built using Next.js 14 (utilizing the App Router), TypeScript, Tailwind CSS, and integrates with the Stellar/Soroban smart contract ecosystem.
Here is the bird's-eye view of the directory structure under /src:
src/
├── app/ # Next.js App Router (Routing, layouts, and page definitions)
│ ├── api/ # API Route Handlers (Server-side endpoints)
│ ├── marketplace/ # Trade invoice marketplace pages
│ ├── swap/ # Token swap interfaces
│ │ └── page.tsx # Swap page entry point
│ ├── globals.css # Global styles and Tailwind directives
│ ├── layout.tsx # Root layout wrapper
│ └── page.tsx # Homepage
├── components/ # Modular, reusable UI components
│ ├── ui/ # Low-level presentational primitives (Button, Tooltip, etc.)
│ ├── layout/ # Shared layout components (Navbar, Sidebar)
│ ├── ConnectWallet.tsx # Feature-specific modular components
│ └── InvoiceMintForm.tsx
├── contexts/ # Specialized React contexts (e.g., Slippage, NetworkCongestion)
├── hooks/ # Custom hooks (e.g., wallet connection, Soroban events)
├── lib/ # Shared utility functions and Stellar helper methods
│ ├── stellar.ts # Lower-level Stellar SDK operations
│ └── format.ts # Numeric and date formatting utils
├── providers/ # Application-wide global providers (e.g., QueryClientProvider)
├── soroban/ # Core Soroban integration layer
│ ├── contracts/ # TypeScript bindings for Soroban smart contracts
│ │ └── invoice.ts # Invoice contract bindings
│ ├── client.ts # Soroban RPC Server client instance manager
│ └── config.ts # Network details (RPC URL, Passphrase, Contract IDs)
├── stores/ # Client-side state stores (Zustand state managers)
│ └── useWeb3Store.ts # Global Web3 wallet and network state store
└── types/ # Project-wide TypeScript type definitions
To keep the codebase maintainable and scalable, we enforce a strict separation between routing/layouts and modular UI components.
- Purpose: Defines routes, page templates, layouts, and error boundaries.
- Rule: Files inside
src/appshould act primarily as compositions of UI components. Avoid writing complex form validation, raw layout styles, or massive component structures directly insidepage.tsxorlayout.tsx. - Conventions:
page.tsx: Defines the unique UI for a route.layout.tsx: Defines shared UI across multiple pages (e.g., sidebar and header).error.tsx/not-found.tsx: Error boundaries and fallback pages.api//route.ts: Server-side API endpoints (Route Handlers).
- Purpose: Holds all modular, reusable components.
- Organization:
src/components/ui/: Low-level, stateless, presentational primitives. These are the bricks of our application (e.g.,Button.tsx,Slider.tsx,Tooltip.tsx). They should not depend on global Web3 state or business logic.src/components/layout/: Components responsible for structural placement (e.g.,Navbar.tsx,Sidebar.tsx).src/components/(Root): Domain-specific or stateful UI blocks. These components combine UI primitives with state and business logic (e.g.,InvoiceMintForm.tsx,ConnectWallet.tsx,SwapInterface.tsx).
Next.js 14 uses React Server Components (RSC) by default. Understanding when to use the 'use client' directive is crucial for application performance, bundle size optimization, and SEO.
By default, keep components as Server Components. This keeps JS bundle sizes small and allows direct, secure server-side operations.
- Use Case: Static layouts, fetching initial metadata, pure presentation content, and structural wrappers.
Add the 'use client' directive at the very top of your file when the component requires client-side interactivity or browser-specific APIs.
- Use Case:
- Using React state or lifecycle hooks (
useState,useReducer,useEffect,useLayoutEffect). - Using browser APIs (e.g.,
window,localStorage, Web3 extension wallets like Freighter). - Attaching event listeners (
onClick,onChange,onSubmit). - Interacting with client-side contexts or hooks (e.g., wallet state, slippage settings).
- Using React state or lifecycle hooks (
// src/components/ui/Card.tsx
// No 'use client' is needed here. This runs entirely on the server.
import React from 'react';
interface CardProps {
title: string;
children: React.ReactNode;
}
export function Card({ title, children }: CardProps) {
return (
<div className="rounded-xl border border-gray-800 bg-gray-900/50 p-6 backdrop-blur-md">
<h3 className="text-lg font-semibold text-white mb-2">{title}</h3>
<div>{children}</div>
</div>
);
}// src/components/AddTrustlineButton.tsx
'use client'; // Required for click handlers, state, and browser wallets
import { useState } from 'react';
import { Button } from './ui/Button';
export function AddTrustlineButton({ assetCode }: { assetCode: string }) {
const [isPending, setIsPending] = useState(false);
const handleAddTrustline = async () => {
setIsPending(true);
try {
// Browser wallet/Freighter interaction here...
console.log(`Adding trustline for ${assetCode}...`);
} catch (error) {
console.error(error);
} finally {
setIsPending(false);
}
};
return (
<Button onClick={handleAddTrustline} disabled={isPending}>
{isPending ? 'Adding...' : `Add ${assetCode} Trustline`}
</Button>
);
}TradeFlow integrates with the Stellar network and Soroban smart contracts. The interaction layer is structured as follows:
graph TD
UI[Client Components / UI] --> Hooks[Custom Hooks: src/hooks]
Hooks --> Stores[Zustand Stores: src/stores]
Hooks --> Bindings[Contract Bindings: src/soroban/contracts]
Bindings --> Client[Soroban Client: src/soroban/client.ts]
Client --> RPC[Stellar/Soroban RPC]
TypeScript wrappers representing Soroban smart contracts (e.g., invoice.ts). These bindings:
- Map contract methods to strongly typed TypeScript functions.
- Serialize JavaScript arguments into XDR format.
- Deserialize contract return values back into typed JavaScript structures.
config.ts: Resolves network-specific configurations (like Testnet or Mainnet RPC endpoints, passphrases, and contract IDs) by reading active network contexts and environment variables.client.ts: Manages a cached, single instance of the SorobanServerclient (getSorobanClient()). It handles cache clearing automatically when switching networks (e.g., Testnet to Futurenet).
We manage decentralized application state through a mix of React Contexts and global Zustand stores:
src/providers/: Holds high-level providers that wrap the app layout tree, such asQueryClientProvider(for React Query caching).src/contexts/: Contains specialized providers for targeted slices of configuration/state (e.g.,SlippageContext,ExpertModeContext,NetworkCongestionContext).src/stores/: Holds Zustand state managers for high-performance reactive state. For example:useWeb3Store.tsstores user wallet connection state, selected Stellar network details, and active account public keys.
Hooks bridge UI components with contract actions. They abstract asynchronous states, event handlers, and feedback mechanisms.
useWalletConnection.ts: Manages connecting/disconnecting via Freighter, loading balances, and handling browser extension events.useSorobanEvents.ts: Listens to RPC event streams for contract emission events.useTxWithToast.ts: Executes a transaction and automatically triggers success/error notifications (toasts).
- Keep Presentational Components Clean: Do not import
useWeb3StoreorgetSorobanClientinto/components/ui/elements. Keep them strictly layout and style-oriented. - Handle Loading and Error States: Always provide skeleton loader fallbacks (
SkeletonRow,TableSkeleton) and leverage custom error boundaries. - Verify Network Compatibility: Before executing a transaction, use the
TransactionGuardcomponent oruseNetworkDetectionhook to check if the user is connected to the matching Stellar network. - Follow Linting Rules: Run
npm run lintand verify tests pass withnpm run testbefore submitting your Pull Request.