Skip to content

Chore/ethlatam perk#1412

Merged
Hugo0 merged 4 commits intopeanut-walletfrom
chore/ethlatam-perk
Nov 7, 2025
Merged

Chore/ethlatam perk#1412
Hugo0 merged 4 commits intopeanut-walletfrom
chore/ethlatam-perk

Conversation

@Hugo0
Copy link
Contributor

@Hugo0 Hugo0 commented Nov 6, 2025

only the perk related changes are relevant. rest are pulls from prod/


Note

Show campaign-cap reached messaging in the perk banner and plumb cap fields through the transaction transformer.

  • Perks UI (receipt):
    • Update perk banner logic in src/components/TransactionDetails/TransactionDetailsReceipt.tsx to handle capped campaigns using perk.isCapped and perk.campaignCapUsd, showing a “campaign limit reached” message and safe amount formatting.
  • Data model / transformer:
    • Extend extraDataForDrawer.perk in src/components/TransactionDetails/transactionTransformer.ts with isCapped, campaignCapUsd, and remainingCapUsd and propagate these fields when mapping entry.extraData.perk.

Written by Cursor Bugbot for commit 880dfea. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Nov 6, 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 Nov 7, 2025 1:13am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 6, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds optional campaign cap fields to transaction perk data and updates receipt rendering to use a local perk object with a new branch that displays a capped-campaign message when isCapped and campaignCapUsd are present; preserves existing percentage/amount messaging otherwise.

Changes

Cohort / File(s) Summary
Type Structure Updates
src/components/TransactionDetails/transactionTransformer.ts
Added optional fields isCapped?: boolean, campaignCapUsd?: number, remainingCapUsd?: number to TransactionDetails.merchantInfo and TransactionDetails.extraDataForDrawer.perk. Additive data carriage only.
Receipt Messaging Logic
src/components/TransactionDetails/TransactionDetailsReceipt.tsx
Replaced direct perk field access with a local perk variable; introduced isCapped / campaignCapUsd checks and a cap-aware rendering branch that shows a specialized capped message (including amountStr when present) and a "campaign limit reached" note; retained existing percentage/amount branches as a fallback.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Mixed changes: type additions plus new conditional rendering logic.
  • Review focus:
    • Ensure new optional fields are correctly propagated and null/undefined-safe in transactionTransformer.ts.
    • Verify rendering edge cases in TransactionDetailsReceipt.tsx (missing perk, formatting of amountStr/percentages, correct branching order for cap vs non-cap messages).

Possibly related PRs

Suggested reviewers

  • kushagrasarathe
  • jjramirezn

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Chore/ethlatam perk' is vague and uses generic terminology without clearly describing the main changes being implemented. Consider a more descriptive title that clearly indicates the primary change, such as 'Add campaign cap handling to perk banner' or 'Support campaign cap fields in perk data model'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-related to the changeset, clearly explaining the perk-related modifications across UI and data layers with specific file references and functional details.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/ethlatam-perk

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf868ed and 880dfea.

📒 Files selected for processing (1)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/app/[...recipient]/client.tsx:394-397
Timestamp: 2025-08-26T15:25:53.328Z
Learning: In `src/components/Common/ActionListDaimoPayButton.tsx`, the `handleCompleteDaimoPayment` function should not display error messages to users when DB update fails because the Daimo payment itself has succeeded - showing errors would be confusing since the payment was successful.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (2)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (2)

576-581: LGTM: Clean variable extraction improves readability.

The extraction of perk fields into local constants makes the subsequent conditional logic much cleaner and easier to follow. The new cap-related fields (isCapped, campaignCapUsd) integrate well with existing fields.


582-588: Capped message handling looks correct.

The conditional rendering properly handles both cases:

  • When amount exists: shows the specific cashback amount with the cap message
  • When amount is undefined: shows a standalone cap-reached message

This addresses the past concern about malformed messages with awkward spacing. Both message variants are complete and clear.


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

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on December 5

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

// If user hit their campaign cap, show special message
if (isCapped && campaignCap) {
return `You received ${amountStr} cashback! You've reached your $${campaignCap.toFixed(0)} campaign limit for now 🎉`
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Handling undefined sponsorship amount at cap

When isCapped is true but amountSponsored is undefined, amountStr will be an empty string. This results in a malformed message: "You received cashback!" with an awkward double space instead of showing the amount. The code should handle the case where amountSponsored might be undefined when the cap is reached, either by providing a fallback value or restructuring the message.

Fix in Cursor Fix in Web

@coderabbitai coderabbitai bot added the enhancement New feature or request label Nov 6, 2025
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: 1

🧹 Nitpick comments (1)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

581-581: Note: remainingCapUsd is extracted but unused.

The field remainingCapUsd is added to the perk type but isn't used in the UI. This is likely intentional for future functionality (e.g., showing how much of the campaign cap remains). If not needed immediately, consider documenting this for future reference.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1e99e9 and 06eca1e.

📒 Files selected for processing (2)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1 hunks)
  • src/components/TransactionDetails/transactionTransformer.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (1)
