Automatic price feed updater for Akash Network oracle contracts using Pyth Network's Hermes API.
hermes-client/
├── src/
│ ├── hermes-client.ts # Main client implementation
│ └── cli.ts # CLI tool
├── package.json # Node.js dependencies
├── tsconfig.json # TypeScript configuration
├── .env.example # Environment variables template
├── Dockerfile # Docker image
├── docker-compose.yml # Docker Compose setup
├── hermes-client.service # Systemd service file
└── README.md # This file
npm installcp .env.example .envEdit .env:
RPC_ENDPOINT=https://rpc.akashnet.net:443
CONTRACT_ADDRESS=akash1your_contract_address
MNEMONIC="your twelve or twenty four word mnemonic"# Build TypeScript
npm run build
# Run one-time update
npm run cli:update
# Query current price
npm run cli:query
# Start daemon (continuous updates)
npm run cli:daemonnpm run build # Compile TypeScript to JavaScript
npm run start # Start the client daemon
npm run dev # Run in development mode
npm run cli # Run CLI tool
npm run cli:update # Update price once
npm run cli:query # Query current price
npm run cli:status # Show client status
npm run cli:daemon # Run continuous updates# After building
node dist/cli.js update # Update price once
node dist/cli.js query # Query current price
node dist/cli.js status # Show status
node dist/cli.js daemon # Run daemon# Configure .env first
cp .env.example .env
# Edit .env with your settings
# Start
docker-compose up -d
# View logs
docker-compose logs -f hermes-client
# Stop
docker-compose down# Build image
docker build -t akash-hermes-client .
# Run container
docker run -d \
--name hermes-client \
--env-file .env \
--restart unless-stopped \
akash-hermes-client
# View logs
docker logs -f hermes-client# 1. Build project
npm run build
# 2. Copy to /opt
sudo mkdir -p /opt/hermes-client
sudo cp -r dist package.json .env /opt/hermes-client/
cd /opt/hermes-client
sudo npm ci --production
# 3. Install systemd service
sudo cp hermes-client.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable hermes-client
sudo systemctl start hermes-client
# 4. Check status
sudo systemctl status hermes-client
sudo journalctl -u hermes-client -f# Docker Compose
docker-compose logs -f
# Docker
docker logs -f hermes-client
# Systemd
sudo journalctl -u hermes-client -f# Check current price
akash query wasm contract-state smart $CONTRACT_ADDRESS '{"get_price":{}}'
# Check configuration
akash query wasm contract-state smart $CONTRACT_ADDRESS '{"get_config":{}}'| Variable | Required | Default | Description |
|---|---|---|---|
RPC_ENDPOINT |
Yes | - | Akash RPC endpoint |
CONTRACT_ADDRESS |
Yes | - | Oracle contract address |
WALLET_SECRET |
Yes | - | Either privateKey:<private key in hex format> or mnemonic:<12/24 words> |
HERMES_ENDPOINT |
No | https://hermes.pyth.network |
Pyth Hermes API |
PRICE_DEVIATION_TOLERANCE |
No | 0 | absolute or percentage value for price deviations which should be ignored (e.g., 100 or 10%) |
UPDATE_INTERVAL_MS |
No | 300000 |
Update interval (5 min) |
GAS_PRICE |
No | 0.025uakt |
Gas price |
DENOM |
No | uakt |
Token denomination |
HEALTHCHECK_PORT |
No | 3000 | healthcheck server port |
OTEL_RESOURCE_ATTRIBUTES |
No | additional attributes attached to all metrics (e.g., service.name=hermes,service.version=1.1.0,deployment.environment=production) |
This service exposes a /health endpoint that can be polled periodically to check whether the service is healthy. It also exposes a /metrics endpoint for collecting service metrics in Prometheus format. The /metrics endpoint is only available when the service is run with instrumentation enabled.
Instrumentation is powered by OpenTelemetry and collects Node.js runtime metrics (e.g., event loop delay, GC, active handles) with a 5-second monitoring precision. The following resource detectors enrich metrics with contextual metadata:
processDetector— process-level attributes such as PID, executable name, and command-line argumentsenvDetector— resource attributes from theOTEL_RESOURCE_ATTRIBUTESenvironment variable. This variable allows to set extra labels (e.g., environment, service version.)hostDetector— host information such as hostnamecontainerDetector— container ID from the cgroup file when running inside a container (e.g., Docker)
To run the service with instrumentation enabled:
# in dev mode
node --experimental-strip-types --watch --env-file=.env --import ./src/instrumentation.ts src/cli.ts daemon
# compiled
node --env-file=.env --import ./dist/instrumentation.js dist/cli.js daemonChange update interval in .env:
UPDATE_INTERVAL_MS=300000 # 5 minutes (default)
UPDATE_INTERVAL_MS=180000 # 3 minutes
UPDATE_INTERVAL_MS=600000 # 10 minutes1. Initialize client
- Load configuration
- Create wallet from mnemonic
- Connect to Akash RPC
- Query contract for price_feed_id
↓
2. Fetch price from Pyth Hermes
GET https://hermes.pyth.network/v2/updates/price/latest?ids={price_feed_id}
↓
3. Query current price from contract
↓
4. Compare publish times
- If new data → Update contract
- If same/old → Skip update
↓
5. Wait for next interval
- Repeat from step 2
The client only submits transactions when:
- ✅ New price data is available (newer publish_time)
- ✅ Wallet has sufficient balance
- ✅ Price passes validation (non-zero, reasonable confidence)
This minimizes transaction costs and blockchain load.
Gas cost: 150,000 gas × 0.025 uakt/gas = 3,750 uakt
Update fee: 1,000 uakt (example, set in contract)
Total: ~4,750 uakt per update
Updates per day: 288
Updates per month: 8,640
Monthly cost: 8,640 × 4,750 uakt = 41.04 AKT
Increase update interval to reduce costs:
UPDATE_INTERVAL_MS=600000 # 10 min → 50% cost savings
UPDATE_INTERVAL_MS=900000 # 15 min → 67% cost savings✅ Use dedicated wallet - Create separate wallet for oracle updates only ✅ Limit funding - Only keep necessary AKT (monthly costs + buffer) ✅ Secure mnemonic - Use environment variables or secrets manager ✅ Never commit .env - Already in .gitignore ✅ Monitor activity - Set up alerts for unusual transactions
# DON'T: Store mnemonic in code
const mnemonic = "word1 word2 word3..."
# DO: Load from environment (not secure for production)
const mnemonic = process.env.MNEMONIC
# BETTER: Use secrets manager (production)
const mnemonic = await secretsManager.getSecret("hermes-mnemonic")"Client not initialized"
# Solution: Ensure initialize() is called
await client.initialize()"Insufficient funds"
# Check wallet balance
akash query bank balances <YOUR_ADDRESS>
# Fund wallet
akash tx bank send <FROM> <ORACLE_ADDRESS> 100000000uakt --gas auto"Failed to fetch from Hermes"
# Test Hermes API
curl "https://hermes.pyth.network/v2/updates/price/latest?ids=<PRICE_FEED_ID>"
# Check price feed ID
akash query wasm contract-state smart $CONTRACT_ADDRESS '{"get_price_feed_id":{}}'"Price already up to date"
- Not an error! Contract already has the latest price
- Client will try again on next interval
# Enable verbose logging
export DEBUG=*
npm run cli:daemon# Test RPC
curl $RPC_ENDPOINT/status
# Test Hermes
curl "https://hermes.pyth.network/api/latest_price_feeds?ids[]=<FEED_ID>"
# Test contract
akash query wasm contract-state smart $CONTRACT_ADDRESS '{"get_config":{}}'For more detailed information:
- Installation & Setup - See this README
- API Reference - See
src/hermes-client.tsJSDoc comments - CLI Reference - Run
npm run cli -- --help - Pyth Network - https://docs.pyth.network/
- Hermes API - https://hermes.pyth.network/docs/
- Issues: Open GitHub issue
- Akash Discord: https://discord.akash.network
- Pyth Discord: https://discord.gg/pythnetwork
MIT License
Ready to deploy! 🚀
Start with: npm install && cp .env.example .env