Skip to content

fix: open profiles for every real receipt peer - #2673

Merged
abalinda merged 3 commits into
devfrom
codex/fix-paid-party-profile-gate
Aug 12, 2026
Merged

fix: open profiles for every real receipt peer#2673
abalinda merged 3 commits into
devfrom
codex/fix-paid-party-profile-gate

Conversation

@kushagrasarathe

@kushagrasarathe kushagrasarathe commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Receipt profile navigation trusted the peer identity signal, but then narrowed it with a presentation-type allowlist. This left valid Peanut users inert on bank-fulfilled request receipts and other real-user flows.

This PR removes that stale allowlist. The shared hasUserProfile predicate now allows any non-link transaction whose peer is a real Peanut user with a valid username. Raw addresses, UUID fallbacks, guests, links, merchants, and generated system labels stay inert.

The bank-fulfilled request strategy now derives peer identity only from recipientAccount. It no longer treats the logged-in sender as the peer.

The receipt header separates name and avatar navigation. A real counterparty name always links to the profile. User initials or a real user avatar also link. Bank flags, rail icons, and the rest of the receipt card stay inert.

The predicate is shared by the activity row and receipt header. The fix therefore covers the history drawer and standalone /receipt/[entryId] page without separate peer-identity logic.

Task

TASK-21010

Design decision: “To” row

The “To” detail row stays unchanged. It only renders for claim_external, where the recipient is not a Peanut user and no profile exists. Expanding that row to other receipt types would be separate product work.

Risks / breaking changes

Low, frontend-only. No API, contract, dependency, or cross-repo change. The risk is an incorrect profile link if peer identity is wrong. Username, generated-label, link, crypto-address, and UUID checks remain as defense in depth.

QA

  • Added strategy coverage that a bank fulfillment never treats the logged-in sender as the peer.
  • Added predicate coverage that generated fallback labels stay inert even if the peer flag is wrong.
  • Added coverage for bank_request_fulfillment, real-user add, unknown future presentation types, and non-user QR/card/external flows.
  • Added receipt-header coverage for linked names, linked real-user avatars/initials, inert bank flags, and an inert card body.
  • Added activity-row click coverage for a bank-fulfilled request peer.
  • pnpm prettier --check .
  • npm run typecheck
  • npm test -- --runInBand — 230 suites, 2,939 passed, 3 skipped.
  • npm run build

Screenshots

N/A — no visible change. This PR changes click targets only.

Follow-up after release

Verify a real bank-fulfilled request receipt on production in both the history drawer and standalone receipt page. Then mark product/feedback/problems/cant-tap-name-to-open-profile.md as shipped in a separate content-only change.

Summary by CodeRabbit

  • Bug Fixes

    • Improved profile availability across supported transaction types, including bank-fulfilled requests.
    • Users with valid profiles can now be selected even when transaction presentation details are unavailable.
    • Bank-fulfilled request payers now navigate directly to the payer’s profile without opening the details drawer.
    • Improved navigation consistency between clickable profile names and avatars.
  • Tests

    • Expanded coverage for profile eligibility, navigation, and bridge-fulfilled requests.

@vercel

vercel Bot commented Aug 12, 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 Aug 12, 2026 1:55pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 12, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58eeaa6d-6443-419f-94d3-247ad7b6b7ef

📥 Commits

Reviewing files that changed from the base of the PR and between 287027b and 564f15d.

📒 Files selected for processing (4)
  • src/components/TransactionDetails/__tests__/transaction-predicates.test.ts
  • src/components/TransactionDetails/__tests__/transactionTransformer.test.ts
  • src/components/TransactionDetails/strategies/intent/p2p-send.ts
  • src/components/TransactionDetails/transaction-predicates.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/TransactionDetails/tests/transaction-predicates.test.ts
  • src/components/TransactionDetails/transaction-predicates.ts

📝 Walkthrough

Walkthrough

The change separates profile-name and profile-avatar eligibility for transaction details. It broadens profile detection across transaction types, corrects bridge-fulfilled request peer mapping, updates header navigation, and adds regression coverage.

Changes

Transaction profile navigation

