Skip to content

fixes for prod release#1349

Merged
Hugo0 merged 2 commits intopeanut-wallet-devfrom
fix/prodreleasefixes
Oct 22, 2025
Merged

fixes for prod release#1349
Hugo0 merged 2 commits intopeanut-wallet-devfrom
fix/prodreleasefixes

Conversation

@Hugo0
Copy link
Contributor

@Hugo0 Hugo0 commented Oct 22, 2025

Getting ready for prod. Various bug fixes attempted here.

@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 22, 2025 11:16pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Warning

Rate limit exceeded

@Hugo0 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 3 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a6f5f97 and 9a11dbc.

📒 Files selected for processing (5)
  • src/app/(mobile-ui)/history/page.tsx (3 hunks)
  • src/app/(mobile-ui)/qr-pay/page.tsx (4 hunks)
  • src/components/Claim/Claim.tsx (1 hunks)
  • src/components/Home/HomeHistory.tsx (2 hunks)
  • src/components/Payment/PaymentForm/index.tsx (3 hunks)

Walkthrough

WebSocket-injected history entries are now passed through the async utility completeHistoryEntry before deduplication, caching, and UI updates across multiple pages/components; error handling and fallbacks were added, and some payment view logic and retry policy were adjusted.

Changes

Cohort / File(s) Change Summary
History page & QR-pay WebSocket handling
src/app/(mobile-ui)/history/page.tsx, src/app/(mobile-ui)/qr-pay/page.tsx
Import and await completeHistoryEntry for incoming WebSocket history entries. Use completedEntry (including uuid, status, formatted amounts, currency fields) for deduplication, cache updates, logging, and downstream state. Added error handling/fallbacks and Sentry logging in QR-pay; handleSimpleFiStatusUpdate is now async and uses completed fields for payment population.
Home history component
src/components/Home/HomeHistory.tsx
Refactored WebSocket entry processing to asynchronously call completeHistoryEntry per entry (with error fallback), iterate in timestamp order, update/insert by completedEntry.uuid, include KYC items before final sorting, and move final sort/slice to after enrichment. Added isSameUser to effect dependencies.
Payment form / store usage
src/components/Payment/PaymentForm/index.tsx
usePaymentStore() usage updated to read new currentView field; balance validation now skips when currentView is CONFIRM or STATUS and the effect depends on currentView.
Claim retry policy
src/components/Claim/Claim.tsx
Adjusted TanStack Query retry policy for sendLink from 3 linear retries to 4 retries with exponential backoff (1s, 2s, 4s, 8s) and a cap on delay.

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, 1 inconclusive)
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.
Title Check ❓ Inconclusive The pull request title "fixes for prod release" is vague and generic, using non-descriptive terminology that does not convey meaningful information about the specific changes in the changeset. The actual changes involve significant modifications to history entry processing (adding async completeHistoryEntry transformation), WebSocket handling across multiple pages, and changes to the public API of usePaymentStore(). None of these specific details are captured in the title, making it unclear what the primary focus of the PR is. Consider revising the title to be more specific and descriptive. For example, a title like "Standardize history entry processing through completeHistoryEntry utility" or "Async processing of WebSocket history entries for consistency" would better reflect the core changes across the multiple modified files and help teammates quickly understand the primary purpose of this PR.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The pull request description "Getting ready for prod. Various bug fixes attempted here." is related to the changeset in that it references bug fixes for production, and the actual changes do involve fixes and improvements to entry processing and validation logic. Although the description is vague and lacks specific details about which bugs are being fixed or what components are affected, the evaluation criteria for this check explicitly states that level of detail is not important—only that the description is related in some way to the changeset.

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
@Hugo0
Copy link
Contributor Author

Hugo0 commented Oct 22, 2025

@coderabbitai Review carefully. Do these tackle actual bugs? Is there any new unexpected behavior introduced or new risks to be considered?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details including files changed
gh pr view 1349 --json title,body,files,additions,deletions --repo peanutprotocol/peanut-ui

echo "---DIFF---"
# Get the diff for the PR
gh pr diff 1349 --repo peanutprotocol/peanut-ui

Length of output: 14258


🏁 Script executed:

#!/bin/bash
# Find and examine the completeHistoryEntry utility function
echo "=== Finding completeHistoryEntry function ==="
fd -e ts -e tsx history.utils

