Architecture Overview
Nester follows a layered architecture separating concerns across clients, backend services, and the blockchain.
System Diagram
┌─────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ │
│ Website (Next.js) DApp (Next.js 16) Mobile (RN) │
│ Marketing/Landing Stellar Wallets iOS/Android │
│ │ (future) │
└──────────────────────────────┼──────────────────────────────┘
│ REST API
┌──────────────────────────────┼──────────────────────────────┐
│ BACKEND LAYER │
│ │ │
│ ┌───────────────────────────▼───────────────────────────┐ │
│ │ API Gateway (Go + Chi Router) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ Vault │ │ Portfolio │ │ User / Auth │ │ │
│ │ │ Manager │ │ Service │ │ Service │ │ │
│ │ └─────────────┘ └──────────────┘ └───────────────┘ │ │
│ │ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ Yield │ │ Auto-Invest │ │ Event │ │ │
│ │ │ Router │ │ Scheduler │ │ Listener │ │ │
│ │ └─────────────┘ └──────────────┘ └───────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Rebalancer │ │ │
│ │ │ Service │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼───────────────────────────┐ │
│ │ PostgreSQL (Supabase) │ │
│ │ users │ vaults │ positions │ shares │ yield_snapshots │ │
│ │ transactions │ events │ auto_invest_mandates │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────┼──────────────────────────────┐
│ BLOCKCHAIN LAYER (Stellar / Soroban) │
│ │ │
│ ┌───────────────────────────▼───────────────────────────┐ │
│ │ Smart Contracts (Rust / Soroban SDK) │ │
│ │ │ │
│ │ VaultFactory Vault Contracts │ │
│ │ Deploy/manage deposit / withdraw / position │ │
│ │ vault instances nVault share token mint/burn │ │
│ │ maturity & penalty logic │ │
│ │ Auto-Invest Mandates Yield Source Adapters │ │
│ │ recurring deposit Blend / Kamino / Aave │ │
│ │ authorization deposit / withdraw / balanceOf │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Stellar Network: Horizon API / Soroban RPC │
└─────────────────────────────────────────────────────────────┘Data Flow: Deposit
User clicks "Deposit 1000 USDC into Balanced Vault"
│
▼
┌─────────────────┐ ┌──────────────────┐
│ Frontend │────▶│ Go Backend │
│ Build Soroban TX │ │ POST /deposit │
└────────┬────────┘ │ Validate + build │
│ │ TX envelope │
▼ └────────┬─────────┘
┌─────────────────┐ │
│ Wallet Extension │◀────────────┘
│ Sign TX │ TX envelope returned
└────────┬────────┘
│ Signed TX
▼
┌─────────────────┐ ┌──────────────────┐
│ Stellar Network │────▶│ Event Listener │
│ Execute TX │ │ Index deposit │
│ Vault.deposit() │ │ Update positions │
└─────────────────┘ └──────────────────┘Data Flow: Auto-Invest
User authorizes "Deposit 200 USDC into Balanced Vault weekly"
│
▼
┌──────────────┐ ┌────────────────┐ ┌─────────────┐
│ Frontend │───▶│ Go Backend │───▶│ Scheduler │
│ POST │ │ Validate + │ │ Registers │
│ /mandates │ │ store mandate │ │ next run │
└──────────────┘ └───────┬────────┘ └──────┬──────┘
│ │ every cycle
▼ ▼
┌────────────────┐ ┌─────────────┐
│ Soroban │───▶│ Vault │
│ Mandate │ │ deposit() │
│ authorization │ │ shares mint │
└────────────────┘ └──────┬──────┘
│
┌────────────────┐ │
│ Event Listener │◀──────────┘
│ Index deposit │
│ Update position│
└────────────────┘Project Structure
bash
nester/
├── apps/
│ ├── website/ # Marketing site (Next.js + pnpm)
│ ├── dapp/
│ │ ├── frontend/ # DApp (Next.js 16 + npm)
│ │ └── contracts/ # Soroban smart contracts (Rust)
│ └── api/ # API Gateway (Go + Chi + PostgreSQL)
├── packages/
│ └── contracts/ # Shared contract types/ABIs
└── mobile/ # React Native app (future)Database Schema
sql
-- Core tables (PostgreSQL / Supabase)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
wallet_addr TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE vaults (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL, -- "Conservative", "Balanced", etc.
vault_type TEXT NOT NULL, -- conservative | balanced | growth
contract_id TEXT NOT NULL, -- Soroban contract address
total_shares NUMERIC DEFAULT 0,
total_assets NUMERIC DEFAULT 0,
apy_current NUMERIC DEFAULT 0,
status TEXT DEFAULT 'active', -- active | paused
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE positions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
vault_id UUID REFERENCES vaults(id),
shares NUMERIC NOT NULL,
deposited_at TIMESTAMPTZ DEFAULT now(),
maturity_at TIMESTAMPTZ,
UNIQUE(user_id, vault_id)
);
CREATE TABLE yield_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vault_id UUID REFERENCES vaults(id),
apy NUMERIC NOT NULL,
tvl NUMERIC NOT NULL,
recorded_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE auto_invest_mandates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
vault_id UUID REFERENCES vaults(id),
amount_usd NUMERIC NOT NULL,
cadence TEXT NOT NULL, -- daily | weekly | monthly
status TEXT DEFAULT 'active', -- active | paused | cancelled
next_run_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);