Skip to content

[TASK-15725] feat: support waiting for merchant amount#1350

Merged
jjramirezn merged 3 commits intopeanut-wallet-devfrom
feat/ask-merchant-again
Oct 24, 2025
Merged

[TASK-15725] feat: support waiting for merchant amount#1350
jjramirezn merged 3 commits intopeanut-wallet-devfrom
feat/ask-merchant-again

Conversation

@jjramirezn
Copy link
Contributor

Do not merge before prod release

WhatsApp.Video.2025-10-22.at.20.27.08.mp4

@vercel
Copy link

vercel bot commented Oct 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Oct 23, 2025 1:34pm

@jjramirezn jjramirezn requested a review from Hugo0 October 22, 2025 23:30
@notion-workspace
Copy link

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Walkthrough

Adds retry and merchant-amount waiting flow to MANTECA and SIMPLEFI QR payment paths, replaces the "Order Not Ready" modal with a waiting UI and support surface, removes iconClassName from DirectSendQr, alters QrCodeIcon SVG rendering, adjusts PeanutLoading sizing, and deletes RewardsCardModal.

Changes

Cohort / File(s) Summary
QR Payment Flow
src/app/(mobile-ui)/qr-pay/page.tsx
Adds state for shouldRetry, waitingForMerchantAmount, and retryCount; integrates useSupportModalContext; gates MANTECA fetch on shouldRetry; implements automatic SIMPLEFI retry loop (up to 3 attempts, delays); replaces "Order Not Ready" modal with waiting UI and support action; renames handleOrderNotReadyRetryhandleSimplefiRetry.
DirectSendQr & Icon
src/components/Global/DirectSendQR/index.tsx, src/components/Global/Icons/qr-code.tsx
DirectSendQr: removes public iconClassName prop, fixes icon class to "custom-size h-full", updates button padding from p-0p-3. QrCodeIcon: stops spreading all props onto <svg>, uses only className, changes viewBox to 0 0 18 18, and replaces multiple paths with a single <path> definition.
UI Small Changes & Removal
src/components/Global/PeanutLoading/index.tsx, src/components/Home/RewardsCardModal.tsx
PeanutLoading: changes logo height from responsive h-6 sm:h-10 → fixed h-10. RewardsCardModal: deletes the component and its props/interface.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jjramirezn
  • kushagrasarathe

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "[TASK-15725] feat: support waiting for merchant amount" directly and clearly describes the primary objective of the changeset. The main change across the altered files centers on the QR payment flow in src/app/(mobile-ui)/qr-pay/page.tsx, which introduces waiting state management, retry logic, and a new waiting UI specifically for the merchant amount scenario. The title is specific, concise, and accurately reflects this primary change without unnecessary noise, making it immediately clear to a teammate reviewing the commit history.
Description Check ✅ Passed The pull request description, while minimal, is related to the changeset. It includes a note stating "Do not merge before prod release" which indicates a deliberate release constraint for this feature, and provides a link to an asset that appears to demonstrate the waiting UI functionality. Although the description lacks detailed technical explanation, it is not completely off-topic; it conveys information about the release timing for this feature and provides a visual reference that relates to the changeset's primary objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ask-merchant-again

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Oct 22, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/components/Global/PeanutLoading/index.tsx (1)

14-14: Prevent potential CLS: set intrinsic size (or use next/image).

Fixed height without width can cause layout shift; also “logo” alt is generic. Prefer explicit intrinsic size or next/image for consistency.

Apply one of:

- <img src={PEANUTMAN_LOGO.src} alt="logo" className="h-10" />
+ <img src={PEANUTMAN_LOGO.src} alt="Peanut logo" width={40} height={40} className="h-10 w-10" />

or

+ import Image from 'next/image'
...
- <img src={PEANUTMAN_LOGO.src} alt="logo" className="h-10" />
+ <Image src={PEANUTMAN_LOGO} alt="Peanut logo" width={40} height={40} className="h-10 w-10" priority />

Please confirm which pattern you prefer across Global components for icon/logo images.

src/components/Global/DirectSendQR/index.tsx (1)

408-413: Icon sizing: rely on standard Tailwind; avoid app‑specific class.

Use w-full with h-full for predictable scaling inside the 5rem button. Also confirm custom-size exists.

