Skip to content

fix(rain): 120s timeout on collateral withdraw submit (was 10s default → duplicate sends)#2279

Merged
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-collateral-submit-timeout
Jun 24, 2026
Merged

fix(rain): 120s timeout on collateral withdraw submit (was 10s default → duplicate sends)#2279
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-collateral-submit-timeout

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Hotfix — request payments via Rain card collateral double-send

Symptom (prod, post-#2245)

Paying a request / direct-send via Rain card collateral shows the payer "there was an issue with your request" after they signeven though the payment succeeds on-chain. The payer retries → the retry also lands → duplicate send.

Observed: one payer → same recipient ×3 ($1 each) for a single intended $1 payment (tx 0x0ce1… confirmed at 23:20:54 while the FE showed an error).

Root cause

POST /rain/cards/withdraw/submit is synchronous: it broadcasts and awaits on-chain confirmation (waitForUserOperationReceipt + confirmIntentByTxHash), and for a request/charge it settles the charge in the same call. The FE's submitWithdrawal sent it with no timeoutMsfetchWithSentry's default 10s. When confirmation exceeds 10s the FE aborts while the tx still lands and the charge settles → false error → retry → duplicate.

Why now: #2245 (73f73bc53) routed request payments through this submit path for the first time. Card withdrawals already used it; request-pays — much higher frequency — only started hitting the 10s cap at go-live. Direct Sentry fingerprint: PEANUT-UI-QP5: rain/cards/withdraw/submit timed out after 10000ms (first seen at deploy).

Fix

Give submitWithdrawal a 120s budget — the same value the verified-withdrawal path (rain.ts:~525) already uses for this exact synchronous-confirm reason. One line + an explanatory comment.

Scope / risk

  • 1 file, src/services/rain.ts. No behavior change except the client wait budget on this one money-path call.
  • Branched off main (prod) as a mini-hotfix.

Verification

  • typecheck ✅ · prettier ✅
  • Behavioral: a >10s collateral submit now resolves to the real success instead of a false abort. A unit test here would only assert "fetch called with 120000" (mock-was-called anti-pattern); recommend a Nutcracker scenario (slow-confirm collateral request-pay) as follow-up coverage instead.

Not in this PR (follow-ups)

  • The stuck-PENDING attempts (no tx) look like Rain's per-account signature cooldown from rapid retries — largely self-resolves once the false errors stop driving retries; worth a separate look.
  • DRY: 5+ call sites still each remember "collateral-only ⇒ skip recordPayment" — consolidate behind one payCharge primitive.
  • Back-merge / cherry-pick this commit to dev so the next release doesn't regress it.

POST /rain/cards/withdraw/submit is synchronous: it broadcasts AND awaits
on-chain confirmation (waitForUserOperationReceipt + confirmIntentByTxHash)
and, for a request/charge, settles the charge in the same call. The FE's
submitWithdrawal sent it with no timeoutMs, so it used fetchWithSentry's
default 10s budget. When confirmation exceeds 10s the FE aborts while the tx
still lands and the charge settles: the payer sees "there was an issue with
your request" on a payment that SUCCEEDED, retries, and double-sends
(observed in prod: one payer -> same recipient x3).

#2245 (73f73bc) routed request payments through this submit path for the
first time, which is why request-pays only started timing out now. Match the
120s budget the verified-withdrawal path already uses for the same
synchronous-confirm reason.

Fixes PEANUT-UI-QP5 (rain/cards/withdraw/submit timed out after 10000ms).
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Building Building Preview, Comment Jun 24, 2026 11:57pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8717a913-b866-4170-83eb-2f793c1cc920

📥 Commits

Reviewing files that changed from the base of the PR and between b4f2aff and 0c82487.

📒 Files selected for processing (1)
  • src/services/rain.ts

Walkthrough

submitWithdrawal now documents /submit as synchronous and sets a 120-second timeout for the withdrawal submission request.

Changes

Withdrawal submission

Layer / File(s) Summary
Submit withdrawal timeout update
src/services/rain.ts
submitWithdrawal now describes /submit as a synchronous flow and passes timeoutMs: 120_000 to the withdrawal submit request.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@Hugo0 Hugo0 merged commit 451f4eb into main Jun 24, 2026
15 of 19 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5745.8 → 5745.8 (0)
Findings: 0 net (+0 new, -0 resolved)

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1554 ran, 0 failed, 0 skipped, 22.0s

📊 Coverage (unit)

metric %
statements 53.4%
branches 36.3%
functions 40.9%
lines 53.3%
⏱ 10 slowest test cases
time test
0.6s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.3s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every stamp stays within canvas at any count
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle invalid ETH address (too short)
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle maximum length (17 digits) US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Perk claim in progress shows disabled button + progress
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid US account with spaces
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle too long for US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle unresolvable ENS name
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant