Skip to content

feat(pricing): public CoinGecko simple/price proxy endpoint#3714

Open
TaprootFreak wants to merge 2 commits into
developfrom
feat/pricing-simple-price
Open

feat(pricing): public CoinGecko simple/price proxy endpoint#3714
TaprootFreak wants to merge 2 commits into
developfrom
feat/pricing-simple-price

Conversation

@TaprootFreak
Copy link
Copy Markdown
Collaborator

Summary

Adds a public, unauthenticated GET /pricing/simple-price endpoint that proxies CoinGecko's /simple/price through api.dfx.swiss, using the central CoinGecko Pro key and a 60 s in-memory cache. Closes #3712.

  • New SimplePriceRequest DTO (comma-list ids & vs_currencies, ids capped at 100).
  • New CoinGeckoService.getSimplePrice() + getSupportedCurrencies(); responses cached via AsyncCache (60 s TTL).
  • Controller validates vs_currencies against the supported list and returns CoinGecko's raw shape unchanged so the wallet only needs to swap the base URL.

Test plan

  • DEV: curl https://dev.api.dfx.swiss/v1/pricing/simple-price?ids=bitcoin,ethereum&vs_currencies=usd,eur returns CoinGecko's standard shape
  • Invalid currency → 400 Bad Request
  • >100 ids → 400 Bad Request
  • Second identical request within 60 s does not hit CoinGecko (cache verified via logs)
  • Wallet (dfx-wallet) swap of COINGECKO_SIMPLE_PRICE to the new endpoint works end-to-end

Out of scope

  • /coins/list endpoint (separate issue)
  • Contract-based lookups (internal getRawPrice covers backend consumers)

Expose GET /pricing/simple-price as a cached, unauthenticated pass-through
to CoinGecko's /simple/price, routing wallet traffic through the central
CoinGecko Pro key. Caps ids at 100, whitelists vs_currencies against
CoinGecko's supported list, and caches responses for 60s.

Closes #3712
Move the vs_currencies whitelist check from the controller into
CoinGeckoService.getSimplePrice so the controller stays a thin
pass-through, drop the duplicate lowercasing pass, restore the
alphabetical import order in coin-gecko.service.ts and document the
response shape via @ApiOkResponse.
@TaprootFreak TaprootFreak marked this pull request as ready for review May 15, 2026 19:59
@TaprootFreak TaprootFreak requested a review from davidleomay as a code owner May 15, 2026 19:59
@davidleomay
Copy link
Copy Markdown
Member

davidleomay commented Jun 2, 2026

Review feedback

1. Cache key granularity

The current cache key is the full request combination (bitcoin,ethereum|usd,eur). This means bitcoin|usd and bitcoin,ethereum|usd are separate cache misses even though the bitcoin/usd price was already fetched.

Consider caching per individual (coinId, currency) pair instead, then assembling the response from cached entries. This dramatically improves hit rate and bounds cache size to numCoins × numCurrencies (finite) rather than 2^numCoins × 2^numCurrencies (combinatorial).

2. Unbounded cache + no rate limit = memory/cost vulnerability

This is a public, unauthenticated endpoint proxying a paid CoinGecko Pro key. The AsyncCache is a Map with no size cap and no eviction — entries are never deleted, only marked stale after the TTL expires and re-fetched on the next get(). Every unique combination of ids/currencies creates a permanent Map entry that lives for the lifetime of the process.

Since every unique combination of ids/currencies creates a new entry, a single client can:

  • Exhaust memory: flood the cache with thousands of unique key combinations (100 allowed ids → near-infinite permutations), each permanently storing a response object. A single burst is enough — entries are never cleaned up, even after TTL.
  • Burn API quota: every cache miss hits CoinGecko, so varying the request slightly bypasses the cache entirely

Every other endpoint on this controller requires AuthGuard + RoleGuard(ADMIN). At minimum this endpoint needs IP-based rate limiting (e.g. @Throttle()), and ideally the per-pair caching approach above which naturally bounds cache size.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(pricing): expose CoinGecko /simple/price proxy endpoint

2 participants