Skip to content

chore: back-merge main into dev (2026-06-01 hotfixes)#2151

Merged
Hugo0 merged 11 commits into
devfrom
chore/sync-main-into-dev-2026-06-01
Jun 1, 2026
Merged

chore: back-merge main into dev (2026-06-01 hotfixes)#2151
Hugo0 merged 11 commits into
devfrom
chore/sync-main-into-dev-2026-06-01

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Routine back-merge so feature work on `dev` builds on the hotfixes that already shipped to `main` today.

Included hotfixes

PR Title Why
#2148 fix(share-asset): surface failing image URL instead of [object Event] Sentry diagnosability
#2147 hotfix(offramp): prevent double-pay after Bridge confirm timeout P0 Konrad incident (Sentry PEANUT-UI-QH9) — gates the Retry button when on-chain leg already fired
#2145 hotfix(add-money): country-scope the bank-deposit gate 2026-06-01 sync-rails incident — 1,595 users seeing wrong blocked modal
#2144 fix(card): gate card-unlock history row on an issued card, not access

Why now

`feat/bridge-confirm-recovery-fe` (#2150) builds on #2147's `submittedTxHash` state. Targeting `main` for that PR is fine, but once it lands, the next dev → main release would conflict without this back-merge. Running this now keeps the dev branch caught up and avoids review-time surprises on subsequent dev-targeted PRs.

Conflict status

Clean merge — no conflicts. `git log dev..main` is straightforward and `dev` has not touched any of the same lines.

Test plan

  • Merge resolved cleanly (no conflicts)
  • CI green (typecheck, tests, format) — should mirror main's CI since this is just main's tree on top of dev

Hugo0 and others added 11 commits June 1, 2026 09:51
Release dev → main · card-launch badges + AR/BR rail nudge + Founder Haus rename
The synthetic "You skipped the line" / card-unlock share-asset row was
derived from hasCardAccess alone. Card access = holding a skip badge
(OG/Devconnect/Arbiverse/Pioneer) or an admin grant; it does NOT mean
the user ever entered the card flow or got a card. Result: 3,230 of
9,757 prod users (every skip-badge holder + admin grants) saw a
shareable "I got my Peanut card" asset for a card they never received —
only 9 users actually have a card.

Gate deriveCardUnlockEntry on hasIssuedCard (>=1 Rain card row, read
from the already-cached rain overview — no extra fetch). No DB change:
the badges are legitimate data; the bug was purely FE gating.
…ry-gate

fix(card): gate card-unlock history row on an issued card, not access
When viewing /add-money/<country>, narrow the 'is the bank channel ready'
gate to that country's rail jurisdiction. Without this, a rejected rail
in an unrelated jurisdiction trips blocked-rejection here and surfaces
'We couldn't unlock this — Verification issue' on a country whose own
rail is fine.

Triggering incident (2026-06-01): a sync run flipped 1,595 users'
BANK_TRANSFER_MX BRIDGE rails to REJECTED (catalog seed artifact, not
a real Bridge endorsement — Mexico is SPEI). The catalog deactivation
hides those rows going forward, but this scoping fix is the broader
guardrail: any future blocked rail in country X must not block country Y.
The Bridge offramp /confirm call from the FE uses fetchWithSentry's 10s
default timeout. When it fires (Sentry PEANUT-UI-QH9, Konrad 2026-06-01):
the on-chain wallet→Bridge deposit has already succeeded, but the FE
showed a generic "contact support" error + a big pink Retry button.
Clicking Retry re-runs sendMoney() and would send funds to the deposit
address a second time — real money lost.

Three fixes, in order of how badly we need each:

1. Gate the Retry button on the in-flight on-chain leg.
   - WithdrawBankPage: a new submittedTxHash state is set as soon as the
     wallet→Bridge tx confirms (before confirmOfframp). When set, the UI
     replaces the Retry button with a Done button + an InfoCard ("your
     transfer is processing, contact support if it doesn't show in 30
     min"). The on-chain leg is irreversible from the FE's side; Bridge
     and the BE poller will reconcile.
   - BankFlowManager (claim-link bank flow): the same risk via claimLink.
     handleConfirmClaim now swallows confirmOfframp errors after the
     on-chain claim succeeded and routes to SUCCESS instead of bubbling
     a retryable error back to ConfirmBankClaimView.

2. Bump confirmOfframp's timeout from 10s → 60s. Matches the long-running
   confirm pattern in services/manteca.ts and services/rain.ts. The BE
   needs more headroom than the default — investigation of why the BE
   write takes >10s is tracked separately.

3. Add "timed out after" to ErrorHandler's timeout matcher list. Our own
   fetchWithSentry AbortError copy ("Request to <url> timed out after
   <ms>ms") didn't hit either existing matcher and fell through to the
   "contact support" fallback. This is a backstop for the non-offramp
   endpoints; fixes 1+2 are what actually defuses the double-pay.

Fixes Sentry PEANUT-UI-QH9.
…-scope-gate

hotfix(add-money): country-scope the bank-deposit gate
…imeout-safety

hotfix(offramp): prevent double-pay after Bridge confirm timeout
Both PEANUT-UI-QHB and PEANUT-UI-QHC fire as `[share-asset] save/share
failed [object Event]` — same user, both buttons, one second apart.
Root cause: when an inlined `<img>` in <ShareAssetD3 /> fails to load
(badge icon URL 404 / CORS / blocker), html-to-image's toBlob rejects
with the raw DOM ErrorEvent (see node_modules/html-to-image/lib/util.js:209,
`img.onerror = reject`). The catch block did `(err as Error).message`,
which is `undefined` on an Event, and `console.error('...', err)`
stringifies the Event as `[object Event]` — that becomes the Sentry
title and we learn nothing about which asset broke.

Changes:
- New ShareAssetCaptureError. captureShareAsset wraps the Event reject
  in this Error type, extracting `target.src` so the failing image URL
  rides along on `failedImages` instead of being lost.
- ShareAssetActions' two catch blocks route through a `describeShareError`
  helper that handles ShareAssetCaptureError, normal Error, and raw DOM
  Event (defence-in-depth for any future call path that bypasses our
  wrapper).
- Both blocks now explicitly call Sentry.captureException with `feature`,
  `action`, `source` tags and the structured detail as `extra` — no longer
  relying on Sentry's console-error auto-capture, which stringified the
  Event opaquely.
- AbortError check tightened to `instanceof Error && name === 'AbortError'`
  so we don't silently swallow non-Error rejections that happen to
  duck-type that field.

Next time this fires, Sentry will show e.g.:
  ShareAssetCaptureError: html-to-image failed to inline image:
  https://cdn.example/badges/123.png
+ extra.failedImages with the array.

PostHog `CARD_SHARE_ASSET_FAILED` events get the same structured fields
so we can dashboard the failing URLs.
…pture

fix(share-asset): surface failing image URL instead of [object Event]
Brings the following main-only hotfixes into dev so feature work can build
on top of them:
- #2148 fix(share-asset): surface failing image URL instead of [object Event]
- #2147 hotfix(offramp): prevent double-pay after Bridge confirm timeout (Konrad PEANUT-UI-QH9)
- #2145 hotfix(add-money): country-scope the bank-deposit gate
- #2144 fix(card): gate card-unlock history row on an issued card, not access
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jun 1, 2026 3:20pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Hugo0, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 4 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0dc7324-542b-4baf-91f4-25f955d020e6

📥 Commits

Reviewing files that changed from the base of the PR and between ead2ae3 and a405919.

📒 Files selected for processing (12)
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
  • src/app/actions/offramp.ts
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx
  • src/components/Card/__tests__/cardUnlock.types.test.ts
  • src/components/Card/cardUnlock.types.ts
  • src/components/Card/share-asset/ShareAssetActions.tsx
  • src/components/Card/share-asset/__tests__/captureShareAsset.test.ts
  • src/components/Card/share-asset/captureShareAsset.ts
  • src/components/Claim/Link/views/BankFlowManager.view.tsx
  • src/components/Home/HomeHistory.tsx
  • src/utils/__tests__/friendly-error.utils.test.tsx
  • src/utils/friendly-error.utils.tsx

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

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5835.68 → 5838.95 (+3.27)
Findings: -1 net (+60 new, -61 resolved)

🆕 New findings (60)

  • critical complexity — src/components/AddWithdraw/AddWithdrawCountriesList.tsx — CC 120, MI 57.32, SLOC 352
  • critical complexity — src/components/Claim/Link/views/BankFlowManager.view.tsx — CC 98, MI 47.81, SLOC 402
  • critical complexity — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — CC 97, MI 52.83, SLOC 328
  • critical complexity — src/components/Home/HomeHistory.tsx — CC 93, MI 58.3, SLOC 300
  • critical complexity — src/utils/friendly-error.utils.tsx — CC 50, MI 45.82, SLOC 136
  • high hotspot — src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 38 commits, +603/-431 lines since 6 months ago
  • high method-complexity — src/utils/friendly-error.utils.tsx:41 — CC 38 SLOC 105
  • high hotspot — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — 37 commits, +368/-164 lines since 6 months ago
  • high method-complexity — src/components/Claim/Link/views/BankFlowManager.view.tsx:289 — CC 32 SLOC 104
  • high hotspot — src/components/Home/HomeHistory.tsx — 31 commits, +284/-202 lines since 6 months ago
  • medium react-long-component — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:61 — WithdrawBankPage is 494 lines — split it
  • medium high-mdd — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:61 — WithdrawBankPage: MDD 141.3 (uses across many lines from declarations)
  • medium high-mdd — src/components/AddWithdraw/AddWithdrawCountriesList.tsx:43 — AddWithdrawCountriesList: MDD 133.3 (uses across many lines from declarations)
  • medium high-mdd — src/components/Claim/Link/views/BankFlowManager.view.tsx:56 — BankFlowManager: MDD 105.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Home/HomeHistory.tsx:49 — HomeHistory: MDD 71.4 (uses across many lines from declarations)
  • medium high-dlt — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:61 — WithdrawBankPage: DLT 67 (calls 67 distinct functions — high context load)
  • medium high-dlt — src/components/AddWithdraw/AddWithdrawCountriesList.tsx:43 — AddWithdrawCountriesList: DLT 62 (calls 62 distinct functions — high context load)
  • medium high-dlt — src/components/Home/HomeHistory.tsx:49 — HomeHistory: DLT 52 (calls 52 distinct functions — high context load)
  • medium high-mdd — src/components/Claim/Link/views/BankFlowManager.view.tsx:289 — handleSuccess: MDD 37.9 (uses across many lines from declarations)
  • medium high-mdd — src/components/Home/HomeHistory.tsx:149 — : MDD 30.9 (uses across many lines from declarations)

…and 40 more.

✅ Resolved (61)

  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx — CC 119, MI 57.15, SLOC 346
  • src/components/Claim/Link/views/BankFlowManager.view.tsx — CC 97, MI 48.16, SLOC 392
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — CC 94, MI 52.59, SLOC 320
  • src/components/Home/HomeHistory.tsx — CC 93, MI 58.45, SLOC 296
  • src/utils/friendly-error.utils.tsx — CC 49, MI 45.94, SLOC 135
  • src/utils/friendly-error.utils.tsx:41 — CC 37 SLOC 104
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — 36 commits, +317/-158 lines since 6 months ago
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 36 commits, +576/-413 lines since 6 months ago
  • src/components/Claim/Link/views/BankFlowManager.view.tsx:264 — CC 32 SLOC 104
  • src/components/Home/HomeHistory.tsx — 30 commits, +276/-201 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:52 — WithdrawBankPage is 458 lines — split it
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx:42 — AddWithdrawCountriesList: MDD 134.0 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:52 — WithdrawBankPage: MDD 131.6 (uses across many lines from declarations)
  • src/components/Claim/Link/views/BankFlowManager.view.tsx:56 — BankFlowManager: MDD 102.0 (uses across many lines from declarations)
  • src/components/Home/HomeHistory.tsx:48 — HomeHistory: MDD 70.8 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:52 — WithdrawBankPage: DLT 66 (calls 66 distinct functions — high context load)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx:42 — AddWithdrawCountriesList: DLT 61 (calls 61 distinct functions — high context load)
  • src/components/Home/HomeHistory.tsx:48 — HomeHistory: DLT 51 (calls 51 distinct functions — high context load)
  • src/components/Claim/Link/views/BankFlowManager.view.tsx:264 — handleSuccess: MDD 37.9 (uses across many lines from declarations)
  • src/components/Home/HomeHistory.tsx:143 — : MDD 30.7 (uses across many lines from declarations)

…and 41 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Card/share-asset/captureShareAsset.ts 6.1 7.0 +0.9
src/components/Card/share-asset/ShareAssetActions.tsx 9.4 10.2 +0.8

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1262 ran, 0 failed, 0 skipped, 22.9s

📊 Coverage (unit)

metric %
statements 50.1%
branches 31.0%
functions 34.3%
lines 50.0%
⏱ 10 slowest test cases
time test
0.5s 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 too long for US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
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 minimum length (6 digits) US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle invalid ETH address (missing 0x prefix)
0.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Perk claimed shows shake class + go home button
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address in lowercase
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@Hugo0 Hugo0 merged commit ac9433b into dev Jun 1, 2026
19 of 21 checks passed
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.

2 participants