Layer / File(s) Summary
Profile predicates and peer mapping
src/components/TransactionDetails/transaction-predicates.ts, src/components/TransactionDetails/strategies/intent/p2p-send.ts, src/components/TransactionDetails/__tests__/transaction-predicates.test.ts, src/components/TransactionDetails/__tests__/transactionTransformer.test.ts
hasUserProfile recognizes real user peers across transaction types. hasUserProfileAvatar limits avatar links to send, request, and receive cards. Bridge-fulfilled requests use the external recipient as the peer.
Receipt and header navigation
src/components/TransactionDetails/TransactionDetailsReceipt.tsx, src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx
The receipt computes name and avatar clickability separately. The header uses shared profile navigation with independent name and avatar controls.
Navigation regression coverage
src/components/TransactionDetails/__tests__/TransactionDetailsHeaderCard.test.tsx, src/components/TransactionDetails/__tests__/TransactionCard.test.tsx
Tests cover clickable names, user avatars, inert bank flags and receipt content, and bank-fulfilled request payer navigation without opening the details drawer.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: 0xkkonrad, abalinda

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: enabling profile navigation for real receipt peers.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-paid-party-profile-gate

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

@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7158 → 7158.83 (+0.83)
Findings: 0 net (+19 new, -19 resolved)

🆕 New findings (19)

  • critical complexity — src/components/TransactionDetails/TransactionDetailsReceipt.tsx — CC 163, MI 51.29, SLOC 428
  • critical method-complexity — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:90 — CC 109 SLOC 219
  • critical complexity — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx — CC 67, MI 45.47, SLOC 153
  • high hotspot — src/components/TransactionDetails/TransactionDetailsReceipt.tsx — 47 commits, +563/-1143 lines since 6 months ago
  • high complexity — src/components/TransactionDetails/transaction-predicates.ts — CC 37, MI 71.44, SLOC 69
  • medium high-mdd — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:90 — TransactionDetailsReceipt: MDD 259.9 (uses across many lines from declarations)
  • medium high-dlt — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:90 — TransactionDetailsReceipt: DLT 86 (calls 86 distinct functions — high context load)
  • medium high-mdd — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:55 — getTitle: MDD 46.1 (uses across many lines from declarations)
  • medium high-mdd — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:196 — TransactionDetailsHeaderCard: MDD 46.4 (uses across many lines from declarations)
  • medium high-mdd — src/components/TransactionDetails/strategies/intent/p2p-send.ts:9 — p2pSendOrRequestFulfill: MDD 30.0 (uses across many lines from declarations)
  • medium method-complexity — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:55 — CC 28 SLOC 102
  • medium method-complexity — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:196 — CC 23 SLOC 22
  • medium complexity — src/components/TransactionDetails/strategies/intent/p2p-send.ts — CC 16, MI 50.33, SLOC 41
  • medium method-complexity — src/components/TransactionDetails/strategies/intent/p2p-send.ts:9 — CC 15 SLOC 39
  • low high-dlt — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:876 — : DLT 15 (calls 15 distinct functions — high context load)
  • low structural-dup — components/Send/link/views/Success.link.send.view.tsx:116 — 9 duplicate lines / 52 tokens with components/TransactionDetails/TransactionDetailsReceipt.tsx:879
  • low missing-return-type — src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:196 — TransactionDetailsHeaderCard: exported fn missing return type annotation
  • low missing-return-type — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:90 — TransactionDetailsReceipt: exported fn missing return type annotation
  • low non-null-asserts — src/components/TransactionDetails/TransactionDetailsReceipt.tsx:90 — TransactionDetailsReceipt: 9 non-null assertions inside body

✅ Resolved (19)

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx — CC 163, MI 51.34, SLOC 426
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:89 — CC 109 SLOC 217
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx — CC 67, MI 45.43, SLOC 154
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx — 46 commits, +556/-1139 lines since 6 months ago
  • src/components/TransactionDetails/transaction-predicates.ts — CC 36, MI 71.71, SLOC 64
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:89 — TransactionDetailsReceipt: MDD 261.1 (uses across many lines from declarations)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:89 — TransactionDetailsReceipt: DLT 85 (calls 85 distinct functions — high context load)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:54 — getTitle: MDD 46.1 (uses across many lines from declarations)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:195 — TransactionDetailsHeaderCard: MDD 45.7 (uses across many lines from declarations)
  • src/components/TransactionDetails/strategies/intent/p2p-send.ts:9 — p2pSendOrRequestFulfill: MDD 29.6 (uses across many lines from declarations)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:54 — CC 28 SLOC 102
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:195 — CC 22 SLOC 22
  • src/components/TransactionDetails/strategies/intent/p2p-send.ts — CC 17, MI 50.2, SLOC 41
  • src/components/TransactionDetails/strategies/intent/p2p-send.ts:9 — CC 16 SLOC 39
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:873 — : DLT 15 (calls 15 distinct functions — high context load)
  • components/Send/link/views/Success.link.send.view.tsx:116 — 9 duplicate lines / 52 tokens with components/TransactionDetails/TransactionDetailsReceipt.tsx:876
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx:195 — TransactionDetailsHeaderCard: exported fn missing return type annotation
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:89 — TransactionDetailsReceipt: exported fn missing return type annotation
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx:89 — TransactionDetailsReceipt: 9 non-null assertions inside body

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2965 ran, 0 failed, 0 skipped, 53.2s

