Powering Africa with affordable, pay-as-you-go solar energy on blockchain.
Stellar SolarGrid is a decentralized PAYG solar energy platform built on Soroban, within the Stellar ecosystem. Households and small businesses in underserved regions access solar electricity through flexible micro-payments — no large upfront costs required.
graph TD
subgraph IoT
Meter["IoT Smart Meter"]
end
subgraph Messaging
Broker["Mosquitto MQTT Broker"]
end
subgraph Backend Services
Bridge["IoT Bridge"]
Backend["Express Backend REST API"]
end
subgraph Blockchain
Contract["Soroban Smart Contract"]
end
subgraph Client
Frontend["React Frontend"]
end
subgraph External
Provider["Energy Provider"]
end
Meter -- "1. Sends usage payloads (MQTT)" --> Broker
Broker -- "2. Consumes usage payloads" --> Bridge
Bridge -- "3. Calls batch_update_usage" --> Contract
Contract -- "4. Emits meter:activated/deactivated events" --> Bridge
Bridge -- "5. Sends ON/OFF relay commands" --> Meter
Frontend -- "Calls contract directly" --> Contract
Frontend -- "Queries REST API" --> Backend
Backend -- "Fires webhook notifications" --> Provider
Users purchase energy access through flexible payment plans (daily, weekly, or usage-based stablecoin payments) via the React Frontend dashboard, which interacts directly with the Soroban smart contract. The contract verifies the payment, activates the user's meter, and tracks the remaining energy units or time validity.
Rather than updating every usage update individually, the IoT Bridge consumes MQTT payloads sent by active smart meters to the Mosquitto broker, aggregates them, and calls batch_update_usage on the Soroban smart contract in a single batch transaction. This saves gas and transaction fees on the Stellar network.
To prevent unauthorized usage reports or unauthorized meter controls, an Allowlist checks and verifies that only registered smart meters (registered via the admin CLI/dashboard) can be active on the system. Additionally, the IoT Bridge/Oracle address is allowlisted on the smart contract to restrict usage updates to trusted nodes.
For local development setup and contributing guidelines, please refer to the Contributing Guide.
- Smart Meter Integration — IoT meters with real-time usage monitoring and on/off control
- Flexible Payment Plans — Daily, weekly, or usage-based micro-payments in stablecoins
- Automated Access Control — Smart contracts enable/disable electricity based on payment status
- Energy Usage Tracking — Dashboards for users and providers
- Rust +
wasm32-unknown-unknowntarget - Stellar CLI
- Node.js >= 18
- Freighter Wallet (browser extension)
A Makefile is provided at the repository root to simplify common development and smart contract deployment/invocation commands:
- Build contracts:
make buildcompiles the contracts into WASM. - Run tests:
make testexecutes Cargo tests in the contracts folder. - Clean builds:
make cleanrunscargo cleaninside the contracts directory. - Deploy contract:
make deploy NETWORK=testnet ADMIN_SECRET_KEY=<key>deploys the built contract WASM to the specified network. - Invoke functions:
make invoke-register CONTRACT_ID=<id> ADMIN_SECRET_KEY=<key> METER_ID=<meter_id> OWNER=<owner>registers a new meter.make invoke-allowlist CONTRACT_ID=<id> ADMIN_SECRET_KEY=<key> OWNER=<owner>adds an owner to the allowlist.
- Backend Logs:
make logsstreams Docker Compose logs for the backend.
Contract deployments to the Stellar Testnet are automated via GitHub Actions:
- To trigger a deployment, push a git tag matching the pattern
contract-v*(e.g.,git tag contract-v1.0.0 && git push origin contract-v1.0.0). - The workflow compiles the contract, installs Stellar CLI, and deploys it to the testnet using the
ADMIN_SECRET_KEYsecret. - The new contract ID is printed as an output of the workflow.
You can pull and run the backend and frontend Docker images built automatically on pushes to main and release tags (v*):
docker pull ghcr.io/OWNER/solargrid-backend:latest
docker pull ghcr.io/OWNER/solargrid-frontend:latest(Replace OWNER with the GitHub organization or username).
You can spin up the local infrastructure (MQTT broker and the backend service) using Docker Compose:
- Copy the environment template at the repository root:
cp .env.example .env
- Populate the
.envfile with yourCONTRACT_ID,ADMIN_SECRET_KEY, andVITE_CONTRACT_ID. - Start the services:
docker compose up --build
The env-check service validates that all required environment variables are correctly populated before the backend starts up, preventing silent configuration errors.
The SolarGrid contract manages:
| Function | Description |
|---|---|
register_meter(meter_id, owner) |
Register a new smart meter |
make_payment(meter_id, amount, plan) |
Pay for energy access |
check_access(meter_id) |
Check if meter is currently active |
get_usage(meter_id) |
Retrieve usage data |
update_usage(meter_id, units) |
Called by IoT oracle to update consumption |
deactivate_meter(meter_id) |
Admin-only: immediately deactivate a meter |
GET /api/meters/:id/balance
Returns the live balance, usage, and active status for a single meter. Responses are cached for 5 seconds to reduce RPC load. The frontend UserDashboard polls this endpoint every 30 seconds.
Response
{
"meter_id": "METER1",
"balance": 5000000,
"units_used": 1200,
"active": true
}| Status | Description |
|---|---|
| 200 | Meter found, returns balance data |
| 404 | Meter not found |
The Meter struct carries a version: u32 field (currently 1). When the struct layout changes in a future release, existing persistent storage entries must be migrated before they can be read by the new code.
- Deploy the new contract WASM (the old entries remain in persistent storage).
- For each registered meter, call the admin-only
migrate_meter(meter_id)function.
It reads the entry as the previous schema (LegacyMeter) and writes it back as the currentMeterv1. - Once all entries are migrated, the
LegacyMetertype andmigrate_meter_v0helper can be removed in a subsequent release.
# Example: migrate a single meter via Stellar CLI
stellar contract invoke \
--id <CONTRACT_ID> \
--source <ADMIN_SECRET> \
--network testnet \
-- migrate_meter --meter_id METER1Note:
migrate_meteris idempotent per entry — calling it on an already-migrated meter will overwrite with the same data. Always test migrations on testnet before mainnet.
Deployed on Stellar Testnet. Switch to Mainnet for production.
- Never commit
.envfiles. Copy.env.exampleto.envand populate locally. ADMIN_SECRET_KEYis loaded once at backend startup into aKeypairobject; the raw secret string is not referenced anywhere after module initialisation.- All error handlers log only
err.message— raw error objects (which may contain XDR or serialised environment variables) are never logged. - Enable secret scanning in CI (e.g.
git-secrets, GitHub secret scanning) to prevent accidental key commits.
MIT