Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

captainyugi00/Solana-Balance-API

Repository files navigation

Solana Balance API – Trial Project

Golang REST API that fetches Solana balances, Docker + Mongo + tests.


1 · Challenge Recap

Item Spec
Endpoint POST /api/get-balance
Body { "wallets": [ARRAY_OF_SOLANA_PUBKEYS] }
External RPC https://helius-rpc.com/?api-key=…
Must • Mongo‑backed API‑key auth
    • IP‑rate‑limit > 10 req/min  
    • Per‑wallet cache (TTL 10 s)  
    • Mutex: 2 concurrent hits → 1 RPC call  
    • Respond “as fast as possible” |

2 · Final Repository Layout

├── go.mod
├── Dockerfile
├── docker-compose.yml
├── main.go               # starts Gin HTTP server
├── internal/             # cohesive packages
│   ├── cache.go          # TTL cache + per‑wallet mutex
│   ├── limiter.go        # IP rate‑limiting middleware
│   ├── mongo.go          # API‑key middleware (Mongo)
│   ├── solana.go         # RPC helper using gagliardetto/solana-go
│   └── handler.go        # POST /api/get-balance logic
└── solana_api_tests/     # complete Python functional test‑suite

3 · Build & Run with Docker

3.1 Dockerfile (multi‑stage)

# ---------- build stage ----------
FROM golang:1.24.4 AS builder

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/solana-api ./main.go

# ---------- runtime stage ----------
FROM gcr.io/distroless/base-debian12
COPY --from=builder /bin/solana-api /solana-api

EXPOSE 8080
ENTRYPOINT ["/solana-api"]

3.2 docker‑compose.yml

services:
  mongo:
    image: mongo:7
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secret
    volumes:
      - mongo-data:/data/db
      - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
    ports:
      - "27017:27017"

  api:
    build: .
    depends_on:
      - mongo
    environment:
      SOLANA_RPC: https://helius-rpc.com/?api-key=
      MONGO_URI: mongodb://admin:secret@mongo:27017/infra?authSource=admin
      MONGO_DB: infra
      PORT: 8080
    ports:
      - "8080:8080"
    restart: unless-stopped

volumes:
  mongo-data:

3.3 Seed a demo API‑key

mongo-init.js automatically inserts:

db = db.getSiblingDB('infra');
db.apikeys.insertOne({ api_key: 'demo-key-123', active: true });

3.4 Up & running

docker compose up --build -d
# API → http://localhost:8080
# Mongo → mongodb://admin:secret@localhost:27017

4 · REST API Usage

curl -XPOST http://localhost:8080/api/get-balance \
     -H 'Content-Type: application/json' \
     -H 'X-API-Key: demo-key-123' \
     -d '{"wallets":["9yg3qP8XqWBzhiyh9frehBmpbZ1S9X4yJ3YwCRxN5ZMx"]}'

Response (lamports):

{
  "balances": {
    "9yg3qP8…": 763982739
  }
}

Convert lamports ÷ 1 000 000 000 for SOL.


5 · Functional Test‑Suite (Python 3.10+)

Path: solana_api_tests/

cd solana_api_tests
pip install -r requirements.txt

# short run (skips 60‑s rate‑limit check)
python run_tests.py --base http://localhost:8080 --key demo-key-123 --skip-rate

# full run (≈80 s, includes rate‑limit validation)
python run_tests.py --base http://localhost:8080 --key demo-key-123

5.1 What the tests cover

  1. Auth – bad key ⇒ 401, good key ⇒ 200
  2. Single wallet – simple happy path
  3. Multiple wallets – parallel fetch
  4. Cache TTL 10 s – miss → hit → expire → miss pattern
  5. Mutex collapse – adapts to limiter burst (1 … 10) and still proves only the first request hits RPC
  6. Rate‑limit – adapts to burst and confirms 11ᵗʰ request within 60 s ⇒ 429

All steps log ISO‑timestamps.


6 · Key Implementation Details

Concern Implementation
Rate‑limiting limiter.go uses golang.org/x/time/rate.
NewLimiterCache(max=10, per=1 min) so refill = 6 s.
Burst defaults to max but can be set to 1 for stricter ops.
Caching + mutex cache.go holds map[string]*cacheEntry and a per‑wallet sync.Mutex in a sync.Map ensuring only one goroutine per wallet performs the RPC. TTL = 10 s.
Mongo auth Middleware checks X-API-Key against apikeys collection (active:true).
RPC Thin wrapper around gagliardetto/solana-go/rpc’s GetBalance.

7 · Known Limits & Tunables

Parameter Default How to change
Rate‑limit window 10 req / 1 min / IP NewLimiterCache(max, per) in main.go
Burst size = max (10) change 2ᵈ arg in rate.NewLimiter
Cache TTL 10 s NewWalletCache(…)
RPC timeout 4 s context.WithTimeout in solana.go

About

A REST API that fetches balance from multiple wallets

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors