[TASK-14805] Feat/kyc links integration#1228
[TASK-14805] Feat/kyc links integration#1228Zishan-7 wants to merge 4 commits intofeat/manteca-integrationfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds Manteca KYC gating and country-aware flow across claim, request, and payment views; introduces dynamic currency handling; updates ActionDetails card to support regional method visuals; adjusts ActionList visibility; refactors a QR-pay error card and footer; tweaks useCurrency initial loading; and adds an API key header to Manteca deposit requests. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
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. 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 (5)
src/app/(mobile-ui)/qr-pay/page.tsx (2)
173-179: Fix: loading state not reset on failed/reverted tx; remove stray statement.Two issues:
- setLoadingState('Idle') is not called when the transaction reverts, leaving the UI in a perpetual loading state.
- A stray statement
finalPaymentLockis present and does nothing.Patch below adds robust error handling around sendMoney, resets loading state on all failure paths, and removes the stray statement.
if (finalPaymentLock.code === '') { - finalPaymentLock setErrorMessage('Could not fetch qr payment details') setIsSuccess(false) setLoadingState('Idle') return } - setLoadingState('Preparing transaction') - const { userOpHash, receipt } = await sendMoney(MANTECA_DEPOSIT_ADDRESS, finalPaymentLock.paymentAgainstAmount) - if (receipt !== null && isTxReverted(receipt)) { - setErrorMessage('Transaction reverted by the network.') - setIsSuccess(false) - return - } - const txHash = receipt?.transactionHash ?? userOpHash + setLoadingState('Preparing transaction') + let userOpHash: string | undefined + let receipt: unknown + try { + const res = await sendMoney( + MANTECA_DEPOSIT_ADDRESS, + finalPaymentLock.paymentAgainstAmount + ) + userOpHash = res.userOpHash + receipt = res.receipt + } catch (err) { + captureException(err) + setErrorMessage('Could not submit transaction. Please try again.') + setIsSuccess(false) + setLoadingState('Idle') + return + } + if (receipt !== null && isTxReverted(receipt as any)) { + setErrorMessage('Transaction reverted by the network.') + setIsSuccess(false) + setLoadingState('Idle') + return + } + const txHash = (receipt as any)?.transactionHash ?? userOpHash setLoadingState('Paying')Also applies to: 181-186, 198-199
380-384: Guard against null methodIcon to avoid runtime crash.If an unexpected paymentLock.type arrives, methodIcon is null and Next/Image will throw. Render a fallback.
- <div className="flex items-center justify-center rounded-full bg-white"> - <Image src={methodIcon} alt="Mercado Pago" width={50} height={50} /> - </div> + <div className="flex items-center justify-center rounded-full bg-white"> + {methodIcon ? ( + <Image + src={methodIcon} + alt={paymentLock?.type === 'PIX' ? 'Pix' : 'Mercado Pago'} + width={50} + height={50} + /> + ) : ( + <Icon name="qr" size={32} aria-label="QR payment" /> + )} + </div>src/app/[...recipient]/client.tsx (1)
493-501: Null-deref risk: currencyPrice!.buy used before data loads. Guard with symbol/price presence.On first render for non-USD currencies, currencyPrice is null while fetch is pending. This crashes the page.
Apply this safe construction:
- currency={ - currencyCode - ? { - code: currencyCode, - symbol: currencySymbol!, - price: currencyPrice!.buy, - } - : undefined - } + currency={ + currencyCode && currencySymbol && currencyPrice + ? { + code: currencyCode, + symbol: currencySymbol, + price: currencyPrice.buy, + } + : undefined + }src/components/Global/PeanutActionDetailsCard/index.tsx (1)
51-52: logo prop should accept string URLs (remote flags), not only StaticImageData.Upstream passes dynamic URLs; current typing rejects strings.
- logo?: StaticImageData + logo?: StaticImageData | stringsrc/services/manteca.ts (1)
136-142: Remove secret API keys from client bundle — move sensitive calls server-side.src/services/manteca.ts currently imports PEANUT_API_KEY and sends it in client requests; the deposit function also contains a hardcoded API key ('DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo'). Move any calls that require the secret (getPrices — src/services/manteca.ts, lines ~136–142 — and deposit) into a server-only module/route (e.g., src/app/actions/manteca/prices.ts) that reads the key from env and proxies requests. Remove PEANUT_API_KEY import and the hardcoded key from client code; update the client to call the new server endpoint and strip all 'api-key' headers from browser-side requests.
🧹 Nitpick comments (17)
src/components/Kyc/CountryRegionRow.tsx (3)
4-7: Tighten prop typing or document expected code format.If
countryCodemay be ISO2 or ISO3 (per prior learnings about mixed maps), clarify the expected format in the prop type or JSDoc to prevent mismatches later.
16-18: Normalize country codes before rendering (aligns with prior mapping gotchas).To avoid lookup misses downstream, normalize once here.
export const CountryRegionRow = ({ countryCode, isBridge }: CountryRegionRowProps) => { if (!isBridge && !countryCode) { return null } return ( <PaymentInfoRow - label="Country/Region" - value={<CountryFlagAndName countryCode={countryCode ?? ''} isBridgeRegion={isBridge} />} + label="Country/Region" + value={ + <CountryFlagAndName + countryCode={(countryCode ?? '').trim().toUpperCase()} + isBridgeRegion={isBridge} + /> + } /> ) }Note: If
CountryFlagAndNamealready normalizes, skip this.
16-17: i18n: replace hardcoded label.Use translations to match the rest of KYC UI.
- <PaymentInfoRow - label="Country/Region" + <PaymentInfoRow + label={t('kyc.countryRegion')}Additional change outside hunk:
+ import { useTranslation } from 'react-i18next' export const CountryRegionRow = ({ countryCode, isBridge }: CountryRegionRowProps) => { + const { t } = useTranslation()src/app/(mobile-ui)/qr-pay/page.tsx (4)
96-114: Handle getCurrencyPrice errors to prevent silent failures.Wrap the async currency fetch in try/catch and surface a friendly message; also avoid setting state on undefined.
- const getCurrencyObject = async () => { - let currencyCode: string - let price: number - currencyCode = paymentLock.paymentAsset - if (paymentLock.code === '') { - price = (await getCurrencyPrice(currencyCode)).sell - } else { - price = Number(paymentLock.paymentPrice) - } - return { - code: currencyCode, - symbol: currencyCode, - price, - } - } - getCurrencyObject().then(setCurrency) + const getCurrencyObject = async () => { + try { + const currencyCode = paymentLock.paymentAsset + const price = + paymentLock.code === '' + ? (await getCurrencyPrice(currencyCode)).sell + : Number(paymentLock.paymentPrice) + return { code: currencyCode, symbol: currencyCode, price } + } catch (err) { + captureException(err) + setErrorInitiatingPayment('Failed to fetch currency price. Please try again.') + return undefined + } + } + getCurrencyObject().then((c) => c && setCurrency(c))
221-233: Minor UI: divider theming + resilient “Go Back”.
- Use themed divider colors to work in dark mode.
- Fallback to /home if there’s no browser history.
- <div className="h-[1px] bg-black"></div> + <div className="h-px bg-black/10 dark:bg-white/10" aria-hidden="true"></div> @@ - <Button onClick={() => router.back()} variant="purple"> + <Button + onClick={() => { + if (typeof window !== 'undefined' && window.history.length > 1) { + router.back() + } else { + router.replace('/home') + } + }} + variant="purple" + >
200-200: Clean deps: remove unused usdAmount from payQR useCallback.Avoid unnecessary re-creations of the callback.
- }, [paymentLock?.code, sendMoney, usdAmount, qrCode, currencyAmount]) + }, [paymentLock?.code, sendMoney, qrCode, currencyAmount])
35-35: Externalize the MANTECA deposit address.Hard-coding chain-specific addresses complicates multi-env deployments. Prefer a config/env-driven constant per network.
-const MANTECA_DEPOSIT_ADDRESS = '0x959e088a09f61aB01cb83b0eBCc74b2CF6d62053' +// e.g., from a config module that resolves by env/network +// import { MANTECA_DEPOSIT_ADDRESS } from '@/config/manteca'src/hooks/useCurrency.ts (1)
21-25: Handle prop changes and avoid stale currency code.If currencyCode prop changes after mount, code won't update. Add an effect to sync.
export const useCurrency = (currencyCode: string | null) => { const [code, setCode] = useState<string | null>(currencyCode?.toUpperCase() ?? null) const [symbol, setSymbol] = useState<string | null>(null) const [price, setPrice] = useState<{ buy: number; sell: number } | null>(null) const [isLoading, setIsLoading] = useState<boolean>(true) + + useEffect(() => { + setCode(currencyCode?.toUpperCase() ?? null) + }, [currencyCode])src/app/[...recipient]/client.tsx (1)
563-567: Typo in user-facing copy.“They payment” -> “The payment”.
- message: 'They payment you are trying to access is invalid. Please check the URL and try again.', + message: 'The payment you are trying to access is invalid. Please check the URL and try again.',src/components/Global/PeanutActionDetailsCard/index.tsx (2)
171-171: Flag CDN expects lowercase country codes.Ensure flag URLs are lowercase to avoid 404s.
- const imgSrc = logo ? logo : `https://flagcdn.com/w320/${countryCodeForFlag}.png` + const imgSrc = logo ? logo : `https://flagcdn.com/w320/${(countryCodeForFlag ?? '').toLowerCase()}.png`
176-184: Alt text mismatches when rendering a custom logo.If logo is used, alt will show “undefined flag”. Make alt conditional.
- <Image - src={imgSrc} - alt={`${countryCodeForFlag} flag`} + <Image + src={imgSrc} + alt={logo ? 'provider logo' : `${countryCodeForFlag} flag`}src/components/Claim/Link/views/MantecaReviewStep.tsx (2)
29-29: Avoid “undefined” exchange-rate output; format and normalize currency code.Pass an uppercased currency to the hook and guard the exchange-rate row so we don’t render “undefined” when price lookup fails; also format the number.
- const { price, isLoading } = useCurrency(currency) + const { price, isLoading } = useCurrency(currency.toUpperCase()) @@ - value: `1 USD = ${price?.buy} ${currency}`, + value: price?.buy + ? `1 USD = ${price.buy.toLocaleString(undefined, { maximumFractionDigits: 4 })} ${currency.toUpperCase()}` + : 'N/A',Also applies to: 41-41
61-66: Normalize currency code in withdraw payload.Ensure backend receives standardized currency codes.
const { data, error: withdrawError } = await mantecaApi.withdraw({ amount: amount.replace(/,/g, ''), destinationAddress, txHash, - currency, + currency: currency.toUpperCase(), })src/components/Common/CountryListRouter.tsx (2)
44-49: Remove duplicate setFlowStep destructuring; use a single alias consistently.Destructuring the same property twice is confusing and error‑prone. Use the aliased setter everywhere.
- const { - setFlowStep: setClaimBankFlowStep, - setSelectedCountry, - setClaimToMercadoPago, - setFlowStep, - } = useClaimBankFlow() + const { setFlowStep: setClaimBankFlowStep, setSelectedCountry, setClaimToMercadoPago } = useClaimBankFlow() @@ - if (isMantecaSupportedCountry) { - setFlowStep(null) // reset the flow step to initial view first + if (isMantecaSupportedCountry) { + setClaimBankFlowStep(null) // reset the flow step to initial view first setClaimToMercadoPago(true) } else { setClaimBankFlowStep(ClaimBankFlowStep.BankDetailsForm) }Also applies to: 66-71
63-63: Normalize ISO2 code and use hasOwnProperty for membership check.This avoids false negatives if ids are lowercase and avoids array allocation.
- const isMantecaSupportedCountry = Object.keys(MantecaSupportedExchanges).includes(country.id) + const iso2 = country.id.toUpperCase() + const isMantecaSupportedCountry = Object.prototype.hasOwnProperty.call(MantecaSupportedExchanges, iso2)src/components/Payment/Views/MantecaFulfillment.view.tsx (1)
37-45: Deduplicate argentinaCountryData.This constant is also defined in Claim/Link/MantecaFlowManager. Move to a shared consts module to avoid drift.
src/components/Claim/Link/MantecaFlowManager.tsx (1)
31-39: Deduplicate argentinaCountryData.Same object exists in MantecaFulfillment.view.tsx. Extract to src/components/AddMoney/consts (or similar) and import in both places.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/app/(mobile-ui)/qr-pay/page.tsx(4 hunks)src/app/[...recipient]/client.tsx(1 hunks)src/components/Claim/Link/MantecaFlowManager.tsx(4 hunks)src/components/Claim/Link/views/MantecaReviewStep.tsx(3 hunks)src/components/Common/CountryList.tsx(1 hunks)src/components/Common/CountryListRouter.tsx(2 hunks)src/components/Global/PeanutActionDetailsCard/index.tsx(3 hunks)src/components/Kyc/CountryRegionRow.tsx(1 hunks)src/components/Payment/Views/MantecaFulfillment.view.tsx(5 hunks)src/hooks/useCurrency.ts(1 hunks)src/services/manteca.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/components/Common/CountryList.tsxsrc/components/Global/PeanutActionDetailsCard/index.tsxsrc/components/Common/CountryListRouter.tsx
📚 Learning: 2025-09-15T16:22:58.382Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#1190
File: src/services/manteca.ts:1-1
Timestamp: 2025-09-15T16:22:58.382Z
Learning: In the manteca service, PEANUT_API_KEY should not be imported in a module that's used by client-side components. The getPrices function that requires the API key should be moved to a server-only module to prevent exposing secrets in the client bundle.
Applied to files:
src/services/manteca.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/components/Kyc/CountryRegionRow.tsxsrc/components/Payment/Views/MantecaFulfillment.view.tsxsrc/components/Common/CountryListRouter.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.
Applied to files:
src/components/Claim/Link/MantecaFlowManager.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1132
File: src/app/[...recipient]/client.tsx:394-397
Timestamp: 2025-08-26T15:25:53.328Z
Learning: In `src/components/Common/ActionListDaimoPayButton.tsx`, the `handleCompleteDaimoPayment` function should not display error messages to users when DB update fails because the Daimo payment itself has succeeded - showing errors would be confusing since the payment was successful.
Applied to files:
src/app/[...recipient]/client.tsx
📚 Learning: 2025-06-18T19:56:55.443Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#919
File: src/components/Withdraw/views/Initial.withdraw.view.tsx:87-87
Timestamp: 2025-06-18T19:56:55.443Z
Learning: In withdraw flows for Peanut Wallet, the PeanutActionDetailsCard should always display "USDC" as the token symbol because it shows the amount being withdrawn from the Peanut Wallet (which holds USDC), regardless of the destination token/chain selected by the user. The TokenSelector is used for choosing the withdrawal destination, not the source display.
Applied to files:
src/components/Global/PeanutActionDetailsCard/index.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
🧬 Code graph analysis (6)
src/components/Payment/Views/MantecaFulfillment.view.tsx (7)
src/context/RequestFulfillmentFlowContext.tsx (1)
useRequestFulfillmentFlow(109-115)src/redux/hooks.ts (1)
usePaymentStore(12-12)src/hooks/useKycStatus.tsx (1)
useKycStatus(12-26)src/context/authContext.tsx (1)
useAuth(182-188)src/services/manteca.ts (1)
mantecaApi(95-233)src/components/AddMoney/consts/index.ts (1)
CountryData(153-162)src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
InitiateMantecaKYCModal(23-70)
src/components/Claim/Link/views/MantecaReviewStep.tsx (1)
src/hooks/useCurrency.ts (1)
useCurrency(21-65)
src/components/Claim/Link/MantecaFlowManager.tsx (5)
src/context/ClaimBankFlowContext.tsx (1)
useClaimBankFlow(143-149)src/components/AddMoney/consts/index.ts (1)
CountryData(153-162)src/hooks/useKycStatus.tsx (1)
useKycStatus(12-26)src/context/authContext.tsx (1)
useAuth(182-188)src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
InitiateMantecaKYCModal(23-70)
src/components/Global/PeanutActionDetailsCard/index.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
Icon(196-205)
src/app/(mobile-ui)/qr-pay/page.tsx (1)
src/components/Kyc/KycVerificationInProgressModal.tsx (1)
PeanutDoesntStoreAnyPersonalInformation(52-59)
src/components/Common/CountryListRouter.tsx (3)
src/context/ClaimBankFlowContext.tsx (1)
useClaimBankFlow(143-149)src/context/RequestFulfillmentFlowContext.tsx (1)
useRequestFulfillmentFlow(109-115)src/components/AddMoney/consts/index.ts (2)
CountryData(153-162)MantecaSupportedExchanges(8-19)
🪛 Gitleaks (8.27.2)
src/services/manteca.ts
[high] 180-180: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (12)
src/components/Kyc/CountryRegionRow.tsx (2)
10-12: Visibility rule: confirm intended behavior for bridge flows.Row hides only when
!isBridge && !countryCode. This means it always shows for bridge flows even with an emptycountryCode. Confirm this is desired UX for “bridge region.”
2-2: Import path/export verified — no action required.src/components/Kyc/CountryFlagAndName.tsx exists and declares
export const CountryFlagAndName; the checked legacy import patterns (./Country-Flag-And-Name, ./country-flag-and-name) returned no matches.src/app/(mobile-ui)/qr-pay/page.tsx (2)
32-34: Switch to Global/Card default export — confirm API parity.Card moved to a default export at '@/components/Global/Card'. Ensure this module indeed has a default export with the same props (className, children), and there are no lingering named imports in adjacent code.
278-278: Footer swap to reusable info component looks good.Consistent with KYC UX across the app. No issues spotted.
src/hooks/useCurrency.ts (1)
25-25: Initial loading flip is fine; ensure call sites don't read price/symbol until ready.Starting with isLoading=true is reasonable, but consumers must guard against null price/symbol on first render.
src/app/[...recipient]/client.tsx (1)
396-399: showActionList condition reads clearer and matches intent.Gates out Manteca fulfillment and shows for direct-pay guests only. Looks good.
src/components/Global/PeanutActionDetailsCard/index.tsx (3)
168-169: Regional method inclusion reads well.Adding isRegionalMethodClaim centralizes the condition. Good.
185-189: Bank overlay suppression for regional claims is sensible.Hiding the bank glyph for REGIONAL_METHOD_CLAIM is consistent with the new visual semantics.
199-201: Condition now includes regional claims — correct gate.Showing withdrawBankIcon for REGIONAL_METHOD_CLAIM aligns visuals across flows.
src/components/Common/CountryList.tsx (1)
137-139: Expanding claim-request support to Manteca countries makes sense.This broadens availability as intended. Verify MantecaSupportedExchanges is up to date with backend support.
src/components/Claim/Link/MantecaFlowManager.tsx (1)
53-57: KYC gating flow — looks good.Auto‑opening the KYC modal when not approved is a clean gate; closing on success and refetching user is correct.
Confirm that fetchUser updates the same auth context used by useKycStatus, so isUserMantecaKycApproved flips to true and unblocks downstream flows.
src/components/Payment/Views/MantecaFulfillment.view.tsx (1)
95-97: Fix optional-chaining before toLowerCase to avoid runtime crashselectedCountry?.id.toLowerCase() will throw if selectedCountry is undefined — chain id before calling toLowerCase.
Location: src/components/Payment/Views/MantecaFulfillment.view.tsx:95-97
- countryCodeForFlag={selectedCountry?.id.toLowerCase()} + countryCodeForFlag={selectedCountry?.id?.toLowerCase()}
| countryCodeForFlag={selectedCountry?.id.toLowerCase()} | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
Runtime crash risk: optional chaining stops before toLowerCase().
Fix chaining to avoid calling toLowerCase on undefined.
- countryCodeForFlag={selectedCountry?.id.toLowerCase()}
+ countryCodeForFlag={selectedCountry?.id?.toLowerCase()}🏁 Script executed:
#!/bin/bash
rg -nP --type=ts --type=tsx -C1 '\?\.\s*id\.toLowerCase\(\)'Length of output: 27
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search repository for patterns where optional chaining may stop before toLowerCase()
rg -n --hidden -S -C1 '\?\.\s*id\.toLowerCase\(\)' || true
rg -n --hidden -S -C1 'selectedCountry\?\.\s*id\.toLowerCase\(' || true
rg -n --hidden -S -C1 '\b\w+\?\.\s*id\s*\.toLowerCase\(' || true
# Try to locate the specific file from the review and print its contents (first 300 lines)
file=$(rg --files -S | rg 'MantecaFlowManager\.tsx' || true)
if [ -n "$file" ]; then
echo "---- $file (first 300 lines) ----"
sed -n '1,300p' "$file" | nl -ba -v1 -w6 -s': '
else
echo "MantecaFlowManager.tsx not found; listing src/components/Claim/Link if it exists:"
ls -la src/components/Claim/Link || true
fiLength of output: 2995
Fix optional chaining before toLowerCase to prevent runtime crash.
selectedCountry?.id.toLowerCase() can call toLowerCase on undefined — use selectedCountry?.id?.toLowerCase().
- src/components/Claim/Link/MantecaFlowManager.tsx (line 123): countryCodeForFlag={selectedCountry?.id?.toLowerCase()}
- src/components/Payment/Views/MantecaFulfillment.view.tsx (lines 48 and 96): replace selectedCountry?.id.toLowerCase() with selectedCountry?.id?.toLowerCase()
🤖 Prompt for AI Agents
In src/components/Claim/Link/MantecaFlowManager.tsx around lines 123-124
(countryCodeForFlag={selectedCountry?.id.toLowerCase()}), and in
src/components/Payment/Views/MantecaFulfillment.view.tsx around lines 48 and 96,
optional chaining is applied to selectedCountry but not to its id before calling
toLowerCase, which can throw if id is undefined; change each occurrence to use
selectedCountry?.id?.toLowerCase() so the toLowerCase call is only invoked when
id exists, preserving undefined when either selectedCountry or id is missing.
| const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ | ||
| queryKey: ['manteca-deposit', chargeDetails?.uuid], | ||
| queryFn: () => | ||
| mantecaApi.deposit({ | ||
| usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0', | ||
| currency: 'ARS', | ||
| currency: selectedCountry?.currency || 'ARS', | ||
| chargeId: chargeDetails?.uuid, | ||
| }), | ||
| refetchOnWindowFocus: false, | ||
| staleTime: Infinity, // don't refetch the data | ||
| enabled: Boolean(chargeDetails?.uuid), | ||
| enabled: Boolean(chargeDetails?.uuid) && isUserMantecaKycApproved, | ||
| }) |
There was a problem hiding this comment.
React Query key misses currency/amount; deposit details can go stale across country/amount changes.
Include currency and amount in the queryKey (and reuse them in queryFn). Otherwise, with staleTime: Infinity, users may see mismatched account details.
- const { data: depositData, isLoading: isLoadingDeposit } = useQuery({
- queryKey: ['manteca-deposit', chargeDetails?.uuid],
- queryFn: () =>
- mantecaApi.deposit({
- usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0',
- currency: selectedCountry?.currency || 'ARS',
- chargeId: chargeDetails?.uuid,
- }),
+ const currency = (selectedCountry?.currency || 'ARS').toUpperCase()
+ const usdAmount = requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0'
+ const { data: depositData, isLoading: isLoadingDeposit } = useQuery({
+ queryKey: ['manteca-deposit', chargeDetails?.uuid, currency, usdAmount],
+ queryFn: () =>
+ mantecaApi.deposit({
+ usdAmount,
+ currency,
+ chargeId: chargeDetails?.uuid,
+ }),
refetchOnWindowFocus: false,
staleTime: Infinity, // don't refetch the data
enabled: Boolean(chargeDetails?.uuid) && isUserMantecaKycApproved,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ | |
| queryKey: ['manteca-deposit', chargeDetails?.uuid], | |
| queryFn: () => | |
| mantecaApi.deposit({ | |
| usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0', | |
| currency: 'ARS', | |
| currency: selectedCountry?.currency || 'ARS', | |
| chargeId: chargeDetails?.uuid, | |
| }), | |
| refetchOnWindowFocus: false, | |
| staleTime: Infinity, // don't refetch the data | |
| enabled: Boolean(chargeDetails?.uuid), | |
| enabled: Boolean(chargeDetails?.uuid) && isUserMantecaKycApproved, | |
| }) | |
| const currency = (selectedCountry?.currency || 'ARS').toUpperCase() | |
| const usdAmount = requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0' | |
| const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ | |
| queryKey: ['manteca-deposit', chargeDetails?.uuid, currency, usdAmount], | |
| queryFn: () => | |
| mantecaApi.deposit({ | |
| usdAmount, | |
| currency, | |
| chargeId: chargeDetails?.uuid, | |
| }), | |
| refetchOnWindowFocus: false, | |
| staleTime: Infinity, // don't refetch the data | |
| enabled: Boolean(chargeDetails?.uuid) && isUserMantecaKycApproved, | |
| }) |
🤖 Prompt for AI Agents
In src/components/Payment/Views/MantecaFulfillment.view.tsx around lines 24 to
35, the react-query key only uses chargeDetails?.uuid so the deposit result can
become stale when currency or amount change; include the currency and amount in
the queryKey (e.g., selectedCountry?.currency and the resolved token amount) and
ensure the same resolved values are captured/used inside the queryFn (compute
amount and currency in local variables before creating the query and pass them
into the queryFn or inline them) so the key uniquely represents the request and
the returned deposit matches the current country/amount.
| 'api-key': 'DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo', | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
Hardcoded API key in client bundle; rotate immediately and move to server-only.
The literal API key will ship to browsers and is already flagged by Gitleaks. This is a secret exposure and must be removed. Attach the key on the server (e.g., Next.js route/action) and rotate the leaked credential.
Apply this minimal fix here (removes the leaked header):
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${Cookies.get('jwt-token')}`,
- 'api-key': 'DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo',
},Follow-up (outside this hunk):
- Ensure your API (PEANUT_API_URL) adds the required Manteca header from server env: process.env.PEANUT_API_KEY.
- In this repo, proxy client calls through a server-only endpoint and attach the header there.
Security hygiene:
- Revoke/rotate the leaked key in the upstream system.
- Add a pre-commit secret scan (gitleaks) and a CI gate.
Verification script to locate any residual exposures:
🏁 Script executed:
#!/bin/bash
# 1) Find the leaked literal and any similar tokens
rg -n "DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo|api-key'\s*:" -C2
# 2) Find client-side imports/uses of PEANUT_API_KEY (should be server-only)
rg -nP --type=ts '\bPEANUT_API_KEY\b' -C2Length of output: 29693
Hardcoded PEANUT API key found — remove it, rotate the key, and ensure server-only usage.
Literal API key present at src/services/manteca.ts:180 — remove immediately, rotate/revoke the leaked credential, and ensure any code that attaches the PEANUT API header runs only on the server.
Apply this minimal fix (removes the leaked header):
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${Cookies.get('jwt-token')}`,
- 'api-key': 'DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo',
},Quick action items:
- Remove the hardcoded header at src/services/manteca.ts:180 and deploy a secret rotation/revocation for that key.
- Verify src/constants/general.consts.ts:49 (export of PEANUT_API_KEY) and avoid importing PEANUT_API_KEY into modules that are bundled for the client — move secret-using logic to server-only routes (app/api/*) or files marked "use server". Note: src/services/manteca.ts also uses 'api-key': PEANUT_API_KEY at ~line 140 — ensure that usage is server-only.
- Audit other files that reference PEANUT_API_KEY / API_KEY (rg already showed many occurrences under src/app/) and confirm none are included in client bundles.
- Add pre-commit and CI secret scanning (gitleaks) and a deployment/ops step to rotate exposed credentials.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'api-key': 'DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo', | |
| }, | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| Authorization: `Bearer ${Cookies.get('jwt-token')}`, | |
| }, |
🧰 Tools
🪛 Gitleaks (8.27.2)
[high] 180-180: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
In src/services/manteca.ts around lines 180-181 there is a hardcoded PEANUT API
key in the headers; remove that literal header entry immediately and replace
code so the header is populated from a server-only secret (e.g., process.env or
a server-only/“use server” module) or removed entirely, then rotate/revoke the
leaked key; additionally verify src/constants/general.consts.ts export of
PEANUT_API_KEY isn’t imported into client-bundled modules (move any uses to
app/api/* or server-only files), audit other occurrences of PEANUT_API_KEY to
ensure they are server-only, and add pre-commit/CI secret scanning (eg.
gitleaks) and a deployment step to rotate exposed credentials.
No description provided.