echo -e "\n=== Content of history.utils file ==="
cat src/utils/history.utils.ts 2>/dev/null || cat src/utils/history.utils.tsx 2>/dev/null || echo "File not found"

echo -e "\n=== Searching for completeHistoryEntry definition ==="
rg -n "completeHistoryEntry" -A 30 -B 2 --type ts

Length of output: 38497


@Hugo0, I've completed a thorough review of this PR. Here's my analysis:

Actual Bugs Fixed

Bug #1: Inconsistent Amount Formatting Between WebSocket and API Data

Files: history/page.tsx, qr-pay/page.tsx, HomeHistory.tsx

Problem: WebSocket entries were not being processed through completeHistoryEntry, which meant they:

  • Had incorrect decimal formatting (e.g., showing wei instead of human-readable amounts for deposits)
  • Were missing computed fields like extraData.usdAmount and extraData.link
  • Lacked proper currency conversions for bridge transactions

Fix: All WebSocket entries now pass through completeHistoryEntry before display/caching, ensuring consistency with API-fetched entries.

Impact: This was causing display bugs where amounts appeared incorrectly or links were missing for real-time transaction updates.

Bug #2: False "Insufficient Balance" Errors During Payment Flow

File: PaymentForm/index.tsx

Problem: After optimistic balance updates on CONFIRM/STATUS views, the balance check effect would still run and compare the input amount against the reduced balance, showing false errors.

Fix: Skip balance validation when currentView === 'CONFIRM' || currentView === 'STATUS'.

Impact: Users would see incorrect balance errors mid-payment, degrading UX.

⚠️ New Risks Introduced

Risk #1: Missing Error Handling for Async Operations

Severity: HIGH

None of the await completeHistoryEntry() calls have try-catch blocks. The function makes API calls to getCurrencyPrice() for bridge transactions:

// In completeHistoryEntry for BRIDGE_ONRAMP type:
const price = await getCurrencyPrice(entry.currency.code)
usdAmount = (Number(entry.currency.amount) / price.buy).toString()

Impact: If the currency price API fails, WebSocket updates will silently fail, leaving the UI in an inconsistent state.

Recommendation: Add error handling:

const completedEntry = await completeHistoryEntry(newEntry).catch((error) => {
    console.error('[History] Failed to complete entry:', error)
    return newEntry // Fallback to raw entry
})

Risk #2: Sequential Processing in HomeHistory

Severity: MEDIUM

Line 97-108 uses for...of loop with await, processing WebSocket entries sequentially:

for (const wsEntry of sortedWsEntries) {
    const completedEntry = await completeHistoryEntry(wsEntry)
    // ...
}

Impact: If multiple WebSocket entries arrive with bridge transactions (requiring API calls), they'll be processed one-by-one, potentially blocking UI updates.

Recommendation: Consider Promise.all() for parallel processing if ordering isn't critical.

Risk #3: Race Condition in HomeHistory useEffect

Severity: LOW

The processEntries() async function is called but not awaited. If dependencies change while processing is in-flight, stale data could be set:

processEntries() // Not awaited

Impact: Rapid dependency changes could cause outdated entries to overwrite newer data.

Risk #4: Potential Performance Issue

Severity: LOW

Every WebSocket entry for bridge transactions now makes an external API call to fetch currency prices. High transaction volume could cause rate limiting.

💡 Code Quality Observations

  1. Good: Added missing isSameUser dependency to HomeHistory useEffect (Line 143)
  2. Good: Proper duplicate checking in history/page.tsx (Lines 62-69)
  3. Good: Simplified logic in HomeHistory by removing manual field assignments
  4. ⚠️ Note: Changed onHistoryEntry callbacks to async - ensure WebSocket library supports this

Recommendation

The bugs fixed are real and important for production. However, add error handling before deploying:

// Example for history/page.tsx
onHistoryEntry: async (newEntry) => {
    try {
        const completedEntry = await completeHistoryEntry(newEntry)
        // ... rest of logic
    } catch (error) {
        console.error('[History] Error processing WebSocket entry:', error)
        // Use raw entry as fallback or skip update
    }
}

Would you like me to generate the error handling code for all affected files?


🧠 Learnings used
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.

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 (5)
src/app/(mobile-ui)/history/page.tsx (2)

71-84: Trim first page to limit after prepend to prevent unbounded growth.

