Skip to content
Β 
Β 

Latest commit

Β 

History

882 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Octraban β€” Backend

Human-readable Soroban contract explorer. Decodes raw XDR into plain English:

"Address GABC... swapped 100 USDC β†’ 98.7 XLM on StellarSwap at ledger 4521983."

🟒 Live on Testnet

The Octraban Soroban contracts this backend indexes are deployed and verifiable on the Stellar test network:

Contract Contract ID Stellar Explorer
Explorer / registry CBKPNRQ4D3KTAAE7MMJ4HL6JNF2J2EBG2PSSRW4YHOMHTRHUU734CFWJ View β†—
Ticket CDX3V6OE72KUIEEJTBLFCQZFXZCAKOYWYXK2KPRM57M6FLZFAVUSVL42 View β†—

Network: Test SDF Network ; September 2015 Β· RPC: https://soroban-testnet.stellar.org Contracts live in the octraban_contract repo.

Stack

  • Node.js + Express + TypeScript
  • PostgreSQL + Prisma ORM
  • Stellar SDK β€” Soroban RPC + XDR decoding
  • Docker Compose β€” one-command setup

Architecture

src/
β”œβ”€β”€ index.ts              # Express app entry
β”œβ”€β”€ config.ts             # Env config
β”œβ”€β”€ db.ts                 # Prisma client
β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ router.ts         # Route aggregator
β”‚   β”œβ”€β”€ transactions.ts   # GET /transactions
β”‚   β”œβ”€β”€ events.ts         # GET /events
β”‚   β”œβ”€β”€ contracts.ts      # GET/POST /contracts (ABI registry)
β”‚   β”œβ”€β”€ wallets.ts        # GET /wallets/:address
β”‚   └── tokens.ts         # GET /tokens (SEP-41)
└── indexer/
    β”œβ”€β”€ rpc.ts            # Stellar RPC client
    β”œβ”€β”€ registry.ts       # ABI registry + SEP-41 built-in ABI
    β”œβ”€β”€ decoder.ts        # XDR β†’ human-readable decoder
    β”œβ”€β”€ indexer.ts        # Ledger polling loop
    └── run.ts            # Indexer entry point

Quick Start

With Docker (recommended)

cp .env.example .env
docker compose up

Local development

cp .env.example .env
# edit .env with your DB URL and RPC endpoint

npm install
npx prisma migrate dev
npm run seed          # seed known contracts (StellarSwap etc.)
npm run dev           # start API server
npm run index         # start indexer (separate terminal)

API Endpoints

Method Path Description
GET /api/v1/transactions List transactions (filter: contract, account, status)
GET /api/v1/transactions/:hash Transaction detail + events
GET /api/v1/events List events (filter: contract, type)
GET /api/v1/events/:id Event detail
GET /api/v1/contracts List registered contracts
GET /api/v1/contracts/:address Contract detail + recent txs/events
POST /api/v1/contracts Register contract ABI metadata
GET /api/v1/wallets/:address/transactions Wallet transaction history
GET /api/v1/wallets/:address/events Wallet event history
GET /api/v1/tokens List SEP-41 tokens
GET /api/v1/tokens/:address Token detail
GET /api/v1/tokens/:address/transfers Token transfer history
GET /health Health check

Registering a Contract ABI

curl -X POST http://localhost:3000/api/v1/contracts \
  -H "Content-Type: application/json" \
  -d '{
    "address": "CXXX...",
    "name": "MyDEX",
    "abi": {
      "functions": [{
        "name": "swap",
        "inputs": [
          { "name": "from", "type": "address" },
          { "name": "amount_in", "type": "i128" },
          { "name": "amount_out", "type": "i128" }
        ],
        "humanTemplate": "{from} swapped {amount_in} β†’ {amount_out} on MyDEX"
      }]
    }
  }'

JWT Authentication & Key Management

The Octraban backend requires an RS256 key pair to sign and verify JWT tokens securely.

Key Generation

In production, you must supply your own keys. Generate them using OpenSSL:

# Generate a 2048-bit RSA private key
openssl genrsa -out private.pem 2048

# Extract the public key
openssl rsa -in private.pem -pubout -out public.pem

Set these in your environment variables JWT_PRIVATE_KEY and JWT_PUBLIC_KEY. Note that multi-line PEM strings must have their newlines replaced with literal \n characters in some environments (like .env files).

JWT Key ID and Rotation

The JWT_KEY_ID uniquely identifies the active key. This supports zero-downtime key rotation. Because the server caches keys, a typical rotation involves:

  1. Generating a new key pair and assigning it a new JWT_KEY_ID (e.g., key_2).
  2. Updating the environment variables to use the new keys.
  3. Restarting the service. New tokens are signed with key_2.

Note: Never log or commit the JWT_PRIVATE_KEY to version control. The server performs a startup validation check and will crash if the keys are missing or malformed when NODE_ENV=production.

Environment Variables

