Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ Swagger documentation available at: `http://localhost:3001/swagger`
## CoinGecko

The monitoring service needs a CoinGecko-compatible endpoint for BTC spot
prices (drives the WCBTC suspicious-liq-price watchdog) and the daily Pro
quota probe. Configuration is two env vars:
prices — they drive the WCBTC suspicious-liq-price watchdog. Configuration
is two env vars:

| Var | Required | Purpose |
|---|---|---|
Expand Down
45 changes: 0 additions & 45 deletions src/monitoringV2/price.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,8 @@ interface CoingeckoEndpoint {
headers: Record<string, string>;
}

interface CoingeckoKeyInfo {
plan?: string;
monthly_call_credit?: number;
current_total_monthly_calls?: number;
current_remaining_monthly_calls?: number;
}

const STALENESS_ALERT_THRESHOLD_MS = 60 * 60 * 1000;
const STALENESS_ALERT_REPEAT_MS = 6 * 60 * 60 * 1000;
const QUOTA_REMAINING_ALERT_THRESHOLD = 25_000;
const QUOTA_ALERT_REPEAT_MS = 24 * 60 * 60 * 1000;

@Injectable()
export class PriceService {
Expand All @@ -53,7 +44,6 @@ export class PriceService {
// the alert indefinitely.
private btcLastSuccessMs: number = Date.now();
private btcStalenessAlertedAt: number | null = null;
private quotaAlertedAt: number | null = null;

constructor(
private readonly providerService: ProviderService,
Expand Down Expand Up @@ -186,41 +176,6 @@ export class PriceService {
);
}

/**
* Daily probe of /api/v3/key through the pricing proxy. Emits a critical
* alert when the monthly remaining call credit drops below
* QUOTA_REMAINING_ALERT_THRESHOLD.
*/
@Cron(CronExpression.EVERY_DAY_AT_NOON)
async checkCoingeckoQuota(): Promise<void> {
try {
const { baseUrl, headers } = this.resolveCoingeckoEndpoint();
const response = await axios.get<CoingeckoKeyInfo>(`${baseUrl}/api/v3/key`, {
headers,
timeout: 10000,
});
const { current_remaining_monthly_calls: remaining, monthly_call_credit: credit } = response.data;
if (typeof remaining !== 'number' || typeof credit !== 'number' || credit <= 0) return;

const pct = Math.round((remaining / credit) * 100);
this.logger.log(`CoinGecko quota: ${remaining} of ${credit} calls remaining (${pct}%)`);

if (remaining >= QUOTA_REMAINING_ALERT_THRESHOLD) {
this.quotaAlertedAt = null;
return;
}
if (this.quotaAlertedAt && Date.now() - this.quotaAlertedAt < QUOTA_ALERT_REPEAT_MS) return;

this.quotaAlertedAt = Date.now();
await this.telegramService.sendCriticalAlert(
`CoinGecko monthly quota almost exhausted: ${remaining.toLocaleString()} of ` +
`${credit.toLocaleString()} calls remaining (${pct}%).`
);
} catch (error) {
this.logger.warn(`CoinGecko quota probe failed: ${error.message ?? error}`);
}
}

private async getGeckoTerminalPricesInUSD(addresses: string[]): Promise<{ [key: string]: string }> {
if (addresses.length === 0) return {};

Expand Down
Loading