Prepending can exceed the intended page size (20). Slice to limit after insertion.

Apply this patch:

-                                    entries: [completedEntry, ...page.entries],
+                                    entries: [completedEntry, ...page.entries].slice(0, 20),

Optionally, derive 20 from the same limit used in the query key to avoid drift.


49-49: Reduce console noise for prod.

Downgrade to debug or wrap with env guard to avoid chatty logs in production.

Also applies to: 67-67

src/components/Payment/PaymentForm/index.tsx (2)

188-191: Clear stale errors before skipping balance check on CONFIRM/STATUS.

Returning before dispatch(paymentActions.setError(null)) can leave old errors visible. Clear error first, then return.

Apply:

-        // Skip balance check if on CONFIRM or STATUS view (balance has been optimistically updated)
-        if (currentView === 'CONFIRM' || currentView === 'STATUS') {
-            return
-        }
-
-        dispatch(paymentActions.setError(null))
+        // Skip balance check if on CONFIRM or STATUS view (balance has been optimistically updated)
+        if (currentView === 'CONFIRM' || currentView === 'STATUS') {
+            dispatch(paymentActions.setError(null))
+            return
+        }
+
+        dispatch(paymentActions.setError(null))

Also applies to: 193-193


72-80: Now let me check the actual payment store implementation and how currentView is typed:

Now I need to locate the payment reducer and custom hook to verify the actual typing:

Based on my verification, I have found that the review comment is accurate and points to a valid optional refactoring suggestion. The TPaymentView type already exists in the codebase and string literal unions have no bundle bloat compared to enums. The standard approach in Redux Toolkit is to declare a type for state and create an initial state value that uses that type.

The review comment correctly identifies that using the existing TPaymentView type union for currentView would improve type safety and enable better IDE autocompletion, preventing potential typos when calling setView().


Consider using the TPaymentView type for view state transitions instead of string literals.

The TPaymentView type already exists in src/redux/types/payment.types.ts and covers all view values used throughout the codebase ('INITIAL' | 'CONFIRM' | 'STATUS' | 'PUBLIC_PROFILE'). Using it consistently when initializing payment state and in the custom hook will provide better type safety and IDE autocomplete support.

src/components/Home/HomeHistory.tsx (1)

92-108: Avoid sequential awaits; batch completeHistoryEntry with Promise.all and handle per-entry errors.

Sequential processing can stall rendering under backlogs. Process in parallel, skip failures, then merge.

Apply:

-                // Process WebSocket entries through completeHistoryEntry to format amounts correctly
-                for (const wsEntry of sortedWsEntries) {
-                    const completedEntry = await completeHistoryEntry(wsEntry)
-                    const existingIndex = entries.findIndex((entry) => entry.uuid === completedEntry.uuid)
-
-                    if (existingIndex !== -1) {
-                        // update existing entry with latest websocket data
-                        entries[existingIndex] = completedEntry
-                    } else {
-                        // add new entry if it doesn't exist
-                        entries.push(completedEntry)
-                    }
-                }
+                // Normalize WS entries in parallel, skipping failures
+                const normalized = await Promise.all(
+                    sortedWsEntries.map(async (wsEntry) => {
+                        try {
+                            return await completeHistoryEntry(wsEntry)
+                        } catch (e) {
+                            Sentry.captureException(e)
+                            return null
+                        }
+                    })
+                )
+                for (const completedEntry of normalized) {
+                    if (!completedEntry) continue
+                    const idx = entries.findIndex((entry) => entry.uuid === completedEntry.uuid)
+                    if (idx !== -1) entries[idx] = completedEntry
+                    else entries.push(completedEntry)
+                }

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6f5f97 and 02c7501.

📒 Files selected for processing (4)
  • src/app/(mobile-ui)/history/page.tsx (3 hunks)
  • src/app/(mobile-ui)/qr-pay/page.tsx (4 hunks)
  • src/components/Home/HomeHistory.tsx (2 hunks)
  • src/components/Payment/PaymentForm/index.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
📚 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)/history/page.tsx
  • src/components/Home/HomeHistory.tsx
  • src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-04-11T11:33:53.245Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#798
File: src/components/Home/HomeHistory.tsx:138-192
Timestamp: 2025-04-11T11:33:53.245Z
Learning: In the HomeHistory component, infinite scrolling is intentionally not implemented despite the presence of useInfiniteQuery and IntersectionObserver code. The component is designed to only display the first 5 entries with a "View all transactions" link for viewing the complete history.

