- Overview
- Tech Stack
- Project Structure
- Feature Areas
- API Layer
- Getting Started
- Environment Variables
- Available Scripts
- Testing
- Continuous Integration
- Contributing
- License
StellarSettle is a Next.js (App Router) frontend built on two related product surfaces:
- Invoice financing marketplace — sellers publish invoices for funding, investors browse and invest, funded invoices are tracked through settlement and payout, all gated behind a KYC flow.
- Creator Keys — a tokenized creator-economy layer: buyable/burnable "keys" per creator with governance voting, vesting schedules, whitelisting, dividend distribution, and admin-controlled trading pauses/timelocks.
Both surfaces share a wallet-based auth layer (Stellar via Freighter), a common API client (lib/api/index.ts), and an admin panel that reviews invoices, manages settlements, and controls key trading.
Note: this Readme was rewritten from the actual current codebase (routes,
lib/apiexports,package.jsonscripts) rather than the project's original template copy, which described only the invoice-marketplace half and referenced scripts/tooling (Playwright e2e, a11y tests, Docker, Vercel CLI steps) that aren't present in this repo. See Getting Started and Available Scripts below for what actually exists today.
| Concern | Library |
|---|---|
| Framework | Next.js 15 (App Router, Turbopack dev server) |
| UI runtime | React 19 |
| Language | TypeScript 5.7 |
| Styling | Tailwind CSS 4 (@tailwindcss/postcss) |
| Component primitives | Radix UI + radix-ui, styled as shadcn/ui ("New York" style) |
| Icons | lucide-react |
| Data fetching / cache | TanStack React Query 5 |
| HTTP client | axios |
| Forms | React Hook Form + @hookform/resolvers |
| Validation | Zod |
| Charts | Recharts |
| Dates | date-fns |
| Toasts | sonner |
| Theming | next-themes |
| Stellar wallet | @stellar/freighter-api |
| Stellar SDK | stellar-sdk |
| Unit/component testing | Vitest 4 + Testing Library (React, DOM, user-event) + jsdom |
| Lint | ESLint (eslint-config-next) |
app/
├── page.tsx # Landing page — top-investors leaderboard + recently-viewed invoices
├── layout.tsx, error.tsx, not-found.tsx, globals.css
├── (auth)/
│ ├── connect-wallet/ # Freighter connection step
│ ├── login/
│ └── register/
├── (dashboard)/
│ ├── seller/ # Seller dashboard
│ │ ├── edit/[id]/ # Edit a published invoice
│ │ ├── invoices/[id]/edit/
│ │ └── publish/ # Publish flow + success page
│ └── investor/ # Investor dashboard (portfolio)
│ └── notifications/
├── admin/
│ ├── invoices/ # Admin invoice queue
│ └── review/ # Admin invoice review
├── kyc/
│ ├── start/
│ ├── reapply/
│ └── confirmed/
├── keys/[keyId]/ # Creator key detail page
├── marketplace/
│ └── [id]/ # Invoice detail
├── profile/
└── api/ # (currently unused — .gitkeep only; the app calls an external API via lib/api)
components/
├── ui/ # shadcn/ui primitives: button, card, input, select, dialog(dropdown/popover), badge, skeleton, sonner, switch, textarea, separator, label
├── layout/ # Navbar, NotificationBellBadge, GoToMarketplaceButton
├── marketplace/ # invoice-card, filter-bar/FilterPanel, countdown-timer, recently-viewed, TopInvestorsLeaderboard
├── invoices/ # PublishInvoiceForm, EditInvoiceForm, InvoiceDetail, InvestDialog/InvestmentModal, DocumentUpload/DocumentPreview, FundingProgressBar, InvoiceStatusBadge, SettlementReturnCard, ShareInvoiceButton, ErrorBoundary
├── dashboard/ # SellerDashboard, InvestorPortfolio, PositionCard, PayoutHistoryTable, KycStatusBanner, KycSubmissionForm, NotificationList/Preferences, OnboardingChecklist, UserProfile, WalletActivityFeed, VestingProgressWidget, KeyTransferModal, HoldingActionsMenu
├── keys/ # BuyKeyPanel, CreatorKeyDetail, CreatorRevenueSection, CreatorVestingSection, GovernanceTab, VoteModal, CreateProposalModal, SupplyCapSection/Settings, WhitelistManager, BurnKeyModal, DistributeDividendsPanel
├── admin/ # AdminInvoiceReview(s), AdminSettlements, AuditLogViewer, TimelockProposalsPanel, TradingControlsPanel
├── wallet/ # WalletChip
└── providers.tsx # App-wide providers (React Query, theming, etc.)
context/
└── AuthContext.tsx # Wallet/session auth state
hooks/ # One hook per data domain — useAuth, useStellarWallet, useInvoices, useInvestments,
# usePortfolio, useSellerDashboard, useCreatorKeys, useCreatorRevenue, useVesting,
# useWhitelist, useTimelockProposals, useTradingPause, useWalletActivity,
# useNotifications, useNotificationPreferences, useLeaderboard, useRecentlyViewed,
# useCountdown, usePageTitle, usePageLoadPerformanceLog
lib/
├── api/index.ts # Single API client module — every network call + response/request type (see below)
├── stellar/index.ts # connectWallet/getNetwork/getAddress/truncateAddress via Freighter
├── auth.ts, format.ts, logger.ts, performance-logger.ts, portfolio.ts, recentlyViewed.ts, utils.ts
└── validation/ # deadline.ts, face-value.ts, investment-amount.ts (Zod schemas)
(auth)/connect-wallet,(auth)/login,(auth)/registerhandle onboarding.lib/stellar/index.tswraps@stellar/freighter-api:connectWallet(),getNetwork(),getAddress(), plus atruncateAddress()display helper.context/AuthContext.tsx+hooks/useAuth.ts/useStellarWallet.tshold the session/wallet state consumed across the app (e.g.components/wallet/WalletChip.tsxin the navbar).
app/kyc/start,app/kyc/reapply,app/kyc/confirmedimplement the seller KYC flow.submitKyc()andfetchSellerKycStatus()inlib/api/index.ts; status is one of the values inSellerKycStatusValue, surfaced viaKycStatusBanner/KycSubmissionForm.
- Sellers:
(dashboard)/seller— publish (PublishInvoiceForm,publishInvoice()), edit, and track invoices viaSellerDashboard/fetchSellerDashboard(). - Investors:
/marketplacebrowse + filter (FilterPanel,filter-bar),/marketplace/[id]detail + invest (InvestDialog/InvestmentModal→investInInvoice()),(dashboard)/investorportfolio (InvestorPortfolio,fetchPortfolio()), payout history (PayoutHistoryTable,fetchInvestorPayouts()). - Invoices carry
status:"open" | "funded" | "settled" | "rejected" | "draft", ayield_percentage, and support position transfer between investors (transferInvoicePosition()). - Documents attach via
uploadDocumentToIpfs()(DocumentUpload/DocumentPreview). - A leaderboard (
TopInvestorsLeaderboard,useLeaderboard) and a recently-viewed list (useRecentlyViewed, backed bylib/recentlyViewed.ts) appear on the landing page.
A parallel tokenized system, per creator:
- Buying/burning:
BuyKeyPanel→buyCreatorKey();BurnKeyModal→burnCreatorKey(); transfer viaKeyTransferModal→transferCreatorKey(). - Supply:
KeySupply,SupplyCapSection/SupplyCapSettings→fetchKeySupply(),updateKeySupplyCap(). - Governance:
GovernanceTab,CreateProposalModal,VoteModal→fetchKeyProposals(),createGovernanceProposal(),castGovernanceVote(). - Vesting:
VestingProgressWidget,CreatorVestingSection,useVesting→fetchVestingSchedule(),claimVestedKeys(). - Whitelisting:
WhitelistManager,useWhitelist→fetchKeyWhitelistStatus(),fetchKeyWhitelist(),addWhitelistAddress(),removeWhitelistAddress(),updateWhitelistMode(). - Revenue & dividends:
CreatorRevenueSection,useCreatorRevenue→fetchCreatorRevenue()(withMonthlyRevenuebreakdowns);DistributeDividendsPanel→distributeDividend(). app/keys/[keyId]is the public creator-key detail page (CreatorKeyDetail,fetchCreatorKeyDetail()).
app/admin (invoice queue, review) plus components under components/admin/:
- Invoice review:
AdminInvoiceReview(s)→fetchAdminInvoices(),approveAdminInvoice(),rejectAdminInvoice(). - Settlements:
AdminSettlements→fetchSettlements(),proposeSettlement(),approveSettlement(). - Key trading controls:
TradingControlsPanel,useTradingPause→fetchAdminKeyControls(),proposeKeyPause(),approveKeyPause()(trading status:"active" | "pause_pending" | "paused"). - Timelock proposals:
TimelockProposalsPanel,useTimelockProposals→fetchTimelockProposals(),executeTimelockProposal(),cancelTimelockProposal(). - Audit log:
AuditLogViewer→fetchAuditLog().
NotificationList,NotificationPreferences,NotificationBellBadge,useNotifications/useNotificationPreferences→fetchNotificationPreferences(),updateNotificationPreference(). Notification events:"new_invoice" | "funding_milestone" | "settlement"; channels:"email" | "in_app".WalletActivityFeed,useWalletActivity→fetchWalletActivity()(typedWalletActivityEvent/WalletActivityType).
lib/api/index.ts is the single module every hook/component calls into for network access — it has no framework dependency of its own (plain fetch/axios + typed request/response interfaces) and is the first place to look when tracing a feature end-to-end. It centralizes: invoices, protocol status, notifications, IPFS document upload, portfolio, seller dashboard/KYC, admin invoice + settlement + key-control + timelock + audit endpoints, creator-key detail/governance/whitelist/supply/vesting/revenue, payouts, and wallet activity — see the file directly for exact request/response shapes; every exported function and type is grep-able there in one place (export async function / export interface).
- Node.js (a version compatible with Next.js 15 / React 19 — Node 18+ is the practical floor)
- A Stellar wallet browser extension for local testing (Freighter)
npm install
cp .env.example .env.local
# edit .env.local with real values
npm run dev
# open http://localhost:3000npm run dev uses Next.js with Turbopack.
From .env.example:
# API
NEXT_PUBLIC_API_URL=http://localhost:3000/api
NEXT_PUBLIC_API_VERSION=v1
# Stellar
NEXT_PUBLIC_STELLAR_NETWORK=testnet
NEXT_PUBLIC_STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
# Smart Contracts
NEXT_PUBLIC_ESCROW_CONTRACT_ID=CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
NEXT_PUBLIC_TOKEN_CONTRACT_ID=CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Features
NEXT_PUBLIC_ENABLE_ANALYTICS=true
NEXT_PUBLIC_ENABLE_NOTIFICATIONS=trueExactly what's defined in package.json — nothing more:
| Command | Description |
|---|---|
npm run dev |
Start the dev server (Turbopack) |
npm run build |
Production build |
npm start |
Serve the production build |
npm run lint |
next lint |
npm run type-check |
tsc --noEmit |
npm test |
vitest run |
npm run shadcn:add |
Installs the full shadcn/ui component set (form, dialog, sheet, toast, table, tabs, tooltip, etc.) beyond the base components already in components/ui/ — run npm cache clean --force first if you hit a "Lock compromised"/npm cache error |
There is no test:e2e, test:a11y, test:coverage, Docker, or Vercel-CLI script in this repo at present — if you need those workflows, add them rather than assuming they exist.
- Runner: Vitest 4,
jsdomenvironment, configured invitest.config.ts/vitest.setup.ts. - Component testing:
@testing-library/react+@testing-library/user-event+@testing-library/dom+@testing-library/jest-dommatchers. - Tests live alongside the code they cover, in
__tests__/directories:app/__tests__,app/admin/invoices/__tests__,app/marketplace/__tests__,components/*/__tests__,hooks/__tests__,lib/__tests__,lib/validation/__tests__,context/__tests__. - Run everything with
npm test.
GitHub Actions workflows in .github/workflows/:
app-ci.yml/ci.yml— lint, type-check, test, build on PRs.pr-target-check.yml— enforces the branch-targeting rule below (PRs againstmainare closed automatically).
Full detail in CONTRIBUTING.md; the essentials:
devis the default/active branch.mainis production-only. Branch offdev, and targetdevin your PR — a PR opened againstmainis closed automatically.- Branch naming:
feature/…,fix/…,docs/…,test/…,refactor/…,chore/…. - Commit messages follow Conventional Commits (
feat(scope): …,fix(scope): …). - Before opening a PR, run locally (see DEVELOPMENT.md):
npm run lint,npx tsc --noEmit,npm run build,npm test. - PRs to
devneed CI green + 1 approval;dev→mainrelease PRs need 2 approvals (maintainer-driven; contributors don't need to manage releases).
No LICENSE file is currently present in this repository. Treat licensing as unspecified until the maintainers add one — don't assume MIT (or any other license) from this document alone.
Built on Stellar.
