diff --git a/.env.example b/.env.example index bb7ff9e..b2e7809 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,17 @@ COINGECKO_BASE_URL=http://pricing-proxy:8080/coingecko # own key) or to the public host anonymously. # COINGECKO_API_KEY= +# GeckoTerminal Configuration. +# +# GECKOTERMINAL_BASE_URL: required. The origin used for GeckoTerminal token +# price calls. Recommended is the in-cluster pricing-proxy +# (https://github.com/DFXswiss/pricing-proxy), which gives cache, coalescing +# and validation on top of the shared free-tier 30 req/min quota. Anything +# GeckoTerminal-compatible works (api.geckoterminal.com directly is fine for +# single-consumer setups, but the free-tier quota is shared across the whole +# host IP). +GECKOTERMINAL_BASE_URL=http://pricing-proxy:8080/geckoterminal + # Telegram Bot Configuration (optional) # TELEGRAM_BOT_TOKEN=5123456789:ABCdefGHIjklMNOpqrsTUVwxyz # Path to the persisted subscribers file. Each operator subscribes themselves by diff --git a/src/config/config.service.ts b/src/config/config.service.ts index 72582bd..80c8759 100644 --- a/src/config/config.service.ts +++ b/src/config/config.service.ts @@ -92,6 +92,10 @@ export class AppConfigService { return this.monitoringConfig.coingeckoBaseUrl || undefined; } + get geckoTerminalBaseUrl(): string | undefined { + return this.monitoringConfig.geckoTerminalBaseUrl || undefined; + } + get environment(): string | undefined { return this.monitoringConfig.environment; } diff --git a/src/config/monitoring.config.ts b/src/config/monitoring.config.ts index b64a1f8..5cc0a97 100644 --- a/src/config/monitoring.config.ts +++ b/src/config/monitoring.config.ts @@ -75,6 +75,10 @@ export class MonitoringConfig { @IsString() coingeckoApiKey?: string; + @IsOptional() + @IsString() + geckoTerminalBaseUrl?: string; + @IsOptional() @IsString() environment?: string; @@ -105,6 +109,7 @@ export default registerAs('monitoring', () => { config.alertTimeframeHours = parseInt(process.env.ALERT_TIMEFRAME_HOURS || '12'); config.coingeckoBaseUrl = process.env.COINGECKO_BASE_URL || ''; config.coingeckoApiKey = process.env.COINGECKO_API_KEY || ''; + config.geckoTerminalBaseUrl = process.env.GECKOTERMINAL_BASE_URL || ''; config.environment = process.env.ENVIRONMENT?.toLowerCase(); config.chain = process.env.CHAIN; diff --git a/src/monitoringV2/price.service.ts b/src/monitoringV2/price.service.ts index 5bf4e91..63fd721 100644 --- a/src/monitoringV2/price.service.ts +++ b/src/monitoringV2/price.service.ts @@ -64,6 +64,9 @@ export class PriceService { if (!this.appConfigService.coingeckoBaseUrl) { throw new Error('COINGECKO_BASE_URL is not set'); } + if (!this.appConfigService.geckoTerminalBaseUrl) { + throw new Error('GECKOTERMINAL_BASE_URL is not set'); + } } registerWcbtcAddress(address: string): void { @@ -225,9 +228,11 @@ export class PriceService { const remaining = addresses.filter((addr) => !cached[addr]); if (remaining.length === 0) return cached; + const baseUrl = this.appConfigService.geckoTerminalBaseUrl; + try { const response = await axios.get( - `https://api.geckoterminal.com/api/v2/simple/networks/citrea/token_price/${remaining.map((a) => a.toLowerCase()).join(',')}`, + `${baseUrl}/api/v2/simple/networks/citrea/token_price/${remaining.map((a) => a.toLowerCase()).join(',')}`, { headers: { accept: 'application/json' }, timeout: 10000,