Variable Default Description
DATABASE_URL β€” PostgreSQL connection string
STELLAR_NETWORK testnet testnet or mainnet
STELLAR_RPC_URL testnet RPC Soroban RPC endpoint
HORIZON_URL testnet Horizon Horizon API endpoint
NETWORK_PASSPHRASE testnet Network passphrase
INDEXER_START_LEDGER 0 Ledger to start indexing from
INDEXER_POLL_INTERVAL_MS 5000 Polling interval
INDEXER_BATCH_SIZE 100 Ledgers per batch
ADMIN_SECRET β€” Bearer token required by every /api/admin/* route (indexer). See docs/ADMIN_AUTH.md.

Mainnet Config

STELLAR_NETWORK=mainnet
STELLAR_RPC_URL=https://mainnet.stellar.validationcloud.io/v1/<API_KEY>
HORIZON_URL=https://horizon.stellar.org
NETWORK_PASSPHRASE=Public Global Stellar Network ; September 2015

Octraban service topology

This repository is the backend tier of the Octraban stack, split across three repos:

  • octraban_frontend β€” Vite/React explorer UI. Reads from the indexer via VITE_INDEXER_URL (default http://localhost:3001).
  • octraban_backend (this repo) β€” the API service plus the indexer (under indexer/) that ingests on-chain data.
    • API service: PORT=3000
    • Indexer API: PORT=3001 (what the frontend queries)
  • octraban_contract β€” Soroban smart contracts (explorer, ticket) deployed to testnet.

Local wiring

  1. Start the indexer (indexer/) β†’ serves on :3001.
  2. Start the API (root) β†’ serves on :3000.
  3. Point the frontend at the indexer: VITE_INDEXER_URL=http://localhost:3001.
  4. Set TESTNET_RPC_URL=https://soroban-testnet.stellar.org for chain access.

Two-service architecture (API on :3000 vs. indexer on :3001)

octraban_backend ships two independent, separately-runnable Node processes. They are not interchangeable β€” new contributors should read this section before deciding what to run.

API service Indexer service
Entrypoint src/index.ts (httpServer.listen(config.port, ...)) indexer/src/index.js β†’ indexer/src/api.js (server.listen(PORT, ...))
Run with npm run dev / npm start (root) cd indexer && npm run dev / npm start
Port 3000 (PORT env, root .env) 3001 (PORT env, indexer/.env)
Responsibility Registers contract ABIs, serves GraphQL/billing/WebSocket features, and runs an in-process copy of the TS indexer (src/indexer/) that also writes to the same Postgres DB. Polls Soroban RPC for ledgers/events, decodes them, persists them to Postgres, and serves the REST/GraphQL API that the frontend actually calls.
Depends on PostgreSQL (Prisma, DATABASE_URL), optional Redis (rate-limit store), Soroban RPC PostgreSQL (pg, DATABASE_URL), optional Redis (REDIS_URL, caching/pub-sub), Soroban RPC (SOROBAN_RPC_URL)

The frontend (octraban_frontend) queries the indexer service on :3001, not the API service on :3000. src/index.ts is a second, largely independent API β€” running it is optional for a working explorer UI, but the two processes share the same Postgres database.

The indexer is still plain JavaScript, unlike the root API's TypeScript. It's being brought under type checking incrementally (npm run typecheck in indexer/, wired into CI) β€” see Indexer Type Checking in CONTRIBUTING.md for the approach and which modules are covered so far.

Both processes migrate the same database with two independent migration systems (Prisma for the API, a small SQL runner for the indexer). See docs/database-ownership.md for which tables each one owns and for npm run db:bringup, the single command that brings a fresh database to a working state for both.

Data flow

Soroban RPC  β†’  indexer (indexer/src/index.js)  β†’  PostgreSQL  β†’  indexer API :3001  β†’  frontend
                                                         ↑
                                        src/index.ts (API :3000, optional, same DB)

Running both locally

# 1. Indexer service β€” what the frontend needs (:3001)
cd indexer
cp .env.example .env   # set DATABASE_URL, SOROBAN_RPC_URL
npm install
npm run dev             # or: npm start

# 2. API service β€” optional, ABI registry / GraphQL / billing (:3000)
cd ..
cp .env.example .env    # set DATABASE_URL, TESTNET_RPC_URL, etc.
npm install
npx prisma migrate dev
npm run dev              # start API server
npm run index             # start the in-process TS indexer poller (separate terminal)

Docker Compose β€” service naming and the indexer alias (issue #18)

The octraban_frontend nginx configuration proxies all /api/ traffic to a Docker host named indexer on port 3001:

location /api/ { proxy_pass http://indexer:3001; }

This repository's docker-compose.yml defines per-network services named indexer-testnet, indexer-mainnet, and indexer-devnet. To make the frontend's proxy target resolve correctly without renaming the services, each indexer service declares a Docker network alias of indexer on the default Compose network:

indexer-testnet:
  networks:
    default:
      aliases:
        - indexer

This means a frontend container joined to the same Compose network can always reach the active indexer at http://indexer:3001, regardless of which network profile is active.

Starting the full stack for local development (testnet β€” the default)

# Copy and configure environment variables
cp .env.example .env
# Set at minimum: POSTGRES_PASSWORD

# Start the testnet stack (db + api + indexer)
docker compose up

# The indexer is now reachable as both:
#   http://indexer-testnet:3001   (service name)
#   http://indexer:3001            (alias β€” what the frontend uses)

For mainnet or devnet, activate the corresponding profile:

docker compose --profile mainnet up
docker compose --profile devnet   up

Note for frontend contributors: Set VITE_INDEXER_URL=http://localhost:3001 when running the frontend outside Docker. Inside a shared Compose network, use http://indexer:3001.

About

Human-readable Soroban contract explorer. Decodes raw XDR into plain English

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages