Skip to content

fix(kyc): close the remaining 'Unlock region' loop doors + claim-geo via URL#2206

Merged
Hugo0 merged 6 commits into
devfrom
fix/kyc-loop-door-cleanup
Jun 10, 2026
Merged

fix(kyc): close the remaining 'Unlock region' loop doors + claim-geo via URL#2206
Hugo0 merged 6 commits into
devfrom
fix/kyc-loop-door-cleanup

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Audit follow-up to the cross-region loop work (#2191/#2194/#2199). Four issues, one theme: a surface decides wrong/nothing → BE silently no-ops → user loops on "You're all set".

  1. UnlockedRegions ROW loop door — the FE kept its own region→provider map that disagreed with the BE registry (mapped ROW→manteca; BE says ROW→null). A Manteca-verified user clicking "Rest of the world" looked same-provider → crossRegion omitted → BE silently early-returned APPROVED → no-op loop. Now: crossRegion: true unconditionally for locked-region clicks (safe — every BE cross-region branch requires APPROVED, so it's a no-op for fresh users), and the surviving display-only map is an exact BE-registry mirror (providerForRegionIntent in regions.utils, ROW→null) with lockstep tests.
  2. Dead KycVerifiedOrReviewModal removed — its only mount had a state that nothing ever set to true (verified by grep). Component + mount + showcase entries deleted.
  3. Limits page per-region pending badge — the old page-level bankRails().some(pending) boolean badged (and disabled) EVERY locked region off any pending rail anywhere; a pending AR rail badged Europe. New pendingBankRailRegionPaths scopes by the rail's jurisdiction.
  4. Claim-link geo survives the auth redirectregionalMethodType is now null-until-chosen (was a 'mercadopago' default that masqueraded as a choice) and the guest tap carries method in the URL (existing searchParams pattern + saveRedirectUrl persistence), so post-login the PIX claim passes targetCountry: 'BR' instead of undefined-or-wrong-AR. Display fallback is deliberately separated from geo derivation.

Risk

Medium-low. Item 1 changes when crossRegion is sent (verified no-op for non-APPROVED users; BE has the explicit unsupported-region signal since #985). Item 4 touches claim-flow state init — regionalMethodType consumers audited (ClaimBankFlowContext, MantecaFlowManager; RequestFulfillmentFlowContext has its own copy, untouched). BE-first deploy ordering already satisfied (#995 live).

QA

Hugo0 added 5 commits June 10, 2026 16:46
…OW no-op loop

A locked region has no functional rail by definition, so clicking one is
always a cross-region request. The FE's private copy of the region->provider
map said ROW->manteca, so a Manteca-verified user clicking 'Rest of the
world' compared manteca===manteca, dropped the crossRegion flag, and the BE
silently early-returned APPROVED -> onKycSuccess -> 'all set' with nothing
unlocked. Send crossRegion unconditionally (BE ignores it pre-approval and
answers 'unsupported-region' for ROW), and keep a single display-only mirror
of the BE registry (ROW->null) in regions.utils with a lockstep test.
Its only mount gated on isKycApprovedModalOpen, which initializes false and
has no setter-to-true anywhere — the modal could never open in production.
Part of the loop-door cleanup: dead 'you're verified' surfaces hide real
KYC-outcome signaling bugs behind code that looks like it handles them.
…region

The page-level isBankRailPending boolean badged EVERY locked region (well,
the hardcoded europe/north-america pair) whenever ANY bank rail was
mid-flight — a pending AR rail marked Europe pending and made it unclickable,
walling users off from regions they could actually start verifying. Derive
pending per region from each rail's jurisdiction (BE catalog country values),
so only the region that's actually provisioning shows the badge.
… in the URL

The Pix / Mercado Pago tap on a claim link only lived in context state, which
the auth redirect's full remount wiped — regionalMethodType fell back to its
'mercadopago' default, so a Pix (BR) claimer re-entered the flow looking like
a Mercado Pago (AR) one. Persist the tapped method as ?method= alongside
step=regional-claim (same plain-searchParams pattern the step param uses),
restore it on re-entry, and make the context value null-until-chosen so a
bare default can never masquerade as a real choice. MantecaFlowManager now
passes targetCountry to the LATAM KYC action only when the geo is reliably
known (explicit country pick, or BR/PIX vs AR/MP from a real method choice).
# Conflicts:
#	src/components/Claim/Link/MantecaFlowManager.tsx
@vercel

vercel Bot commented Jun 10, 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 10, 2026 4:36pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b956cdb5-b2d5-4433-8c2c-a57ce5627520

📥 Commits

Reviewing files that changed from the base of the PR and between 8ceb81f and d17ae80.

📒 Files selected for processing (13)
  • src/app/(mobile-ui)/dev/components/page.tsx
  • src/app/(mobile-ui)/dev/ds/patterns/modal/page.tsx
  • src/components/Claim/Link/Initial.view.tsx
  • src/components/Claim/Link/MantecaFlowManager.tsx
  • src/components/Claim/Link/SendLinkActionList.tsx
  • src/components/Claim/useClaimLink.tsx
  • src/components/Global/KycVerifiedOrReviewModal/index.tsx
  • src/components/Profile/index.tsx
  • src/components/Profile/views/UnlockedRegions.view.tsx
  • src/context/ClaimBankFlowContext.tsx
  • src/features/limits/views/LimitsPageView.tsx
  • src/utils/__tests__/regions.utils.test.ts
  • src/utils/regions.utils.ts
💤 Files with no reviewable changes (4)
  • src/components/Profile/index.tsx
  • src/app/(mobile-ui)/dev/components/page.tsx
  • src/components/Global/KycVerifiedOrReviewModal/index.tsx
  • src/app/(mobile-ui)/dev/ds/patterns/modal/page.tsx

Walkthrough

This PR refactors KYC region routing to use a capability-based model, removes the deprecated KycVerifiedOrReviewModal, and adds method selection persistence through authentication redirects. It introduces utility functions for provider routing and region pending status, updates the claim flow to preserve payment method selection via URL parameters, and migrates limits page pending tracking from a boolean to per-region capability data.

Changes

KYC Region Routing & Regional Claim Method Preservation

Layer / File(s) Summary
Provider routing utility functions
src/utils/regions.utils.ts, src/utils/__tests__/regions.utils.test.ts
providerForRegionIntent() maps KYC intents to provider identifiers; pendingBankRailRegionPaths() computes affected regions from bank-rail state. Both utilities tested with intent mapping and rail scenarios.
Regional method context & URL preservation
src/context/ClaimBankFlowContext.tsx, src/components/Claim/useClaimLink.tsx
ClaimBankFlowContext now treats regionalMethodType as nullable; useClaimLink extends addParamStep() to persist optional method parameter and removes it in removeParamStep().
Method selection & restoration across auth
src/components/Claim/Link/SendLinkActionList.tsx, src/components/Claim/Link/Initial.view.tsx
Unauthenticated users tapping regional methods persist selection via URL; after auth redirect, Initial.view reads method param and restores context state.
Region modal routing & KYC initiation
src/components/Profile/views/UnlockedRegions.view.tsx
Region click handling now derives modal type from downstream provider rail state using providerForRegionIntent utility; locked regions unconditionally set cross-region flag; local provider mapping removed.
MantecaFlowManager method & country derivation
src/components/Claim/Link/MantecaFlowManager.tsx
Introduces display-only displayMethodType fallback and derives targetCountry from explicit selection or regional method; recipient text and KYC initiation use derived country.
Pending regions via rail capability
src/features/limits/views/LimitsPageView.tsx
Migrates from boolean isBankRailPending to pendingRegionPaths set computed from rails; LockedRegionsList marks regions pending by individual rail status instead of global flag.
Modal removal & reference cleanup
src/components/Profile/index.tsx, src/app/(mobile-ui)/dev/...
Remove KycVerifiedOrReviewModal component and its rendering in Profile; update development reference pages to remove deleted modal from examples.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • peanutprotocol/peanut-ui#2192: Both PRs remove/replace KycVerifiedOrReviewModal usage across multiple component files as part of deprecation.
  • peanutprotocol/peanut-ui#2163: Both PRs refactor UnlockedRegions.view.tsx KYC region routing logic to derive provider intent and compare rail states instead of legacy markers.
  • peanutprotocol/peanut-ui#2205: Both PRs change KYC initiation wiring in MantecaFlowManager to pass derived country/geo context to handleInitiateKyc for LATAM flows.

Suggested labels

enhancement

Suggested reviewers

  • jjramirezn
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: closing 'Unlock region' loop issues and enabling claim geo persistence via URL parameters across auth redirects.
Description check ✅ Passed The description is directly related to the changeset, providing detailed context for all four main fixes: ROW loop door, dead modal removal, per-region pending badges, and claim-link geo persistence.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

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

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5839.11 → 5836.99 (-2.12)
Findings: +2 net (+52 new, -50 resolved)

🆕 New findings (52)

  • critical complexity — src/components/Claim/Link/Initial.view.tsx — CC 212, MI 51.12, SLOC 685
  • critical complexity — src/components/Claim/Link/SendLinkActionList.tsx — CC 61, MI 57.82, SLOC 174
  • critical complexity — src/components/Profile/views/UnlockedRegions.view.tsx — CC 56, MI 62.4, SLOC 150
  • high complexity — src/components/Claim/useClaimLink.tsx — CC 47, MI 56.34, SLOC 264
  • high complexity — src/utils/regions.utils.ts — CC 44, MI 61.11, SLOC 137
  • high complexity — src/components/Claim/Link/MantecaFlowManager.tsx — CC 35, MI 60.02, SLOC 86
  • high complexity — src/app/(mobile-ui)/dev/components/page.tsx — CC 34, MI 67.54, SLOC 145
  • medium react-long-component — src/app/(mobile-ui)/dev/components/page.tsx:120 — ComponentsPage is 1274 lines — split it
  • medium react-long-component — src/app/(mobile-ui)/dev/ds/patterns/modal/page.tsx:16 — ModalPage is 349 lines — split it
  • medium high-mdd — src/components/Claim/Link/Initial.view.tsx:57 — InitialClaimLinkView: MDD 137.2 (uses across many lines from declarations)
  • medium high-dlt — src/components/Claim/Link/Initial.view.tsx:57 — InitialClaimLinkView: DLT 94 (calls 94 distinct functions — high context load)
  • medium high-mdd — src/components/Claim/Link/MantecaFlowManager.tsx:27 — MantecaFlowManager: MDD 83.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Profile/views/UnlockedRegions.view.tsx:60 — UnlockedRegions: MDD 71.0 (uses across many lines from declarations)
  • medium high-mdd — src/components/Claim/Link/SendLinkActionList.tsx:69 — SendLinkActionList: MDD 58.3 (uses across many lines from declarations)
  • medium high-mdd — src/context/ClaimBankFlowContext.tsx:58 — ClaimBankFlowContextProvider: MDD 48.5 (uses across many lines from declarations)
  • medium high-mdd — src/components/Profile/index.tsx:21 — Profile: MDD 42.6 (uses across many lines from declarations)
  • medium high-mdd — src/features/limits/views/LimitsPageView.tsx:23 — LimitsPageView: MDD 36.1 (uses across many lines from declarations)
  • medium high-mdd — src/components/Claim/Link/Initial.view.tsx:243 — : MDD 32.4 (uses across many lines from declarations)
  • medium method-complexity — src/components/Claim/Link/Initial.view.tsx:57 — CC 29 SLOC 189
  • medium hotspot — src/components/Profile/index.tsx — 25 commits, +72/-128 lines since 6 months ago

…and 32 more.

✅ Resolved (50)

  • src/components/Claim/Link/Initial.view.tsx — CC 210, MI 51.21, SLOC 680
  • src/components/Profile/views/UnlockedRegions.view.tsx — CC 67, MI 62.79, SLOC 173
  • src/components/Claim/Link/SendLinkActionList.tsx — CC 61, MI 57.9, SLOC 173
  • src/components/Claim/useClaimLink.tsx — CC 46, MI 56.52, SLOC 261
  • src/app/(mobile-ui)/dev/components/page.tsx — CC 34, MI 67.53, SLOC 145
  • src/utils/regions.utils.ts — CC 34, MI 62.64, SLOC 110
  • src/components/Claim/Link/MantecaFlowManager.tsx — CC 33, MI 60.42, SLOC 84
  • src/app/(mobile-ui)/dev/components/page.tsx:120 — ComponentsPage is 1277 lines — split it
  • src/app/(mobile-ui)/dev/ds/patterns/modal/page.tsx:16 — ModalPage is 350 lines — split it
  • src/components/Claim/Link/Initial.view.tsx:57 — InitialClaimLinkView: MDD 135.5 (uses across many lines from declarations)
  • src/components/Claim/Link/Initial.view.tsx:57 — InitialClaimLinkView: DLT 93 (calls 93 distinct functions — high context load)
  • src/components/Claim/Link/MantecaFlowManager.tsx:27 — MantecaFlowManager: MDD 75.6 (uses across many lines from declarations)
  • src/components/Profile/views/UnlockedRegions.view.tsx:67 — UnlockedRegions: MDD 70.3 (uses across many lines from declarations)
  • src/components/Claim/Link/SendLinkActionList.tsx:69 — SendLinkActionList: MDD 57.5 (uses across many lines from declarations)
  • src/context/ClaimBankFlowContext.tsx:52 — ClaimBankFlowContextProvider: MDD 48.5 (uses across many lines from declarations)
  • src/components/Profile/index.tsx:22 — Profile: MDD 43.5 (uses across many lines from declarations)
  • src/features/limits/views/LimitsPageView.tsx:23 — LimitsPageView: MDD 35.8 (uses across many lines from declarations)
  • src/components/Claim/Link/Initial.view.tsx:242 — : MDD 32.4 (uses across many lines from declarations)
  • src/components/AddMoney/components/MantecaAddMoney.tsx:232 — 30 duplicate lines / 167 tokens with src/components/Claim/Link/MantecaFlowManager.tsx:133
  • src/components/Claim/Link/Initial.view.tsx:57 — CC 29 SLOC 188

…and 30 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/utils/regions.utils.ts 6.4 7.2 +0.8
src/components/Global/KycVerifiedOrReviewModal/index.tsx 4.5 0.0 -4.5

The worktree env shim (symlink to the primary tree's generated flags)
got swept into a commit; in CI it's a broken symlink that makes the
flag-generation postinstall mkdir fail, cascading every job red.
@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

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

📊 Coverage (unit)

metric %
statements 51.1%
branches 33.1%
functions 38.9%
lines 51.1%
⏱ 10 slowest test cases
time test
0.4s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every stamp stays within canvas at any count
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle unresolvable ENS name
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/components/Request/__tests__/request-states.test.tsx › API failure shows error message and toast
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 US account with spaces 2
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 valid ETH address with surrounding spaces
📍 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

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant