Human-readable Soroban contract explorer. Decodes raw XDR into plain English:
"Address GABC... swapped 100 USDC β 98.7 XLM on StellarSwap at ledger 4521983."
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.orgContracts live in the octraban_contract repo.
- Node.js + Express + TypeScript
- PostgreSQL + Prisma ORM
- Stellar SDK β Soroban RPC + XDR decoding
- Docker Compose β one-command setup
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
cp .env.example .env
docker compose upcp .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)| 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 |
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"
}]
}
}'| 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. |
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 2015This 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(defaulthttp://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)
- API service:
- octraban_contract β Soroban smart contracts (explorer, ticket) deployed to testnet.
- Start the indexer (
indexer/) β serves on:3001. - Start the API (root) β serves on
:3000. - Point the frontend at the indexer:
VITE_INDEXER_URL=http://localhost:3001. - Set
TESTNET_RPC_URL=https://soroban-testnet.stellar.orgfor chain access.
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.tsis a second, largely independent API β running it is optional for a working explorer UI, but the two processes share the same Postgres database.
Soroban RPC β indexer (indexer/src/index.js) β PostgreSQL β indexer API :3001 β frontend
β
src/index.ts (API :3000, optional, same DB)
# 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)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:
- indexerThis 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.
# 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 upNote for frontend contributors: Set
VITE_INDEXER_URL=http://localhost:3001when running the frontend outside Docker. Inside a shared Compose network, usehttp://indexer:3001.