The Automatic Gas Top-up Alert feature monitors the Admin wallet XLM balance to prevent running out of funds for transaction fees. When the balance drops below a configured threshold, a critical webhook alert is sent to Discord.
- Continuous Monitoring: The
GasBalanceMonitorServicechecks the admin wallet balance every 5 minutes (configurable). - Threshold Detection: If the balance falls below 20 XLM (default, configurable), an alert is triggered
- Alert Notification: A critical webhook alert is sent to Discord
- Rate Limiting: Alerts are rate-limited to a maximum of 1 per hour to prevent notification spam
- Persistence: Alert timing survives process restarts, ensuring proper rate limiting across deployments
- Escalation: After 3 consecutive balance check failures, a critical escalation alert is sent
- Status Tracking: Current balance, failure count, and threshold are tracked for operational visibility
SOROBAN_ADMIN_SECRETorORACLE_SECRET_KEY- The secret key of the Admin wallet to monitor
- Used to derive the public key for balance queries
- Already required by other StellarFlow components
-
GAS_BALANCE_ALERT_THRESHOLD_XLM- Alert threshold in XLM (default:
20) - Minimum balance before alert is triggered
- Example:
GAS_BALANCE_ALERT_THRESHOLD_XLM=15(alert when balance < 15 XLM)
- Alert threshold in XLM (default:
-
DEBUG- Enable debug logging for balance checks (logs every check, can be verbose)
- Default: unset (only logs warnings/errors)
DISCORD_WEBHOOK_URL(required)- Discord webhook URL for alerts
- Example:
https://discord.com/api/webhooks/XXXXXXXXXXXXX/XXXXXXXXXXXXXXXXX
# Admin account to monitor
SOROBAN_ADMIN_SECRET=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Alert threshold (20 XLM default)
GAS_BALANCE_ALERT_THRESHOLD_XLM=20
# Webhook configuration (Discord only)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXTriggered when: Admin wallet balance < configured threshold
Frequency: Maximum 1 per hour (rate-limited)
Format: Discord embed with red color
Fields:
- Current Balance (XLM)
- Alert Threshold (XLM)
- Deficit (how much below threshold)
- Action Required
- Timestamp
Triggered when: 3 consecutive failed balance checks
Frequency: Once per escalation cycle
Format: Discord embed with red color
Indicates:
- Unable to reach Stellar Horizon
- Environment variable misconfiguration
- Network connectivity issues
Fields:
- Consecutive Failures count
- Last Known Balance
- Issue Description
- Required Action (investigate connectivity)
- Timestamp
Check the health endpoint or monitor logs for:
[GasBalanceMonitor] Started with 300000ms check interval (threshold: 20 XLM)
⛽ Gas balance monitor service started
To get current service status:
# From Node.js environment
const { getGasBalanceMonitorService } = require('./src/services/gasBalanceMonitorService');
const monitor = getGasBalanceMonitorService();
console.log(monitor.getStatus());Output:
{
"isRunning": true,
"checkIntervalMs": 300000,
"balanceThresholdXLM": 20,
"lastKnownBalance": 25.5,
"consecutiveFailures": 0
}Look for these log messages on application start:
⛽ Gas balance monitor service started
[GasBalanceMonitor] Started with 300000ms check interval (threshold: 20 XLM)
To manually check the admin wallet balance:
npm run check:gas-balance- Verify
DISCORD_WEBHOOK_URLis set correctly - Verify network connectivity to Discord API
- Check logs for webhook errors
- Test webhook with curl to verify it's working
If you see "CRITICAL: Gas Monitor Failures", the balance check has failed 3+ times:
- Check Stellar Horizon connectivity (TESTNET or MAINNET based on your config)
- Verify
SOROBAN_ADMIN_SECRETorORACLE_SECRET_KEYis valid - Verify
STELLAR_NETWORKis set correctly (TESTNET or PUBLIC) - Check DNS resolution for Horizon
- Review firewall/proxy rules
- Enable
DEBUG=1for detailed error logging
- Verify
GAS_BALANCE_ALERT_THRESHOLD_XLMis set correctly - Verify admin wallet actually has low XLM
- Verify Stellar Horizon API is reachable
- Check
STELLAR_NETWORKis set correctly - Enable
DEBUG=1for verbose logging - Verify at least 1 hour has passed since last alert (rate limiting)
To temporarily disable alerts without stopping the service:
- Set
GAS_BALANCE_ALERT_THRESHOLD_XLMto an extremely high value (e.g.,999999)
| Variable | Purpose | Default | Required |
|---|---|---|---|
STELLAR_NETWORK |
Network to monitor (TESTNET or PUBLIC) | TESTNET | No |
SOROBAN_ADMIN_SECRET |
Admin wallet secret (priority 1) | - | If ORACLE_SECRET_KEY not set |
ORACLE_SECRET_KEY |
Admin wallet secret (priority 2, fallback) | - | If SOROBAN_ADMIN_SECRET not set |
DISCORD_WEBHOOK_URL |
Discord webhook for alerts | - | Yes |
GAS_BALANCE_ALERT_THRESHOLD_XLM |
Balance alert threshold | 20 | No |
DEBUG |
Enable verbose logging | unset | No |
The service uses a lazy singleton to defer initialization:
let _instance: GasBalanceMonitorService | null = null;
export function getGasBalanceMonitorService(): GasBalanceMonitorService {
if (!_instance) {
_instance = new GasBalanceMonitorService();
}
return _instance;
}This prevents Keypair.fromSecret from running at import time, which would crash if environment variables are missing.
- Gets created lazily on first call to
getGasBalanceMonitorService() - Loaded and started in
httpServer.listen()block - Loads persisted alert time from
/tmp/gas_balance_last_alert_time.json - Runs immediate check on startup before periodic timer
- Gracefully stopped on SIGINT/SIGTERM signals
- Logs all lifecycle events with
[GasBalanceMonitor]prefix
- Query Stellar Horizon for account balance
- Find native (XLM) balance
- Compare against threshold
- If below threshold and rate limit OK: send alert
- Track consecutive failures; escalate after 3
- Persist alert time to survive restarts
- Alert time persisted to
/tmp/gas_balance_last_alert_time.json - Max 1 alert per hour (MIN_ALERT_INTERVAL_MS = 3600000 ms)
- Persistence survives process restarts
- Each process instance has independent rate limiting
- Failure escalation separate from balance alert rate limiting
- Set appropriate threshold: 20 XLM is appropriate for most use cases
- Monitor actively: Check Discord channel regularly for alerts
- Plan top-ups: Use alerts to trigger XLM purchase/transfer workflows
- Test in TESTNET: Verify alerts work before production deployment
- Document choices: Record your threshold selection rationale
- Setup notifications: Use Discord roles/mentions for important alerts
- Enable DEBUG in dev: Set
DEBUG=1during development/troubleshooting - Persist for containers: Consider database storage for alert timing in production
- Rate limiting is per process; multiple instances have independent limits
- Alert time persisted to
/tmp/may not survive container restarts - Wallet address intentionally omitted from alerts (security)
- Condition: balance < threshold (not <=)
- No dynamic threshold adjustment based on transaction volume
- GitHub issue #162: "Automatic Gas Top-up" Alert
- Complements multi-sig and price update functionality
- Reuses existing Discord webhook infrastructure
Look for these logs on startup:
⛽ Gas balance monitor service started
[GasBalanceMonitor] Started with 300000ms check interval (threshold: 20 XLM)
const { getGasBalanceMonitorService } = require('./src/services/gasBalanceMonitorService');
const monitor = getGasBalanceMonitorService();
console.log(monitor.getStatus());DEBUG=1 npm startcurl -X POST "YOUR_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"embeds": [{
"title": "Test",
"color": 16711680
}]
}'# TESTNET
curl https://horizon-testnet.stellar.org/
# MAINNET
curl https://horizon.stellar.org/cat /tmp/gas_balance_last_alert_time.jsonOutput should show last alert timestamp if any alerts were sent.