📊 Coverage (unit)

metric %
statements 66.5%
branches 51.7%
functions 56.5%
lines 67.3%
⏱ 10 slowest test cases
time test
3.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.9s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.6s src/utils/__tests__/auth-token.test.ts › ignores the guarded marker and falls back to the plain token
0.5s src/utils/__tests__/auth-token.test.ts › is none — never guarded — when only the guarded marker is present
0.4s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.4s src/utils/__tests__/sentry.utils.test.ts › defaults to the client budget under a browser global
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/utils/__tests__/sentry.utils.test.ts › still lets a per-call timeoutMs win over the default
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@kushagrasarathe
kushagrasarathe marked this pull request as ready for review August 12, 2026 07:20
@kushagrasarathe
kushagrasarathe requested review from 0xkkonrad and abalinda and removed request for 0xkkonrad and abalinda August 12, 2026 07:20
@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@abalinda

Copy link
Copy Markdown
Contributor

isPeerActuallyUser is true for every viewer on the bank-fulfilled branch

Verified against head 287027b. The widened hasUserProfile is correct in intent, but it now rests on a flag that is not trustworthy on one branch.

src/components/TransactionDetails/strategies/intent/p2p-send.ts:29

isPeerActuallyUser: !!entry.recipientAccount?.isUser || !!entry.senderAccount?.isUser,

This branch is already gated on entry.userRole === EHistoryUserRole.SENDER, so senderAccount is the viewer, not the peer. The viewer is always a logged-in Peanut user. The || therefore makes the flag unconditionally true, whatever the recipient is. Every sibling branch in this file reads a single account, which is the correct shape.

src/components/TransactionDetails/transaction-predicates.ts:131

Dropping the card-type list removes the last thing that masked the flag above:

return (
    !!transaction.isPeerActuallyUser &&
    !transaction.extraDataForDrawer?.isLinkTransaction &&
    !!userName &&
    !isCryptoAddress(userName) &&
    !isUuid(userName)
)

nameKey is not checked, so a front-end label passes. On the bank branch userName falls back to recipientAccount.identifier, then to the literal 'Recipient'. Both reach router.push(profileUrl(userName)) in the header card and land on a 404 profile.

Reachability is unconfirmed. A request is created by a Peanut user, so recipientAccount.isUser is probably true in the common case. This may be latent rather than live. A query for P2P_REQUEST_FULFILL with fulfillmentType = 'bridge' and a non-user recipient would settle it. The type list was doing this job by accident before, so the PR should not rely on the data staying that way.

Suggested fix — two lines.

// p2p-send.ts:29 — match the sibling branches
isPeerActuallyUser: !!entry.recipientAccount?.isUser,
// transaction-predicates.ts — same class as the address and uuid filters
&& !transaction.nameKey

The tests cannot catch this

src/components/TransactionDetails/__tests__/transaction-predicates.test.ts:183

Every rewritten "non-user counterparty" case sets isPeerActuallyUser: false, so the assertion reduces to false === false. The card-type list is no longer covered. A strategy that sets the flag on a merchant row would turn merchant names into profile links and the suite would stay green.

A case with isPeerActuallyUser: true plus a nameKey, expecting hasUserProfile to be false, would fail today and pass after the fix.

Note on CI

The e2e check on this PR is a green empty run. The job aborts before any test, under continue-on-error with no harness secret. Real coverage here is unit and typecheck.

@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@abalinda Valid finding, thanks. The bank branch incorrectly included the logged-in sender in peer identity, and hasUserProfile did not reject generated labels. Fixed in 564f15db5: the strategy now trusts only recipientAccount.isUser, and the shared predicate rejects any nameKey. I added regression tests for both cases; they failed before the fix and pass now. The full local gate is green: Prettier, typecheck, 230 suites with 2,939 passing tests, and the production build.

@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@abalinda
abalinda merged commit 6e14a49 into dev Aug 12, 2026
20 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