Skip to content

Architecture ​

 ┌────────────────────┐        HTTPS / WebSocket        ┌───────────────────────────┐
 │  React client       │ ─────────────────────────────▶ │  Express API + Socket.io   │
 │  (Vite, Zustand)    │ ◀───────────────────────────── │  (Node.js)                 │
 └────────────────────┘                                 │                             │
                                                        │  routes → services → lib    │
                                                        │  jobs (scheduler)           │
                                                        └──────┬─────────┬────────────┘
                                                               │         │
                                                  Prisma       │         │  @stellar/stellar-sdk
                                                               ▼         ▼
                                                     ┌──────────────┐  ┌──────────────────┐
                                                     │ PostgreSQL   │  │ Horizon (Stellar)│
                                                     └──────────────┘  └──────────────────┘
                                                               │
                                     Cloudinary · SMTP · Smile ID (optional integrations)

Layers in the API ​

LayerDirectoryResponsibility
Routessrc/routesHTTP wiring, middleware, response shaping
Middlewaresrc/middlewareAuth, zod validation, rate limits, uploads, errors
Servicessrc/servicesBusiness logic and database access
Rulessrc/services/tradeRules.jsPure trade state rules, unit-tested
Stellarsrc/stellarHorizon client, wallets, payments, escrow
Libsrc/libAmount math, crypto, errors, pagination, logging
Socketsrc/socketSocket.io auth, rooms and emit helpers
Jobssrc/jobsOrder expiry, auto-refunds, reconciliation

Routes stay thin: they validate input, call a service, and return JSON. Services throw AppErrors which the error middleware turns into consistent responses.

Key design decisions ​

  • Custodial wallets in Phase 1 — keys are generated per user and encrypted at rest, so trades settle without the user signing.
  • One escrow account per trade — isolates funds and makes every trade independently verifiable on-chain. See escrow design.
  • Database is the source of truth for trade state; the ledger is the source of truth for funds — transitional states and reconciliation bridge the two.
  • Exact decimal math — XLM and NGN are handled as BigInt minor units. See amounts.
  • Naira never touches the platform — payment confirmation is manual by the seller.

Nexlm is a peer-to-peer facilitator and never holds customer Naira.