B2B stablecoin settlement infrastructure for fintechs
Settla is the settlement backbone that fintechs like Lemfi, Fincra, and Paystack plug into for cross-border payments. Each fintech gets its own tenant with negotiated fee schedules, isolated treasury positions, and dedicated rate limits. The platform routes payments through stablecoin rails (GBP → USDT → NGN) with smart provider selection, double-entry ledger tracking, and real-time treasury management.
Built for 50 million transactions/day — 580 TPS sustained, 5,000 TPS peak. The ledger sustains 25,000 writes/second using TigerBeetle, treasury reservations complete in under 1 microsecond via in-memory atomics, and the API gateway resolves tenant auth in 107 nanoseconds from local cache.
┌─────────────────────────────────────────────────────────────────┐
│ Settla Dashboard │
│ (Vue 3 + Nuxt ops console) │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────┴──────────────────────────────────┐
│ Settla API (Fastify Gateway) │
│ REST endpoints + Webhooks │
└──────┬──────────────┬───────────────┬──────────────┬────────────┘
│ gRPC/Proto │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────────┐
│ Settla │ │ Settla │ │ Settla │ │ Settla │
│ Core │ │ Ledger │ │ Rail │ │ Treasury │
│ (engine + │ │ (double- │ │ (router + │ │ (positions + │
│ state │ │ entry + │ │ providers +│ │ liquidity) │
│ machine) │ │ CQRS) │ │ blockchain)│ │ │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └───────┬────────┘
│ │ │ │
└──────────────┴──────┬───────┴─────────────────┘
│ NATS JetStream
┌──────┴──────┐
│ Settla │
│ Node │
│ (workers + │
│ sagas) │
└──────┬──────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Ledger │ │ Transfer │ │ Treasury │
│ DB │ │ DB │ │ DB │
└──────────┘ └──────────┘ └──────────┘
PostgreSQL (partitioned)
Settla ships as a single Go binary (settla-server) where each module (Core, Ledger, Rail, Treasury) communicates through interfaces defined in domain/interfaces.go, not through direct struct access. This gives us:
- Day-one velocity — one repo, one build, one deploy; no distributed system overhead
- Compile-time contracts — Go's type system enforces module boundaries; the engine depends on
domain.LedgerService, not onledger.Servicedirectly - Zero-cost extraction — every interface is a future gRPC seam. To extract Ledger: write a gRPC server hosting
ledger.Service, write a client implementingdomain.LedgerService, swap one line inmain.go - Honest complexity — we don't pay the microservices tax (service discovery, distributed tracing, network partitions) until a specific module actually needs independent scaling or deployment
The worker process (settla-node) is already a separate binary communicating via NATS events — it validates the extraction pattern from day one.
See ADR-001: Modular Monolith for extraction triggers and the full decision record.
| Module | Package | Responsibility |
|---|---|---|
| Settla Core | core |
Settlement engine, transfer lifecycle, state machine |
| Settla Ledger | ledger |
Immutable double-entry ledger, CQRS, balance snapshots |
| Settla Rail | rail |
Smart payment router, provider abstraction, blockchain clients |
| Settla Treasury | treasury |
Position tracking, liquidity management, rebalancing |
| Settla Node | node |
NATS JetStream workers, event-driven saga processing |
| Settla API | api/gateway, api/webhook |
REST gateway (Fastify), webhook dispatcher |
| Settla Dashboard | dashboard |
Vue 3 + Nuxt ops console |
- Go 1.22+
- Node.js 22+ with pnpm
- Docker and Docker Compose
- buf (protobuf toolchain)
- golangci-lint
- golang-migrate (database migrations)
# Start infrastructure (Postgres x3, NATS, Redis)
make docker-up
# Build Go binaries
make build
# Install TypeScript dependencies
pnpm install
# Run the gateway in dev mode
pnpm --filter @settla/gateway dev
# Run all Go tests
make test
# Run linter
make lintsettla/
├── core/ # Settlement engine + state machine
├── ledger/ # Double-entry ledger + CQRS
├── rail/ # Router, providers, blockchain
│ ├── router/
│ ├── provider/
│ └── blockchain/
├── treasury/ # Position tracking + liquidity
├── node/ # NATS workers + saga processing
│ ├── worker/
│ └── messaging/
├── domain/ # Shared domain types + interfaces
├── store/ # Database repositories (SQLC)
│ ├── ledgerdb/
│ ├── transferdb/
│ └── treasurydb/
├── api/ # TypeScript services
│ ├── gateway/ # Fastify REST API
│ └── webhook/ # Webhook dispatcher
├── dashboard/ # Vue 3 + Nuxt ops console
├── cmd/ # Go entrypoints
│ ├── settla-server/ # Core + Ledger + Rail + Treasury
│ └── settla-node/ # Worker process
├── proto/ # Protobuf definitions
├── db/ # Migrations + SQLC queries
├── deploy/ # Docker + compose
├── scripts/ # Dev tools + seed data
└── docs/ # Architecture docs + ADRs
| Metric | Sustained | Peak |
|---|---|---|
| Transactions/day | 50,000,000 | — |
| Transactions/sec | 580 TPS | 5,000 TPS |
| Ledger writes/sec | 2,300 | 25,000 |
| Treasury reservation | — | <1μs (in-memory) |
| Auth lookup | — | 107ns (local cache) |
| Gateway replicas | 4 | 8 |
| Server replicas | 6 | 12 |
See Capacity Planning for the full math and bottleneck analysis.
Run all 5 demo scenarios (uses in-memory stores, no infrastructure required):
make demoScenarios:
- GBP→NGN Corridor (Lemfi) — full pipeline: quote → fund → on-ramp → settle → off-ramp → complete
- NGN→GBP Corridor (Fincra) — reverse corridor with different fee schedule
- Tenant Isolation — proves Lemfi cannot see Fincra's transfers
- Burst Concurrency — 100 concurrent transfers, verifies no over-reservation
- Per-Tenant Fees — demonstrates negotiated fee schedules (Lemfi: 40/35 bps, Fincra: 25/20 bps)
- Decimal math only —
shopspring/decimal(Go) anddecimal.js(TS) for all monetary amounts - Balanced postings — every ledger entry must balance (debits = credits)
- Valid state transitions — transfers follow a strict state machine
- Idempotent mutations — every write operation uses idempotency keys
- UTC timestamps — all times stored and transmitted in UTC
- UUID identifiers — all entity IDs are UUIDs
- Tenant isolation — all data is tenant-scoped, no cross-tenant leakage
| ADR | Decision | Key Threshold |
|---|---|---|
| 001 | Modular Monolith | Team <10 engineers, extract when modules need independent scaling |
| 002 | TigerBeetle for Ledger Writes | >10K writes/sec breaks single Postgres |
| 003 | CQRS Dual-Backend Ledger | 250M entry_lines/day requires separated read/write paths |
| 004 | In-Memory Treasury Reservation | SELECT FOR UPDATE deadlocks >5% at peak on ~50 hot rows |
| 005 | NATS Partitioned Events | 580 events/sec with per-tenant ordering guarantee |
| 006 | Two-Level Cache (Local + Redis) | 5K auth lookups/sec; Redis-only adds 2.5s cumulative latency/sec |
| 007 | PgBouncer Connection Pooling | 900 connections vs Postgres limit of ~200 |
| 008 | Multi-Database Bounded Contexts | Cross-context JOINs bottleneck at 50M txn/day |
| 009 | gRPC Between TypeScript and Go | JSON overhead ~2ms at 5K TPS |
| 010 | Decimal-Only Monetary Math | float64 loses precision at >15 significant digits |
| 011 | Per-Tenant Fee Schedules | B2B platform with negotiated per-fintech rates |
| 012 | HMAC-SHA256 Webhook Signatures | Public webhook URLs require cryptographic verification |
| 013 | Monthly Table Partitioning | 1.5B rows/month degrades queries and VACUUM |