- className={twMerge(
-     'mx-auto h-20 w-20 cursor-pointer justify-center rounded-full p-3 hover:bg-primary-1/100',
-     className
- )}
+ className={twMerge(
+     'mx-auto h-20 w-20 cursor-pointer justify-center rounded-full p-3 hover:bg-primary-1/100',
+     className
+ )}
...
- <Icon name={icon} className="custom-size h-full" />
+ <Icon name={icon} className="h-full w-full" />

If other consumers depended on the removed iconClassName prop, confirm all call sites were updated.

src/app/(mobile-ui)/qr-pay/page.tsx (2)

731-744: Auto-retry timer lacks cleanup; can update unmounted state.

Store the timeout id and clear it in a cleanup to avoid leaks and stale retries.

- useEffect(() => {
-     if (waitingForMerchantAmount && !shouldRetry) {
-         if (retryCount.current < 3) {
-             retryCount.current++
-             setTimeout(() => {
-                 setShouldRetry(true)
-             }, 3000)
-         } else {
-             setWaitingForMerchantAmount(false)
-             setShowOrderNotReadyModal(true)
-         }
-     }
- }, [waitingForMerchantAmount, shouldRetry])
+ useEffect(() => {
+     let t: ReturnType<typeof setTimeout> | undefined
+     if (waitingForMerchantAmount && !shouldRetry) {
+         if (retryCount.current < 3) {
+             retryCount.current++
+             t = setTimeout(() => setShouldRetry(true), 3000)
+         } else {
+             setWaitingForMerchantAmount(false)
+             setShowOrderNotReadyModal(true)
+         }
+     }
+     return () => {
+         if (t) clearTimeout(t)
+     }
+ }, [waitingForMerchantAmount, shouldRetry])

836-848: Next/Image intrinsic size mismatch; likely blur/CLS.

width/height are 20 but class sets 128px; set intrinsic size to match rendered size (or use sizes/fill).

- <Image
-     src={chillPeanutAnim.src}
-     alt="Peanut Mascot"
-     width={20}
-     height={20}
-     className="absolute z-0 h-32 w-32 -translate-y-20 translate-x-26"
- />
+ <Image
+     src={chillPeanutAnim.src}
+     alt="Peanut Mascot"
+     width={128}
+     height={128}
+     className="absolute z-0 h-32 w-32 -translate-y-20 translate-x-26"
+     priority
+ />

Confirm translate-x-26 exists in your Tailwind scale; otherwise use arbitrary value e.g. translate-x-[6.5rem].

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8815921 and d6972eb.

📒 Files selected for processing (5)
  • src/app/(mobile-ui)/qr-pay/page.tsx (6 hunks)
  • src/components/Global/DirectSendQR/index.tsx (1 hunks)
  • src/components/Global/Icons/qr-code.tsx (1 hunks)
  • src/components/Global/PeanutLoading/index.tsx (1 hunks)
  • src/components/Home/RewardsCardModal.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Home/RewardsCardModal.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/Global/DirectSendQR/index.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (206-215)
src/app/(mobile-ui)/qr-pay/page.tsx (3)
src/context/SupportModalContext.tsx (1)
  • useSupportModalContext (22-28)
src/services/manteca.ts (1)
  • mantecaApi (102-290)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (206-215)

Comment on lines +4 to +6
<svg className={props.className} viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M0.337891 10.6832H10.6854V0.335693H0.337891V10.6832ZM2.92477 2.92257H8.09852V8.09632H2.92477V2.92257Z"
d="M12 18H10V16H12V18ZM10 11H8V16H10V11ZM18 9H16V13H18V9ZM16 7H14V9H16V7ZM4 9H2V11H4V9ZM2 7H0V9H2V7ZM9 2H11V0H9V2ZM1.5 1.5V4.5H4.5V1.5H1.5ZM5 6H1C0.45 6 0 5.55 0 5V1C0 0.45 0.45 0 1 0H5C5.55 0 6 0.45 6 1V5C6 5.55 5.55 6 5 6ZM1.5 13.5V16.5H4.5V13.5H1.5ZM5 18H1C0.45 18 0 17.55 0 17V13C0 12.45 0.45 12 1 12H5C5.55 12 6 12.45 6 13V17C6 17.55 5.55 18 5 18ZM13.5 1.5V4.5H16.5V1.5H13.5ZM17 6H13C12.45 6 12 5.55 12 5V1C12 0.45 12.45 0 13 0H17C17.55 0 18 0.45 18 1V5C18 5.55 17.55 6 17 6ZM16 16V13H12V15H14V18H18V16H16ZM14 9H10V11H14V9ZM10 7H4V9H6V11H8V9H10V7ZM11 6V4H9V2H7V6H11ZM3.75 2.25H2.25V3.75H3.75V2.25ZM3.75 14.25H2.25V15.75H3.75V14.25ZM15.75 2.25H14.25V3.75H15.75V2.25Z"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