Applied to files:

  • src/app/(mobile-ui)/history/page.tsx
  • src/components/Home/HomeHistory.tsx
🧬 Code graph analysis (4)
src/app/(mobile-ui)/history/page.tsx (3)
src/utils/history.utils.ts (1)
  • completeHistoryEntry (200-291)
src/hooks/useTransactionHistory.ts (1)
  • HistoryResponse (17-21)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
src/components/Home/HomeHistory.tsx (2)
src/hooks/useBridgeKycFlow.ts (1)
  • KycHistoryEntry (17-23)
src/utils/history.utils.ts (1)
  • completeHistoryEntry (200-291)
src/app/(mobile-ui)/qr-pay/page.tsx (1)
src/utils/history.utils.ts (2)
  • HistoryEntry (89-131)
  • completeHistoryEntry (200-291)
src/components/Payment/PaymentForm/index.tsx (1)
src/redux/hooks.ts (1)
  • usePaymentStore (12-12)
🔇 Additional comments (1)
src/app/(mobile-ui)/history/page.tsx (1)

56-58: Cache key parity is correct—no issues found.

The useTransactionHistory hook called with mode: 'infinite' and limit: 20 (lines 40-43) produces the query key [TRANSACTIONS, 'infinite', { limit: 20 }], which matches exactly the cache key in setQueryData (line 57). The 'infinite' mode intentionally excludes username flags (only the 'public' mode includes username); this aligns with the hook's design for querying the current user's own transaction history.

@Hugo0
Copy link
Contributor Author

Hugo0 commented Oct 22, 2025

@coderabbitai Review the entire PR again with risk analysis.

@Hugo0 Hugo0 merged commit d003c94 into peanut-wallet-dev Oct 22, 2025
2 of 5 checks passed
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get complete PR details
echo "=== PR Details ==="
gh pr view 1349 --json title,body,files,additions,deletions,commits --repo peanutprotocol/peanut-ui

echo -e "\n=== Full PR Diff ==="
gh pr diff 1349 --repo peanutprotocol/peanut-ui

echo -e "\n=== Get completeHistoryEntry implementation ==="
cat src/utils/history.utils.ts

echo -e "\n=== Get usePaymentStore hook implementation ==="
rg -n "currentView" -B 5 -A 5 --type ts -g "!node_modules" -g "!.next" | head -100

echo -e "\n=== Check for any WebSocket usage patterns ==="
rg -n "onHistoryEntry" -B 3 -A 10 --type tsx --type ts -g "!node_modules"

Length of output: 37401


@Hugo0, I've completed a comprehensive re-review with deep risk analysis. Here's my complete assessment:


🔴 CRITICAL ISSUES

Issue #1: Race Condition in HomeHistory.tsx

Severity: CRITICAL
Lines: 85, 143

// Line 85: Function is called but NOT awaited
processEntries()

Problem: The async processEntries() function is fire-and-forget. If the useEffect dependencies change while completeHistoryEntry calls are in-flight (especially for bridge transactions that fetch currency prices), stale data will overwrite fresh data via setCombinedEntries.

Attack Vector: User receives multiple WebSocket entries → first call starts processing → dependencies change → second call starts → first call finishes and sets stale state.

Fix Required:

useEffect(() => {
    if (!isLoading && historyData?.entries) {
        let cancelled = false
        const processEntries = async () => {
            // ... processing logic
            if (!cancelled) {
                setCombinedEntries(entries.slice(0, isPublic ? 20 : 5))
            }
        }
        processEntries()
        return () => { cancelled = true }
    }
}, [historyData, wsHistoryEntries, isPublic, user, isLoading, isSameUser])

Issue #2: Unhandled API Failures in completeHistoryEntry

Severity: HIGH
Location: src/utils/history.utils.ts

The completeHistoryEntry function calls getCurrencyPrice() for bridge transactions (lines handling BRIDGE_ONRAMP, BRIDGE_OFFRAMP, etc.) with NO internal error handling:

// From completeHistoryEntry - NO try-catch here
const price = await getCurrencyPrice(entry.currency.code)
usdAmount = (Number(entry.currency.amount) / price.buy).toString()