src/components/TransactionDetails/transactionTransformer.ts (1)

79-81: LGTM! Clean additive changes.

The three new optional fields (isCapped, campaignCapUsd, remainingCapUsd) are properly typed and consistently added in both the interface definition and the mapping logic. The optional nature of these fields ensures backward compatibility.

Also applies to: 530-532


// For non-capped messages, use amountStr
const amountStr =
amount !== undefined && amount !== null ? `$${amount.toFixed(2)}` : ''
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Inconsistent Truthiness Breaks Zero Amount Display

The conditions checking amount ? on lines 596, 598, and 600 are inconsistent with the amountStr definition on line 593. When amount is 0, amountStr will be "$0.00" but the truthy check amount ? evaluates to false, preventing the amount from being displayed. This creates a mismatch where zero-value perks won't show their amount even though the string is properly formatted.

Fix in Cursor Fix in Web

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: 0

🧹 Nitpick comments (1)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

583-589: Consider refining the capped message phrasing.

The current message structure places two celebratory statements back-to-back: "You received $X cashback! You've reached your $Y campaign limit 🎉". The double-exclamation creates slightly awkward phrasing.

Consider one of these alternatives:

 const amountText =
     amount !== undefined && amount !== null
-        ? `You received $${amount.toFixed(2)} cashback! `
+        ? `You received $${amount.toFixed(2)} cashback. `
         : ''
-return `${amountText}You've reached your $${campaignCap.toFixed(0)} campaign limit 🎉`
+return `${amountText}You've reached your $${campaignCap.toFixed(0)} campaign limit for now 🎉`

Or restructure to a single cohesive message:

-const amountText =
-    amount !== undefined && amount !== null
-        ? `You received $${amount.toFixed(2)} cashback! `
-        : ''
-return `${amountText}You've reached your $${campaignCap.toFixed(0)} campaign limit for now 🎉`
+if (amount !== undefined && amount !== null) {
+    return `You received $${amount.toFixed(2)} cashback and reached your $${campaignCap.toFixed(0)} campaign limit 🎉`
+} else {
+    return `You've reached your $${campaignCap.toFixed(0)} campaign limit 🎉`
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06eca1e and bf868ed.

📒 Files selected for processing (1)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/app/[...recipient]/client.tsx:394-397
Timestamp: 2025-08-26T15:25:53.328Z
Learning: In `src/components/Common/ActionListDaimoPayButton.tsx`, the `handleCompleteDaimoPayment` function should not display error messages to users when DB update fails because the Daimo payment itself has succeeded - showing errors would be confusing since the payment was successful.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2025-07-07T20:22:11.092Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 958
File: src/app/actions/tokens.ts:266-266
Timestamp: 2025-07-07T20:22:11.092Z
Learning: In `src/app/actions/tokens.ts`, within the `fetchWalletBalances` function, using the non-null assertion operator `!` on `process.env.MOBULA_API_KEY!` is intentional and correct, and should not be flagged for replacement with explicit validation.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.

Applied to files:

  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Deploy-Preview
🔇 Additional comments (2)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (2)

576-580: Verify that perk properties are always defined.

The code extracts discountPercentage, amountSponsored, isCapped, and campaignCapUsd from the perk object without null checks. While the parent conditional ensures perk.claimed exists, it doesn't guarantee these new optional fields are present. If percentage is undefined, lines 595-601 will produce malformed messages like "You received undefined% cashback...".

Add defensive checks or fallback values for these properties:

 const perk = transaction.extraDataForDrawer.perk
-const percentage = perk.discountPercentage
-const amount = perk.amountSponsored
-const isCapped = perk.isCapped
-const campaignCap = perk.campaignCapUsd
+const percentage = perk.discountPercentage ?? 0
+const amount = perk.amountSponsored
+const isCapped = perk.isCapped ?? false
+const campaignCap = perk.campaignCapUsd

Or add an early return if required fields are missing:

const perk = transaction.extraDataForDrawer.perk
if (!perk.discountPercentage) return 'Perk details unavailable'
const percentage = perk.discountPercentage
// ... rest of code

585-586: Good handling of undefined amount.

The explicit checks for amount !== undefined && amount !== null properly address the previous review concern about malformed messages when amount is missing. This ensures the message remains clear whether or not the amount is available.

@Hugo0 Hugo0 merged commit 88edd1d into peanut-wallet Nov 7, 2025
6 checks passed
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