[TASK-14780] Feat/add manteca currencies to exchange rate widget#1255
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughReplaces Bridge-centric exchange-rate logic with a centralized Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (2)📚 Learning: 2025-08-12T17:47:28.362ZApplied to files:
📚 Learning: 2025-05-19T19:40:43.138ZApplied to files:
🧬 Code graph analysis (1)src/app/api/exchange-rate/route.ts (2)
🔇 Additional comments (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🧪 Early access (Sonnet 4.5): enabledWe are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience. Note:
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/api/exchange-rate/route.ts (1)
118-159: Inconsistent error handling between main handler and helper function.The
getExchangeRatehelper returns the raw rate value from Bridge/Frankfurter responses, but these functions returnNextResponseobjects, not numbers. This will cause the JSON parsing on lines 144-145 and 153-154 to fail.Fix the return type mismatch:
if (bridgeResult) { - const data = await bridgeResult.json() - return data.rate + // bridgeResult is a NextResponse, extract the rate from it + if (bridgeResult instanceof NextResponse) { + // Bridge already returns the rate in the response + // We need to refactor fetchFromBridge to return just the rate for this helper + return null // Fall back to Frankfurter + } + return bridgeResult } // Use Frankfurter for all other pairs or as fallback const frankfurterResult = await fetchFromFrankfurter(from, to) -const data = await frankfurterResult.json() -return data.rate +// Same issue here - fetchFromFrankfurter returns NextResponse +// Need to refactor to return just the rate value +return nullActually, a better approach would be to refactor the helper functions to return rate values instead of NextResponse objects, or create separate internal functions for rate fetching:
+async function getBridgeRate( + from: string, + to: string, + rateType: 'buy_rate' | 'sell_rate', + shouldInvert: boolean +): Promise<number | null> { + const bridgeAPIKey = process.env.BRIDGE_API_KEY + if (!bridgeAPIKey) { + console.warn('Bridge API key not set') + return null + } + + try { + const url = `https://api.bridge.xyz/v0/exchange_rates?from=${from.toLowerCase()}&to=${to.toLowerCase()}` + const response = await fetch(url, { + method: 'GET', + headers: { 'Api-Key': bridgeAPIKey }, + }) + + if (!response.ok) { + console.error(`Bridge API error: ${response.status} ${response.statusText}`) + return null + } + + const bridgeData: BridgeExchangeRateResponse = await response.json() + if (!bridgeData[rateType]) { + console.error(`Invalid Bridge response: missing ${rateType}`) + return null + } + + let rate = parseFloat(bridgeData[rateType]) + if (shouldInvert) { + rate = 1 / rate + } + return rate + } catch (error) { + console.error('Bridge API exception:', error) + return null + } +} + +async function getFrankfurterRate(from: string, to: string): Promise<number | null> { + try { + const url = `https://api.frankfurter.app/latest?from=${from}&to=${to}` + const response = await fetch(url) + + if (!response.ok) { + console.error(`Frankfurter API error: ${response.status} ${response.statusText}`) + return null + } + + const data = await response.json() + return data.rates[to] * 0.995 // Subtract 50bps + } catch (error) { + console.error('Frankfurter API exception:', error) + return null + } +}Then update the
getExchangeRatefunction to use these new helpers.
🧹 Nitpick comments (3)
src/app/api/exchange-rate/route.ts (3)
271-271: Document the 50bps fee adjustment.The 0.995 multiplier (50 basis points reduction) on Frankfurter rates should be documented to explain the business logic behind this adjustment.
const exchangeRate: ExchangeRateResponse = { - rate: data.rates[to] * 0.995, // Subtract 50bps + rate: data.rates[to] * 0.995, // Apply 0.5% margin/fee to Frankfurter rates for consistency with other providers }
1-279: Add rate limiting and monitoring for external API calls.The route makes multiple external API calls without rate limiting, which could lead to hitting provider limits or increased costs. Consider implementing request throttling and monitoring.
Consider implementing:
- Rate limiting per provider using a library like
p-limitorbottleneck- Metrics/monitoring for API call success rates and latencies
- Circuit breaker pattern for failing providers
- Caching layer (Redis/in-memory) to reduce API calls for frequently requested pairs
18-18: Externalize LATAM currency codes into a shared constant
- Move the hardcoded
MANTECA_CURRENCIESset to a shared export (e.g. insrc/constants/manteca.consts.tsas
export const MANTECA_SUPPORTED_CURRENCIES = ['ARS','BRL','COP','CRC','PUSD','GTQ','PHP','BOB']) and import it insrc/app/api/exchange-rate/route.ts.- All current codes (
ARS,BRL,COP,CRC,PUSD,GTQ,PHP,BOB) map to entries inMANTECA_COUNTRIES.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/api/exchange-rate/route.ts(4 hunks)src/constants/countryCurrencyMapping.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/app/api/exchange-rate/route.ts
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.
Applied to files:
src/constants/countryCurrencyMapping.ts
🧬 Code graph analysis (1)
src/app/api/exchange-rate/route.ts (1)
src/services/manteca.ts (1)
mantecaApi(95-234)
🔇 Additional comments (2)
src/constants/countryCurrencyMapping.ts (1)
45-47: Good change enabling LATAM currencies!The removal of
comingSoonflags for BRL and ARS properly activates these currencies in the system, aligning with the new Manteca integration for LATAM currency support.src/app/api/exchange-rate/route.ts (1)
46-92: Well-structured fallback chain for USD conversions.The implementation properly prioritizes Manteca for LATAM currencies, falls back to Bridge for specific pairs, and ultimately uses Frankfurter as the final fallback. The error handling with console warnings helps with debugging provider failures.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/app/api/exchange-rate/route.ts (2)
9-9: Consider centralizing the MANTECA_CURRENCIES set.The list of Manteca-supported currencies is duplicated between this file and
src/constants/index.ts(whereMANTECA_CURRENCIESarray is defined in the relevant snippet fromcurrency.ts). If the supported currencies change, both locations need updates.Consider importing this set from a shared constants file:
-// LATAM currencies that should use Manteca API -const MANTECA_CURRENCIES = new Set(['ARS', 'BRL', 'COP', 'CRC', 'PUSD', 'GTQ', 'PHP', 'BOB']) +import { MANTECA_CURRENCIES } from '@/constants' + +const MANTECA_CURRENCIES_SET = new Set(MANTECA_CURRENCIES)Then replace references to
MANTECA_CURRENCIESwithMANTECA_CURRENCIES_SETthroughout this file.
37-65: Good fallback pattern for USD-based pairs.The logic correctly attempts
fetchFromCurrencyPricefirst for Manteca/EUR/MXN currencies, then falls back to Frankfurter. The warning log on line 60 is helpful for debugging.However, consider adding validation for the rate returned from
fetchFromCurrencyPricebefore returning it:const currencyPriceRate = await fetchFromCurrencyPrice(fromUc, toUc) - if (currencyPriceRate !== null) { + if (currencyPriceRate !== null && isFinite(currencyPriceRate) && currencyPriceRate > 0) { return NextResponse.json( { rate: currencyPriceRate },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/app/api/exchange-rate/route.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/app/api/exchange-rate/route.ts
🧬 Code graph analysis (1)
src/app/api/exchange-rate/route.ts (2)
src/app/actions/exchange-rate.ts (2)
ExchangeRateResponse(8-15)getExchangeRate(25-61)src/app/actions/currency.ts (1)
getCurrencyPrice(9-48)
🔇 Additional comments (2)
src/app/api/exchange-rate/route.ts (2)
2-2: LGTM: Using action instead of direct API calls.The import of
getCurrencyPricealigns with the feedback to use actions rather than callingmantecaApi.getPricesdirectly. This centralizes currency pricing logic appropriately.Based on past review comments.
147-172: Function works correctly for direct HTTP responses.
fetchFromFrankfurtercorrectly fetches rates from the Frankfurter API, applies the 50 bps spread (line 164), and returns a properly formattedNextResponse.However, this function returns
NextResponsewhich makes it incompatible for internal rate calculations ingetExchangeRate(lines 104-106). See the critical issue flagged in lines 91-111.
No description provided.