A11Y and theming regression: props dropped; hardcoded black.

Removing props spread breaks aria/role propagation; fill="black" blocks theming via currentColor. Restore prop forwarding and use currentColor.

-export const QrCodeIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
-    <svg className={props.className} viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+export const QrCodeIcon: FC<SVGProps<SVGSVGElement>> = ({ className, ...rest }) => (
+    <svg
+        className={className}
+        viewBox="0 0 18 18"
+        xmlns="http://www.w3.org/2000/svg"
+        aria-hidden={rest['aria-label'] ? undefined : true}
+        focusable="false"
+        {...rest}
+    >
         <path
-            d="M12 18H10V16H12V18ZM10 11H8V16H10V11ZM18 9H16V13H18V9ZM16 7H14V9H16V7ZM4 9H2V11H4V9ZM2 7H0V9H2V7ZM9 2H11V0H9V2ZM1.5 1.5V4.5H4.5V1.5H1.5ZM5 6H1C0.45 6 0 5.55 0 5V1C0 0.45 0.45 0 1 0H5C5.55 0 6 0.45 6 1V5C6 5.55 5.55 6 5 6ZM1.5 13.5V16.5H4.5V13.5H1.5ZM5 18H1C0.45 18 0 17.55 0 17V13C0 12.45 0.45 12 1 12H5C5.55 12 6 12.45 6 13V17C6 17.55 5.55 18 5 18ZM13.5 1.5V4.5H16.5V1.5H13.5ZM17 6H13C12.45 6 12 5.55 12 5V1C12 0.45 12.45 0 13 0H17C17.55 0 18 0.45 18 1V5C18 5.55 17.55 6 17 6ZM16 16V13H12V15H14V18H18V16H16ZM14 9H10V11H14V9ZM10 7H4V9H6V11H8V9H10V7ZM11 6V4H9V2H7V6H11ZM3.75 2.25H2.25V3.75H3.75V2.25ZM3.75 14.25H2.25V15.75H3.75V14.25ZM15.75 2.25H14.25V3.75H15.75V2.25Z"
-            fill="black"
+            d="M12 18H10V16H12V18ZM10 11H8V16H10V11ZM18 9H16V13H18V9ZM16 7H14V9H16V7ZM4 9H2V11H4V9ZM2 7H0V9H2V7ZM9 2H11V0H9V2ZM1.5 1.5V4.5H4.5V1.5H1.5ZM5 6H1C0.45 6 0 5.55 0 5V1C0 0.45 0.45 0 1 0H5C5.55 0 6 0.45 6 1V5C6 5.55 5.55 6 5 6ZM1.5 13.5V16.5H4.5V13.5H1.5ZM5 18H1C0.45 18 0 17.55 0 17V13C0 12.45 0.45 12 1 12H5C5.55 12 6 12.45 6 13V17C6 17.55 5.55 18 5 18ZM13.5 1.5V4.5H16.5V1.5H13.5ZM17 6H13C12.45 6 12 5.55 12 5V1C12 0.45 12.45 0 13 0H17C17.55 0 18 0.45 18 1V5C18 5.55 17.55 6 17 6ZM16 16V13H12V15H14V18H18V16H16ZM14 9H10V11H14V9ZM10 7H4V9H6V11H8V9H10V7ZM11 6V4H9V2H7V6H11ZM3.75 2.25H2.25V3.75H3.75V2.25ZM3.75 14.25H2.25V15.75H3.75V14.25ZM15.75 2.25H14.25V3.75H15.75V2.25Z"
+            fill="currentColor"
         />
     </svg>
 )
📝 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.