Impact: If the currency price API fails (rate limit, network error, invalid currency code), the entire function throws. While callers in history/page.tsx and qr-pay/page.tsx now have try-catch, HomeHistory.tsx's try-catch only logs errors and falls back to raw entry - which defeats the purpose of the fix.

Recommendation: Add defensive error handling inside completeHistoryEntry:

case EHistoryEntryType.BRIDGE_ONRAMP: {
    tokenSymbol = entry.tokenSymbol
    usdAmount = entry.amount.toString()
    if (entry.currency?.code) {
        entry.currency.code = entry.currency.code.toUpperCase()
    }
    if (usdAmount === entry.currency?.amount && entry.currency?.code && entry.currency?.code !== 'USD') {
        try {
            const price = await getCurrencyPrice(entry.currency.code)
            usdAmount = (Number(entry.currency.amount) / price.buy).toString()
        } catch (error) {
            console.error('[completeHistoryEntry] Failed to fetch currency price:', error)
            // Fallback: use original amount as USD approximation
            usdAmount = entry.amount.toString()
        }
    }
    break
}

⚠️ HIGH-RISK ISSUES

Issue #3: Fallback Data Quality in Error Handlers

Severity: HIGH
Files: history/page.tsx (lines 54-68), HomeHistory.tsx (lines 100-114)

When completeHistoryEntry fails, the fallback creates entries with:

completedEntry = {
    ...newEntry,
    timestamp: new Date(newEntry.timestamp),
    extraData: {
        ...newEntry.extraData,
        usdAmount: newEntry.amount.toString(), // ⚠️ This is incorrect for DEPOSIT entries
    },
}

Problem: For DEPOSIT transactions, newEntry.amount is in wei (raw blockchain units). The fallback converts it to string without formatUnits, so amounts will display as massive numbers (e.g., "1000000" instead of "1.00").

Impact: Users see incorrect amounts for failed WebSocket deposits, which could cause panic or confusion.

Fix: Conditionally format based on entry type:

let fallbackAmount = newEntry.amount.toString()
if (newEntry.type === 'DEPOSIT' && newEntry.extraData?.blockNumber) {
    try {
        fallbackAmount = formatUnits(BigInt(newEntry.amount), PEANUT_WALLET_TOKEN_DECIMALS)
    } catch {
        fallbackAmount = '0.00' // Safest fallback
    }
}

Issue #4: qr-pay Approval Flow Guards

Severity: MEDIUM-HIGH
File: qr-pay/page.tsx (lines 176-198)

The new guards for missing currency or simpleFiPayment data on approval are good defensive programming, but they hide a deeper issue: Why would these ever be missing on approval?

Questions:

  1. Can SimpleFi send an approved status without currency data?
  2. Can simpleFiPayment be null at this point in the flow?

If these are impossible states, the guards are masking bugs in state management. If they're possible, the error messages should guide users to support, not just say "check your history."

Recommendation: Add telemetry to understand if these branches ever execute:

if (!completedEntry.currency?.code || !completedEntry.currency?.amount) {
    console.error('[SimpleFi WebSocket] UNEXPECTED: Currency data missing on approval')
    Sentry.captureException(new Error('SimpleFi approved without currency - data integrity issue'), {
        extra: { 
            entryUuid: completedEntry.uuid,
            hasExtraData: !!completedEntry.extraData,
            status: completedEntry.status 
        },
        level: 'error',
    })
    // ... existing error handling
}

⚠️ MEDIUM-RISK ISSUES

Issue #5: Sequential Processing Performance

Severity: MEDIUM
File: HomeHistory.tsx (lines 97-108)

for (const wsEntry of sortedWsEntries) {
    const completedEntry = await completeHistoryEntry(wsEntry)
    // ...
}

Problem: Each bridge transaction waits for the previous one to complete its API call. With 5 bridge entries, this could take 5+ seconds sequentially vs. <1s in parallel.

Impact: UI feels sluggish when multiple transactions arrive.

Solution: Use Promise.allSettled for parallel processing with error resilience:

const completionPromises = sortedWsEntries.map(async (wsEntry) => {
    try {
        return await completeHistoryEntry(wsEntry)
    } catch (error) {
        console.error('[HomeHistory] Failed to process WebSocket entry:', error)
        // Return fallback entry
        return { ...wsEntry, /* fallback processing */ }
    }
})

const completedResults = await Promise.allSettled(completionPromises)
completedResults.forEach((result) => {
    if (result.status === 'fulfilled') {
        const completedEntry = result.value
        // ... existing logic to add/update entries
    }
})

Issue #6: Claim Retry Strategy Change

Severity: MEDIUM (UX Impact)
File: Claim.tsx (lines 86-87)

Before: 3 retries with linear backoff (1s, 2s, 3s) = ~6s total
After: 4 retries with exponential backoff (1s, 2s, 4s, 8s) = ~15s total

Trade-off Analysis:

  • Pro: Better handles DB replication lag and blockchain indexing delays
  • ⚠️ Con: Users wait 2.5x longer before seeing an error
  • ⚠️ Con: The exponential delay formula Math.min(1000 * 2 ** attemptIndex, 10000) produces: 1s, 2s, 4s, 8s (total 15s), but the cap at 10s never applies since 8s < 10s

Questions:

  1. What's the P99 latency for DB replication in production?
  2. Are 15 seconds of retries justified, or should we show a "still loading" message after 5s?

Recommendation: Add user feedback during retries:

onError: (error, attemptIndex) => {
    if (attemptIndex > 1) {
        toast.info('Still loading your link... Please wait.')
    }
}

🟡 LOW-RISK ISSUES

Issue #7: Async WebSocket Callbacks

Severity: LOW
Files: All WebSocket handlers

The WebSocket onHistoryEntry callbacks are now async, but not all WebSocket libraries properly handle async callbacks (they may not await them).

Verification Needed: Check useWebSocket implementation to ensure it handles async callbacks:

// If the library does this:
if (onHistoryEntry) {
    onHistoryEntry(entry) // ❌ Won't wait for async
}

// It should do this:
if (onHistoryEntry) {
    await onHistoryEntry(entry) // ✅ Waits for completion
}

ACTUAL BUGS FIXED

Bug #1: Amount Formatting Inconsistency

Impact: HIGH - Production data integrity issue
Files: All WebSocket handlers

Problem Fixed: WebSocket entries weren't processed through completeHistoryEntry, causing:

  • DEPOSIT entries showing wei instead of formatted amounts (1000000 instead of 1.00)
  • Missing extraData.usdAmount for calculations
  • Missing extraData.link for navigation
  • Incorrect currency conversions for bridge transactions

Verification: ✅ Fix is correct, but see error handling issues above.

Bug #2: False Balance Errors

Impact: HIGH - Blocked user payments
File: PaymentForm/index.tsx

Problem Fixed: After payment submission, optimistic balance updates reduced the displayed balance. The validation effect would then compare the input amount against the reduced balance and show "insufficient balance" errors mid-transaction.

Solution: Skip validation on CONFIRM/STATUS views where balance is already committed.

Verification: ✅ Fix is correct. Added currentView to dependencies properly.


📊 RISK SUMMARY MATRIX

Risk Severity Likelihood Impact Mitigation Required
Race condition in HomeHistory CRITICAL High UI shows stale/wrong data YES - Add cancellation token
getCurrencyPrice failure HIGH Medium Transaction display breaks YES - Add error handling
Fallback data shows wei amounts HIGH Medium Users see wrong amounts YES - Format fallback amounts
SimpleFi guards hide bugs MEDIUM Low Masks state management issues MAYBE - Add telemetry
Sequential processing slow MEDIUM High Sluggish UI with multiple txs OPTIONAL - Parallel processing
Long retry delays MEDIUM High User waits 15s for errors OPTIONAL - Add user feedback
Async callback support LOW Low Callbacks might not complete VERIFY - Check useWebSocket impl

🎯 RECOMMENDATIONS

Must Fix Before Production:

  1. Fix HomeHistory race condition (CRITICAL)
  2. Add error handling to getCurrencyPrice calls (HIGH)
  3. Fix fallback amount formatting for DEPOSIT entries (HIGH)

Should Fix:

  1. ⚠️ Add telemetry to SimpleFi approval guards
  2. ⚠️ Consider parallel processing in HomeHistory
  3. ⚠️ Add user feedback during long Claim retries

Verify:

  1. 🔍 Check that useWebSocket awaits async callbacks
  2. 🔍 Load test with multiple simultaneous bridge transactions

Would you like me to generate the complete fixed code for the critical issues (1-3)?

✅ Actions performed

Full review triggered.

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.

1 participant