Skip to content

Repository files navigation

Hermes replayer of Pyth price feed into Akash

Automatic price feed updater for Akash Network oracle contracts using Pyth Network's Hermes API.

πŸ“ Project Structure

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

πŸš€ Quick Start

1. Install Dependencies

npm install

2. Configure Environment

cp .env.example .env

Edit .env:

HC_RPC_ENDPOINT=https://rpc.akashnet.net:443
HC_CONTRACT_ADDRESS=akash1your_contract_address
HC_MNEMONIC="your twelve or twenty four word mnemonic"

3. Run

# 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:daemon

πŸ“‹ Available Commands

NPM Scripts

npm 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

CLI Usage

# 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

🐳 Docker Deployment

Option 1: Docker Compose (Recommended)

# 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

Option 2: Docker Build & Run

# 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

πŸ”„ Systemd Service (Linux Production)

# 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

πŸ“Š Monitoring

Check Logs

# Docker Compose
docker-compose logs -f

# Docker
docker logs -f hermes-client

# Systemd
sudo journalctl -u hermes-client -f

Query Contract

# Check current price
akash query wasm contract-state smart $HC_CONTRACT_ADDRESS '{"get_price":{}}'

# Check configuration
akash query wasm contract-state smart $HC_CONTRACT_ADDRESS '{"get_config":{}}'

βš™οΈ Configuration

Environment Variables

Variable Required Default Description
HC_RPC_ENDPOINT Yes - Akash RPC endpoint
HC_HERMES_API_KEY No - optional hermes api key. After 31 Jul 2026 it will become required
HC_CONTRACT_ADDRESS Yes - Oracle contract address
HC_WALLET_SECRET Yes - Either privateKey:<private key in hex format> or mnemonic:<12/24 words>
HC_HERMES_ENDPOINT No https://hermes.pyth.network Pyth Hermes API
HC_PRICE_DEVIATION_TOLERANCE No 0 absolute or percentage value for price deviations which should be ignored (e.g., 100 or 10%)
HC_PRICE_FETCHING_METHOD No polling polling or sse
HC_PRICE_UPDATE_TX_METHOD No ordered ordered or unordered
HC_UPDATE_INTERVAL_MS No 5000 Update interval (default 5 sec)
HC_GAS_PRICE No 0.025uakt Gas price
HC_DENOM No uakt Token denomination
HC_HEALTHCHECK_PORT No 3000 healthcheck server port
HC_SMART_CONTRACT_CONFIG_CACHE_TTL_MS No 3600000 (1h) smart contract config cache ttl in milliseconds
OTEL_RESOURCE_ATTRIBUTES No additional attributes attached to all metrics (e.g., service.name=hermes,service.version=1.1.0,deployment.environment=production)

Instrumentation

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 arguments
  • envDetector β€” resource attributes from the OTEL_RESOURCE_ATTRIBUTES environment variable. This variable allows to set extra labels (e.g., environment, service version.)
  • hostDetector β€” host information such as hostname
  • containerDetector β€” 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 daemon

Update Frequency

Change update interval in .env:

HC_UPDATE_INTERVAL_MS=300000   # 5 minutes (default)
HC_UPDATE_INTERVAL_MS=180000   # 3 minutes
HC_UPDATE_INTERVAL_MS=600000   # 10 minutes

πŸ’‘ How It Works

Workflow

1. 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

Smart Update Logic

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.

πŸ’° Cost Estimation

Per Update

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

Monthly Cost (5-minute updates)

Updates per day:    288
Updates per month:  8,640
Monthly cost:       8,640 Γ— 4,750 uakt = 41.04 AKT

Reduce Costs

Increase update interval to reduce costs:

HC_UPDATE_INTERVAL_MS=600000   # 10 min β†’ 50% cost savings
HC_UPDATE_INTERVAL_MS=900000   # 15 min β†’ 67% cost savings

πŸ”’ Security

Wallet Security

βœ… 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

Best Practices

# 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")

πŸ”§ Troubleshooting

Common Issues

"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 $HC_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

Debug Mode

# Enable verbose logging
export DEBUG=*
npm run cli:daemon

Test Connectivity

# Test RPC
curl $HC_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 $HC_CONTRACT_ADDRESS '{"get_config":{}}'

πŸ“š Documentation

For more detailed information:

πŸ†˜ Support

πŸ“„ License

MIT License


Ready to deploy! πŸš€

Start with: npm install && cp .env.example .env

About

No description, website, or topics provided.

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages