A TypeScript background service for data feeding and processing.
- 🚀 TypeScript-based background service
- 📦 Modular service architecture
- 🔧 Configurable via environment variables
- 📊 Built-in logging and health monitoring
- 🛡️ Graceful shutdown handling
- 🔄 Service management system
- 🌐 Network configuration management
- 📡 Multi-network monitoring and health checks
- 💰 Price feeder system with multiple sources
- 🔗 Price oracle integration
- 📈 Real-time price monitoring and posting
- 🔔 Optional Discord webhook alerts on feeder failures
- 💸 Optional Discord alerts when signer balance is low on Algorand and Voi
gooch-feeder/
├── config/
│ ├── networks.json # Network configurations
│ ├── networks/ # Detailed network configs
│ │ └── voi-mainnet.json
│ └── feeders.json # Price feeder configurations
├── src/
│ ├── config/ # Configuration files
│ ├── services/ # Service implementations
│ │ ├── feeder-manager-service.ts
│ │ ├── price-lookup-service.ts
│ │ ├── price-oracle-service.ts
│ │ ├── network-monitoring-service.ts
│ │ └── price-feed-service.ts
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ └── index.ts # Main entry point
├── dist/ # Compiled JavaScript output
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── .eslintrc.json # ESLint configuration
└── README.md # This file
- Node.js (v16 or higher)
- npm or yarn
- Clone the repository:
git clone <repository-url>
cd gooch-feeder- Install dependencies:
npm install- Copy environment configuration:
cp env.example .env- Configure network settings:
# Edit the network configuration file
nano config/networks.json- Edit
.envfile with your configuration:
# Environment variables
NODE_ENV=development
PORT=3000
LOG_LEVEL=info
# Database configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=gooch_feeder
DB_USERNAME=your_username
DB_PASSWORD=your_password
# API configuration
API_BASE_URL=https://api.example.com
API_TIMEOUT=30000
API_RETRIES=3Run the service in development mode:
npm run devBuild the project:
npm run buildRun the compiled service:
npm startnpm run dev- Run in development mode with ts-nodenpm run build- Compile TypeScript to JavaScriptnpm start- Run the compiled servicenpm run watch- Watch for changes and recompilenpm run clean- Remove dist directorynpm run lint- Run ESLintnpm run lint:fix- Fix ESLint issues
npm run service:start- Start service in backgroundnpm run service:stop- Stop background servicenpm run service:restart- Restart background servicenpm run service:status- Check service statusnpm run service:logs- View service logs
npm run pm2:start- Start with PM2 process managernpm run pm2:stop- Stop PM2 processnpm run pm2:restart- Restart PM2 processnpm run pm2:delete- Remove PM2 processnpm run pm2:logs- View PM2 logsnpm run pm2:status- Check PM2 status
The service uses a modular architecture with the following components:
Manages the lifecycle of all services, including initialization, health checks, and graceful shutdown.
Provides structured logging with timestamps and service identification.
Centralized configuration management using environment variables.
To add a new service, implement the Service interface:
import { Service } from './types';
export class MyService implements Service {
name = 'MyService';
async initialize(): Promise<void> {
// Initialize your service
}
async shutdown(): Promise<void> {
// Cleanup resources
}
async isHealthy(): Promise<boolean> {
// Return health status
return true;
}
}Then register it with the service manager in src/index.ts.
The service supports two types of network configurations:
Contains basic network settings:
- Network definitions: RPC URLs, WebSocket URLs, chain IDs, explorer URLs
- Default network: Primary network to use
- Fallback networks: Backup networks if primary fails
- Global settings: Request limits, timeouts, health check intervals
Contains comprehensive network-specific data:
- Token configurations: Contract addresses, decimals, symbols
- Contract addresses: Price oracle, lending pools, market controller
- Asset prices: Current prices with timestamps
- PreFi parameters: Collateral factors, liquidation thresholds
- Contract state: Global state and metadata
{
"networks": {
"voiMainnet": {
"name": "VOI Mainnet",
"chainId": 0,
"rpcUrl": "https://mainnet-api.voi.nodely.dev",
"wsUrl": "wss://mainnet-api.voi.nodely.dev",
"explorerUrl": "https://voi.observer",
"enabled": true,
"timeout": 30000,
"retries": 3
}
},
"defaultNetwork": "voiMainnet",
"fallbackNetworks": ["mainnet", "polygon"],
"globalSettings": {
"maxConcurrentRequests": 10,
"requestDelay": 1000,
"healthCheckInterval": 60000,
"circuitBreakerThreshold": 5,
"circuitBreakerTimeout": 300000
},
"detailedNetworksDir": "config/networks"
}{
"metadata": {
"network": "voi-mainnet",
"contractId": "46826662",
"isDeployed": true
},
"networkConfig": {
"networkId": "voi-mainnet",
"name": "VOI Mainnet",
"rpcUrl": "https://mainnet-api.voi.nodely.dev",
"contracts": {
"priceOracle": "46826662",
"lendingPools": ["46505156"]
},
"tokens": {
"VOI": {
"contractId": "46504436",
"decimals": 6,
"symbol": "VOI"
}
}
},
"assetPrices": {
"voi": {
"symbol": "VOI",
"price": 0.5,
"lastUpdated": "2025-10-22T01:23:58.000Z"
}
}
}The service automatically monitors network health and tracks:
- Response times
- Error counts
- Last check timestamps
- Circuit breaker status
The service includes a comprehensive price feeder system that can fetch prices from multiple sources and post them to price oracles.
Each feeder is configured with:
- ID: Format
network-poolid-marketid(e.g.,voi-mainnet-46505156-VOI) - Method:
fetch,post, orfetch-and-post - Source: API, RPC, WebSocket, or smart contract
- Destination: Price oracle contract, database, or API
- Validation: Price limits, change thresholds, required fields
- Fallback: Alternative sources if primary fails
{
"feeders": {
"voi-mainnet-46505156-VOI": {
"id": "voi-mainnet-46505156-VOI",
"method": "fetch-and-post",
"networkId": "voi-mainnet",
"poolId": "46505156",
"marketId": "VOI",
"assetSymbol": "VOI",
"enabled": true,
"interval": 30000,
"timeout": 10000,
"retries": 3,
"priority": 10,
"source": {
"type": "api",
"url": "https://api.coingecko.com/api/v3/simple/price",
"params": {
"ids": "voi-network",
"vs_currencies": "usd"
}
},
"destination": {
"type": "price-oracle",
"contractAddress": "46826662",
"functionName": "updatePrice"
},
"validation": {
"minPrice": 0.01,
"maxPrice": 1000,
"maxPriceChange": 50
}
}
}
}- VOI Rewards API: Real-time VOI market data from multiple exchanges
- CoinGecko API: Real-time cryptocurrency prices
- Binance API: Exchange prices
- RPC Calls: Direct blockchain queries
- WebSocket: Real-time price streams
- Smart Contracts: On-chain price feeds
The service integrates with the VOI Rewards API to fetch real-time VOI prices from multiple exchanges:
- Nomadex: Primary VOI network DEX
- Tinyman: Algorand DEX
- PactFi: Algorand DEX
- Uniswap: Base network
- Humble: VOI network DEX
The system uses the weighted average price from the aggregates data, which provides a more stable and representative price across all exchanges. If the weighted average is unavailable, it falls back to selecting the best price based on trading volume and exchange priority.
- Request Throttling: 2-second minimum interval between API requests
- Staggered Feeder Starts: 5-second delays between feeder initialization
- Exponential Backoff: Smart retry logic with jitter for rate limit errors
- Rate Limit Detection: Automatic detection of 429 errors and appropriate backoff
- Fallback Sources: Multiple price sources for redundancy
- Configurable Intervals: 60-second primary intervals, 120-second fallback intervals
The service is designed to run continuously as a background process. Here are several ways to keep it running:
npm run devThis runs the service in the foreground for development. Press Ctrl+C to stop.
# Start in background
npm run service:start
# Check status
npm run service:status
# View logs
npm run service:logs
# Stop service
npm run service:stopFirst install PM2 globally:
npm install -g pm2Then use PM2 commands:
# Start with PM2
npm run pm2:start
# Check status
npm run pm2:status
# View logs
npm run pm2:logs
# Stop
npm run pm2:stopCreate a systemd service file or use launchd on macOS for automatic startup.
Run the service in a Docker container for isolation and easy deployment.
- Heartbeat Monitoring: Logs every minute to confirm service is running
- Background Tasks: Runs periodic tasks every 30 seconds (configurable)
- Graceful Shutdown: Handles SIGINT/SIGTERM signals properly
- Error Handling: Catches uncaught exceptions and unhandled rejections
- Process Management: PID file tracking and status monitoring
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | development |
PORT |
Service port | 3000 |
LOG_LEVEL |
Logging level | info |
DB_HOST |
Database host | localhost |
DB_PORT |
Database port | 5432 |
DB_NAME |
Database name | gooch_feeder |
API_BASE_URL |
API base URL | https://api.example.com |
API_TIMEOUT |
API timeout (ms) | 30000 |
API_RETRIES |
API retry count | 3 |
DISCORD_WEBHOOK_URL |
Discord incoming webhook URL for failure notifications | (unset = disabled) |
DISCORD_MIN_INTERVAL_MS |
Min ms between Discord alerts per feeder (anti-spam) | 60000 (0 = no throttle) |
DISCORD_NOTIFY_ON_CLI |
Send Discord alerts from npm run test:feeder / test:all-feeders |
false |
DISCORD_NOTIFY_ON_STARTUP |
One Discord message when the service starts (feeder counts, mode) | true |
DISCORD_NOTIFY_BALANCE |
Discord when signer balance is low on Algorand + Voi mainnets | true |
DISCORD_BALANCE_THRESHOLD_MICRO |
Low-balance threshold in micro-units (5e6 = 5 ALGO / 5 VOI) | 5000000 |
DISCORD_BALANCE_CHECK_INTERVAL_MS |
How often to poll balances | 900000 (15 min) |
DISCORD_BALANCE_MIN_INTERVAL_MS |
Min time between Discord low-balance alerts per network | 3600000 (1 h) |
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request
MIT License - see LICENSE file for details.