Suggested change
<svg className={props.className} viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M0.337891 10.6832H10.6854V0.335693H0.337891V10.6832ZM2.92477 2.92257H8.09852V8.09632H2.92477V2.92257Z"
d="M12 18H10V16H12V18ZM10 11H8V16H10V11ZM18 9H16V13H18V9ZM16 7H14V9H16V7ZM4 9H2V11H4V9ZM2 7H0V9H2V7ZM9 2H11V0H9V2ZM1.5 1.5V4.5H4.5V1.5H1.5ZM5 6H1C0.45 6 0 5.55 0 5V1C0 0.45 0.45 0 1 0H5C5.55 0 6 0.45 6 1V5C6 5.55 5.55 6 5 6ZM1.5 13.5V16.5H4.5V13.5H1.5ZM5 18H1C0.45 18 0 17.55 0 17V13C0 12.45 0.45 12 1 12H5C5.55 12 6 12.45 6 13V17C6 17.55 5.55 18 5 18ZM13.5 1.5V4.5H16.5V1.5H13.5ZM17 6H13C12.45 6 12 5.55 12 5V1C12 0.45 12.45 0 13 0H17C17.55 0 18 0.45 18 1V5C18 5.55 17.55 6 17 6ZM16 16V13H12V15H14V18H18V16H16ZM14 9H10V11H14V9ZM10 7H4V9H6V11H8V9H10V7ZM11 6V4H9V2H7V6H11ZM3.75 2.25H2.25V3.75H3.75V2.25ZM3.75 14.25H2.25V15.75H3.75V14.25ZM15.75 2.25H14.25V3.75H15.75V2.25Z"
export const QrCodeIcon: FC<SVGProps<SVGSVGElement>> = ({ className, ...rest }) => (
<svg
className={className}
viewBox="0 0 18 18"
xmlns="http://www.w3.org/2000/svg"
aria-hidden={rest['aria-label'] ? undefined : true}
focusable="false"
{...rest}
>
<path
d="M12 18H10V16H12V18ZM10 11H8V16H10V11ZM18 9H16V13H18V9ZM16 7H14V9H16V7ZM4 9H2V11H4V9ZM2 7H0V9H2V7ZM9 2H11V0H9V2ZM1.5 1.5V4.5H4.5V1.5H1.5ZM5 6H1C0.45 6 0 5.55 0 5V1C0 0.45 0.45 0 1 0H5C5.55 0 6 0.45 6 1V5C6 5.55 5.55 6 5 6ZM1.5 13.5V16.5H4.5V13.5H1.5ZM5 18H1C0.45 18 0 17.55 0 17V13C0 12.45 0.45 12 1 12H5C5.55 12 6 12.45 6 13V17C6 17.55 5.55 18 5 18ZM13.5 1.5V4.5H16.5V1.5H13.5ZM17 6H13C12.45 6 12 5.55 12 5V1C12 0.45 12.45 0 13 0H17C17.55 0 18 0.45 18 1V5C18 5.55 17.55 6 17 6ZM16 16V13H12V15H14V18H18V16H16ZM14 9H10V11H14V9ZM10 7H4V9H6V11H8V9H10V7ZM11 6V4H9V2H7V6H11ZM3.75 2.25H2.25V3.75H3.75V2.25ZM3.75 14.25H2.25V15.75H3.75V14.25ZM15.75 2.25H14.25V3.75H15.75V2.25Z"
fill="currentColor"
/>
</svg>
)
🤖 Prompt for AI Agents
In src/components/Global/Icons/qr-code.tsx around lines 4 to 6, the SVG was
changed to drop prop forwarding and uses a hardcoded black fill which breaks
accessibility (aria/role) and theming; restore spreading incoming props onto the
root <svg> (e.g. keep className but add {...props}) so aria, role, title, etc.
propagate, and remove the hardcoded fill="black" on the path (or replace it with
fill="currentColor") so the icon respects CSS color/theming.

})
.catch((error) => {
if (error.message.includes("provider can't decode it")) {
setWaitingForMerchantAmount(true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sad :(

But good on removing dead code

Signed-off-by: Hugo Montenegro <hugo@peanut.to>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/app/(mobile-ui)/qr-pay/page.tsx (2)

390-409: CRITICAL: Reset retryCount on success and avoid brittle error matching.

Two critical issues remain from previous reviews:

  1. retryCount not reset on success: Line 398 should reset retryCount.current = 0 when payment lock succeeds. Without this, subsequent "Scan again" attempts will immediately hit the retry cap.

  2. Brittle error string matching (line 401): Matching error.message.includes("provider can't decode it") is fragile. If the API message changes, this logic breaks.

Apply this diff to fix both issues:

 .then((pl) => {
     setWaitingForMerchantAmount(false)
+    retryCount.current = 0
     setPaymentLock(pl)
 })
 .catch((error) => {
-    if (error.message.includes("provider can't decode it")) {
+    // TODO: Use typed error codes from API instead of string matching
+    if (error.message && typeof error.message === 'string' && error.message.includes("provider can't decode it")) {
         setWaitingForMerchantAmount(true)
     } else {
         setErrorInitiatingPayment(error.message)
         setWaitingForMerchantAmount(false)
     }
 })

910-913: CRITICAL: "Scan again" must reset retryCount.

Line 912 enables retry but doesn't reset retryCount.current. If the user previously exhausted 3 retries, clicking "Scan again" will immediately show this modal again.

Apply this diff:

 onClick={() => {
     setShowOrderNotReadyModal(false)
+    retryCount.current = 0
     setShouldRetry(true)
 }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6972eb and 9efc9c4.

📒 Files selected for processing (1)
  • src/app/(mobile-ui)/qr-pay/page.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-15T14:47:26.891Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#857
File: src/hooks/useWebSocket.ts:77-82
Timestamp: 2025-05-15T14:47:26.891Z
Learning: The useWebSocket hook in src/hooks/useWebSocket.ts is designed to provide raw history entries, while the components using it (such as HomeHistory.tsx) are responsible for implementing deduplication logic based on UUID to prevent duplicate entries when combining WebSocket data with other data sources.

Applied to files:

  • src/app/(mobile-ui)/qr-pay/page.tsx
🧬 Code graph analysis (1)
src/app/(mobile-ui)/qr-pay/page.tsx (3)
src/context/SupportModalContext.tsx (1)
  • useSupportModalContext (39-45)
src/services/manteca.ts (1)
  • mantecaApi (102-290)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (206-215)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Deploy-Preview
🔇 Additional comments (4)
src/app/(mobile-ui)/qr-pay/page.tsx (4)

46-47: LGTM! Clean context and state additions.

The new imports and state variables are well-structured for the merchant-amount waiting and retry features.

Also applies to: 89-92


767-772: LGTM! SimpleFi retry effect is correctly structured.

The retry trigger logic is clean and follows the same pattern as the MANTECA flow.


774-786: LGTM! Auto-retry logic with exponential backoff is well-implemented.

The 3-retry limit with 3-second delays provides a good balance between user experience and server load. The fallback to showing the support modal after retries is user-friendly.


919-925: LGTM! Support button integration is user-friendly.

The "Having trouble?" option provides a clear escape hatch when automatic retries fail. Good UX addition.

@cursor
Copy link

cursor bot commented Oct 23, 2025

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: SimpleFi Retry Logic Fails on Payment Processor Change

The useEffect handling SimpleFi retry logic is missing paymentProcessor from its dependency array. This means the effect might not re-run when the payment processor changes, potentially preventing the SimpleFi retry mechanism from activating as expected.

Fix in Cursor Fix in Web

@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: CTA Title Missing in DirectSendQr Button

The DirectSendQr component no longer renders the ctaTitle prop in its button, even though the prop is still accepted. This breaks existing functionality for callers expecting the title to be displayed, as the text will not appear.

Fix in Cursor Fix in Web

@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: Persistent Retry Count Blocks QR Scans

The retryCount ref isn't reset. Once it hits 3 attempts, it stays at that value, which prevents any future QR scans from retrying and immediately shows the "Order Not Ready" modal.

Fix in Cursor Fix in Web

@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: Unintended Retries Due to Callback Dependency

The useEffect depends on handleSimplefiRetry which is a callback defined with useCallback. This creates a dependency issue because handleSimplefiRetry is recreated on every render due to its dependencies (simpleFiQrData, setLoadingState). This means the effect will run every time handleSimplefiRetry changes, not just when shouldRetry changes from false to true. This could cause unintended retries. The effect should only depend on shouldRetry and call the retry function directly, or handleSimplefiRetry should be wrapped in a ref or the effect should use exhaustive dependencies correctly.

Fix in Cursor Fix in Web

@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: Retry Logic Fails to Clean Up Timeout

The retry logic has a race condition. When waitingForMerchantAmount becomes true and shouldRetry is false, it schedules a retry after 3 seconds by calling setShouldRetry(true). However, there's no cleanup of this timeout if the component unmounts or if waitingForMerchantAmount changes before the 3 seconds elapse. This could lead to state updates on an unmounted component or retries happening when they shouldn't. The setTimeout should return a cleanup function in the useEffect.

Fix in Cursor Fix in Web

@jjramirezn jjramirezn merged commit 16b3c92 into peanut-wallet-dev Oct 24, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants