Skip to content

Heliobond/backend

Heliobond Backend

Node.js oracle server for the Heliobond platform. It simulates IoT sensor data for solar panel and satellite readings, computes impact scores from that data, and submits update_impact_score transactions to the Soroban ProjectRegistry contract on Stellar. An hourly cron job keeps on-chain scores current automatically; the same logic is exposed over REST for on-demand updates.


Architecture

flowchart TD
    subgraph Client
        A[Browser / External caller]
    end

    subgraph Express["Express (src/index.ts)"]
        H[GET /health]
        IOT[iot.ts\nGET /api/iot/solar/:id\nGET /api/iot/satellite/:id]
        ADMIN[admin.ts\nPOST /api/admin/update-scores\n— Bearer token required]
        CRON[node-cron\nhourly @ :00]
    end

    subgraph Lib["lib modules"]
        SC[scoring.ts\npure computation\ncomputeScores]
        ST[stellar.ts\nRPC client\nsignAndSubmit]
        REG[registry.ts\ncontract calls\nupdateImpactScore\ngetTotalProjects]
    end

    subgraph Stellar
        RPC[Stellar RPC\nsoroban-testnet.stellar.org]
        CONTRACT[Soroban\nProjectRegistry\ncontract]
    end

    A -->|HTTP| H
    A -->|HTTP| IOT
    A -->|HTTP + Bearer| ADMIN

    IOT -->|read-only sim| SC
    ADMIN --> SC
    ADMIN --> REG
    CRON --> SC
    CRON --> REG

    REG --> ST
    ST --> RPC
    RPC --> CONTRACT
Loading

Data flow for a score update (admin route or cron):

  1. getSolarData(id) and getSatelliteData(id) produce deterministic, hourly-seeded sensor readings.
  2. computeScores({ solar, satellite }) derives credit_quality and green_impact (pure, no I/O).
  3. updateImpactScore(id, cq, gi) in registry.ts builds and prepares a Soroban transaction.
  4. signAndSubmit(xdr, keypair) in stellar.ts signs, submits, and polls until the transaction is confirmed.

API Reference

Full request/response details, validation rules, and error codes are in API.md.

Method Path Auth Description
GET /health Liveness + uptime and last cron run
GET /api/iot/solar/:id Simulated solar panel reading for project id
GET /api/iot/satellite/:id Simulated satellite / vegetation reading for project id
GET /api/projects Paginated list of projects with scores (?limit=&cursor=)
GET /api/projects/:id Single project detail
GET /api/portfolio/:address Indexed deposit/withdraw history for an address
POST /api/admin/update-scores Bearer token Submit impact score update(s) to the Soroban contract

Errors return a consistent { "error": "<code>", "message": "<detail>" } JSON shape (never a stack trace). All /api/* routes are rate limited and return 429 with a Retry-After header once the limit is exceeded.

GET /health

{ "status": "ok" }

GET /api/iot/solar/:id

{
  "power_output_kw": 742.15,
  "efficiency_pct": 74.21,
  "max_power_kw": 1000,
  "timestamp": 1718150400000
}

Readings are deterministic per (project_id, hour) — the same id returns the same values within a given clock hour.

GET /api/iot/satellite/:id

{
  "forest_density_pct": 68.44,
  "ndvi_score": 0.684,
  "timestamp": 1718150400000
}

POST /api/admin/update-scores

Headers: Authorization: Bearer <ADMIN_API_KEY>

Body (optional):

{ "project_ids": [1, 2, 3] }

Omit project_ids (or send an empty array) to update every project registered on-chain (fetched via getTotalProjects()).

Response:

{
  "updated": 2,
  "results": [
    {
      "project_id": 1,
      "tx_hash": "abc123...",
      "credit_quality": 74,
      "green_impact": 69
    }
  ],
  "errors": []
}

Soroban does not support multi-call batching; transactions are submitted sequentially.


Score Formula

Both output values are integers in [0, 100].

credit_quality = clamp(efficiency_pct, 0, 100)

green_impact   = clamp(
                   (power_output_kw / max_power_kw) * 50
                 + (forest_density_pct / 100)        * 50,
                   0, 100
                 )

credit_quality reflects how efficiently the solar array is operating.
green_impact is a 50/50 blend of power production ratio and vegetation health.


Environment Variables

Create a .env file (see .env.example):

Variable Required Default Description
STELLAR_NETWORK No testnet testnet or mainnet — selects the network passphrase
ADMIN_SECRET_KEY Yes Stellar secret key (S...) used to sign transactions
PROJECT_REGISTRY_CONTRACT_ID Yes Soroban contract address for the ProjectRegistry
RPC_URL No https://soroban-testnet.stellar.org Stellar RPC endpoint
PORT No 3001 HTTP port the server listens on
FRONTEND_URL No http://localhost:3000 Origin allowed by CORS
ADMIN_API_KEY No Bearer token for /api/admin/*. If unset, auth is skipped (dev only)
RATE_LIMIT_WINDOW_MS No 60000 Public rate-limit window (ms)
RATE_LIMIT_MAX No 100 Public max requests per IP per window
RATE_LIMIT_ADMIN_WINDOW_MS No RATE_LIMIT_WINDOW_MS Admin rate-limit window (ms)
RATE_LIMIT_ADMIN_MAX No 20 Admin max requests per IP per window

Getting Started

Prerequisites: Bun (curl -fsSL https://bun.sh/install | bash).

# 1. Install dependencies
bun install

# 2. Configure environment
cp .env.example .env
# Edit .env — ADMIN_SECRET_KEY and PROJECT_REGISTRY_CONTRACT_ID are required to
# start the server; the rest have sensible defaults.

# 3. Development (ts-node + hourly cron + 5-min indexer)
bun run dev          # -> Heliobond backend listening on port 3001

# Verify it's up
curl http://localhost:3001/health

# Production
bun run build && bun start

# Quality gate
bun run build        # tsc type-check
bun run test         # jest suite

Tech Stack

Layer Technology
Runtime Node.js 20
Language TypeScript
HTTP framework Express 5
Stellar SDK @stellar/stellar-sdk v15
Scheduler node-cron v4
Package manager / test runner Bun
Test framework Jest + ts-jest + Supertest

About

Heliobond API backend — Stellar indexer and REST endpoints

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors