Skip to content

Landing page v2.1#1089

Merged
Hugo0 merged 15 commits intopeanut-wallet-devfrom
feat/lpv2.1
Aug 14, 2025
Merged

Landing page v2.1#1089
Hugo0 merged 15 commits intopeanut-wallet-devfrom
feat/lpv2.1

Conversation

@Zishan-7
Copy link
Contributor

Contributes to TASK-13599 and TASK-13479

@notion-workspace
Copy link

LP v2.1 implementation

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 12, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Replaces BusinessIntegrate with DropLink in landing exports and page composition; adds RegulatedRails and Footer; introduces new landing components, icons/illustrations, a currency picker, exchange-rate API + hook, country-currency mappings, an Exchange page, and a comparison script.

Changes

Cohort / File(s) Summary
Page composition & landing exports
src/app/page.tsx, src/components/LandingPage/index.ts
Reorders landing sections on the main page; replaces BusinessIntegrate with DropLink in re-exports and usage; adds RegulatedRails and Footer to page composition.
New landing components
src/components/LandingPage/dropLink.tsx, src/components/LandingPage/RegulatedRails.tsx, src/components/LandingPage/Footer.tsx, src/components/LandingPage/CurrencySelect.tsx
Adds DropLink (animated CTA section), RegulatedRails (hero with marquee of bank logos), Footer (three-column site footer), and a client-side CurrencySelect popover with search, flags, and selection handling.
Landing component modifications
src/components/LandingPage/noFees.tsx, src/components/LandingPage/yourMoney.tsx, src/components/LandingPage/securityBuiltIn.tsx, src/components/LandingPage/imageAssets.tsx, src/components/LandingPage/hero.tsx
Converts NoFees to a client interactive flow with URL sync and exchange-rate fetching; restructures YourMoney to a two-column CTA using LandingCountries; updates copy/layout in SecurityBuiltIn; swaps Cloud → borderCloud asset usage; adjusts hero height/spacing.
Removed component
src/components/LandingPage/businessIntegrate.tsx
Deletes the BusinessIntegrate component and its export.
Assets & illustration exports
src/assets/icons/index.ts, src/assets/illustrations/index.ts
Adds brand icon exports (BBVA_ICON, BRUBANK_ICON, N26_ICON, SANTANDER_ICON, REVOLUT_ICON, STRIPE_ICON, MERCADO_PAGO_ICON, PIX_ICON, WISE_ICON, GITHUB_WHITE_ICON) and LandingCountries illustration export.
Icon system
src/components/Global/Icons/Icon.tsx, src/components/Global/Icons/chevron-down.tsx
Adds ChevronDownIcon component, extends IconName union with 'chevron-down', and maps it in the icon registry.
API, hook & constants
src/app/api/exchange-rate/route.ts, src/hooks/useExchangeRate.ts, src/constants/countryCurrencyMapping.ts
Adds GET /api/exchange-rate (Bridge primary, Frankfurter fallback, caching, 50bps adjustment), introduces useExchangeRate hook for two-way conversion, and adds CountryCurrencyMapping type + dataset.
Other client changes & pages
src/components/0_Bruddle/Button.tsx, src/app/exchange/page.tsx
Marks Button as a client component; adds an ExchangePage that composes NoFees and Footer.
Build/dev tooling script
scripts/compare-rates.mjs
Adds a Node script to fetch and compare rates from Bridge, Frankfurter, and an optional local API, with delta analysis.
Asset usage updates
src/components/LandingPage/imageAssets.tsx
Replaces Cloud imports/usages with borderCloud asset references.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Hugo0
  • FacuBozzi
  • jjramirezn

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 969847d and 0c7b577.

📒 Files selected for processing (2)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/hero.tsx (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/lpv2.1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Zishan-7
Copy link
Contributor Author

@Hugo0 exchange rate widget API integration and incomplete and on small hand icon is missing in your money component. Rest everything is done

@vercel
Copy link

vercel bot commented Aug 12, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Preview Comment Aug 14, 2025 3:57pm

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

🧹 Nitpick comments (10)
src/app/api/bridge/exchange-rate/route.ts (1)

46-52: Consider more specific error handling.

The current implementation forwards the Bridge API's HTTP status code directly. Consider mapping specific Bridge API error codes to more appropriate client-facing status codes if needed.

For example:

 if (!response.ok) {
     console.error(`Bridge API error: ${response.status} ${response.statusText}`)
+    const statusCode = response.status === 404 ? 400 : response.status
     return NextResponse.json(
         { error: 'Failed to fetch exchange rates from Bridge API' },
-        { status: response.status }
+        { status: statusCode }
     )
 }
src/components/LandingPage/dropLink.tsx (1)

7-8: Consider moving the color constant to a design system or theme file.

The hardcoded color #90A8ED should ideally be part of a centralized design system or theme configuration for better maintainability and consistency across components.

-// Define the background color as a constant
-const businessBgColor = '#90A8ED'

Move this to a theme file or use Tailwind's color system:

// In a theme/colors.ts file
export const colors = {
  business: '#90A8ED'
}
src/components/LandingPage/Footer.tsx (2)

68-68: Incorrect alt text for hand middle finger image.

The alt text says "Hand waving" but should describe the actual image content.

-                <Image src={handMiddleFinger.src} alt="Hand waving" width={20} height={20} />
+                <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} />

60-70: Consider accessibility for decorative hand icons.

Most hand icons appear to be decorative except for the clickable peace sign. Consider adding aria-hidden="true" to decorative images and proper accessibility attributes to the clickable one.

                <Image
                    src={handPeace.src}
                    alt="Hand peace"
                    width={20}
                    height={20}
+                    role="button"
+                    tabIndex={0}
+                    onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') window.open('https://youtube.com/shorts/qd2FbzLS380?si=T5xk7xrTGYiIiWFu', '_blank') }}
                    onClick={() => window.open('https://youtube.com/shorts/qd2FbzLS380?si=T5xk7xrTGYiIiWFu', '_blank')}
                />
-                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} />
+                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} aria-hidden="true" />
-                <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} />
+                <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} aria-hidden="true" />
-                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} />
+                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} aria-hidden="true" />
src/components/LandingPage/CurrencySelect.tsx (3)

15-31: Consider loading currency data from an external source.

The hardcoded currency list limits scalability. As the component grows, consider loading currency data from a configuration file or API for better maintainability.

Create a separate file for currency data:

// utils/currencies.ts
export const currencies = [
  // ... currency data
]

22-24: Incorrect country name for EUR.

"Euro" is not a country name. Consider using "European Union" or a more appropriate designation.

    {
        countryCode: 'eu',
-        country: 'Euro',
+        country: 'European Union',
        currency: 'EUR',
    },

54-54: Clean up commented code.

The commented height value should be removed or properly documented if it's intended for future use.

-                height={'52'} //{'72'} commented out for now, this will be final height when we add all the currencies
+                height={'52'}

If this is a planned change, consider adding a proper TODO comment:

-                height={'52'} //{'72'} commented out for now, this will be final height when we add all the currencies
+                height={'52'} // TODO: Change to '72' when adding all currencies
src/components/LandingPage/yourMoney.tsx (1)

20-48: Consider removing unused features array

The features array and its associated imports (lines 1-6, 11-48) are no longer used in the component but remain in the code.

Remove the unused code to improve maintainability:

-import iphoneYourMoney1 from '@/assets/iphone-ss/iphone-your-money-1.png'
-import iphoneYourMoney2 from '@/assets/iphone-ss/iphone-your-money-2.png'
-import iphoneYourMoney3 from '@/assets/iphone-ss/iphone-your-money-3.png'
-import freeGlobalTransfers from '@/assets/illustrations/free-global-transfers.svg'
-import payAnyoneAnywhere from '@/assets/illustrations/pay-anyone-anywhere.svg'
-import getPaidWorldwide from '@/assets/illustrations/get-paid-worldwide.svg'
 import Image from 'next/image'
 import { LandingCountries } from '@/assets'
 import { Button } from '../0_Bruddle'

-interface Feature {
-    id: number
-    title: string
-    titleSvg: any
-    description: string
-    imageSrc: any
-    imageAlt: string
-}
-
-const features: Feature[] = [
-    {
-        id: 1,
-        title: 'FREE GLOBAL TRANSFERS',
-        titleSvg: freeGlobalTransfers,
-        description:
-            'Move money between your own accounts in 140+ countries and 50+ currencies, no fees, live FX rates.',
-        imageSrc: iphoneYourMoney1,
-        imageAlt: 'iPhone showing global transfer screen',
-    },
-    {
-        id: 2,
-        title: 'PAY ANYONE, ANYWHERE',
-        titleSvg: payAnyoneAnywhere,
-        description:
-            'Send funds in seconds through WhatsApp, a phone number, or a QR code. No bank details, no friction.',
-        imageSrc: iphoneYourMoney2,
-        imageAlt: 'iPhone showing payment options screen',
-    },
-    {
-        id: 3,
-        title: 'GET PAID WORLDWIDE',
-        titleSvg: getPaidWorldwide,
-        description:
-            'Get paid by clients in 140+ countries, direct to your account, and settle in the currency you prefer.',
-        imageSrc: iphoneYourMoney3,
-        imageAlt: 'iPhone showing payment request screen',
-    },
-]
-
 export function YourMoney() {
src/components/LandingPage/noFees.tsx (2)

252-256: Format exchange rate display for consistency

The exchange rate should be formatted to a consistent number of decimal places for better readability.

             {destinationAmount > 0 && (
                 <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1">
-                    1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}
+                    1 {sourceCurrency} = {currentExchangeRate.toFixed(4)} {destinationCurrency}
                 </div>
             )}

234-234: Format destination amount display

The destination amount should be formatted to 2 decimal places for currency display consistency.

-                value={destinationAmount}
+                value={destinationAmount.toFixed(2)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e4bfa9 and eb701b0.

⛔ Files ignored due to path filters (17)
  • src/assets/icons/bbva-logo.svg is excluded by !**/*.svg
  • src/assets/icons/brubank-logo.svg is excluded by !**/*.svg
  • src/assets/icons/github-white.png is excluded by !**/*.png
  • src/assets/icons/mercado-pago-logo.svg is excluded by !**/*.svg
  • src/assets/icons/n26-logo.svg is excluded by !**/*.svg
  • src/assets/icons/pix-logo.svg is excluded by !**/*.svg
  • src/assets/icons/revolut-logo.svg is excluded by !**/*.svg
  • src/assets/icons/santander-logo.svg is excluded by !**/*.svg
  • src/assets/icons/stripe-logo.svg is excluded by !**/*.svg
  • src/assets/icons/wise-logo.svg is excluded by !**/*.svg
  • src/assets/illustrations/hand-middle-finger.svg is excluded by !**/*.svg
  • src/assets/illustrations/landing-countries.svg is excluded by !**/*.svg
  • src/assets/illustrations/mobile-send-in-seconds.svg is excluded by !**/*.svg
  • src/assets/illustrations/no-hidden-fees.svg is excluded by !**/*.svg
  • src/assets/illustrations/pay-zero-fees.svg is excluded by !**/*.svg
  • src/assets/iphone-ss/iphone-drop-a-link-mobile.png is excluded by !**/*.png
  • src/assets/iphone-ss/iphone-drop-a-link.png is excluded by !**/*.png
📒 Files selected for processing (16)
  • src/app/api/bridge/exchange-rate/route.ts (1 hunks)
  • src/app/page.tsx (3 hunks)
  • src/assets/icons/index.ts (1 hunks)
  • src/assets/illustrations/index.ts (1 hunks)
  • src/components/Global/Icons/Icon.tsx (3 hunks)
  • src/components/Global/Icons/chevron-down.tsx (1 hunks)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/Footer.tsx (1 hunks)
  • src/components/LandingPage/RegulatedRails.tsx (1 hunks)
  • src/components/LandingPage/businessIntegrate.tsx (0 hunks)
  • src/components/LandingPage/dropLink.tsx (1 hunks)
  • src/components/LandingPage/imageAssets.tsx (5 hunks)
  • src/components/LandingPage/index.ts (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/components/LandingPage/securityBuiltIn.tsx (2 hunks)
  • src/components/LandingPage/yourMoney.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • src/components/LandingPage/businessIntegrate.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/components/LandingPage/yourMoney.tsx
🧬 Code Graph Analysis (7)
src/app/api/bridge/exchange-rate/route.ts (1)
src/app/actions/exchange-rate.ts (1)
  • ExchangeRateResponse (8-15)
src/components/Global/Icons/Icon.tsx (1)
src/components/Global/Icons/chevron-down.tsx (1)
  • ChevronDownIcon (3-10)
src/components/LandingPage/CurrencySelect.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/LandingPage/dropLink.tsx (1)
src/components/0_Bruddle/Button.tsx (1)
  • Button (75-266)
src/components/LandingPage/RegulatedRails.tsx (1)
src/components/Global/MarqueeWrapper/index.tsx (1)
  • MarqueeWrapper (16-33)
src/components/LandingPage/yourMoney.tsx (1)
src/components/0_Bruddle/Button.tsx (1)
  • Button (75-266)
src/app/page.tsx (5)
src/components/LandingPage/RegulatedRails.tsx (1)
  • RegulatedRails (34-127)
src/components/LandingPage/marquee.tsx (1)
  • Marquee (11-24)
src/components/LandingPage/yourMoney.tsx (1)
  • YourMoney (50-87)
src/components/LandingPage/securityBuiltIn.tsx (1)
  • SecurityBuiltIn (48-91)
src/components/LandingPage/faq.tsx (1)
  • FAQs (16-44)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)


[error] 32-32: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)


[error] 38-38: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)


[error] 45-45: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)


[error] 52-52: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

src/components/LandingPage/RegulatedRails.tsx

[error] 119-119: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (18)
src/components/LandingPage/imageAssets.tsx (2)

4-4: LGTM! Clean asset replacement.

The import change from Cloud to borderCloud aligns with the updated visual design.


59-59: LGTM! Consistent asset usage throughout.

All cloud animations now consistently use the new borderCloud asset, maintaining visual coherence across the landing page.

Also applies to: 68-68, 89-89, 99-99

src/components/Global/Icons/chevron-down.tsx (1)

3-10: LGTM! Well-implemented SVG icon component.

The component follows React best practices:

  • Proper TypeScript typing with SVGProps<SVGSVGElement>
  • Uses currentColor for flexible theming
  • Spreads props correctly for customization
  • Clean, semantic SVG structure
src/app/api/bridge/exchange-rate/route.ts (2)

27-31: LGTM! Proper environment variable validation.

Good security practice to validate the API key presence before making external requests.


34-36: LGTM! Appropriate parameter validation.

Proper validation of required query parameters with clear error messaging.

src/components/LandingPage/securityBuiltIn.tsx (4)

21-24: LGTM! Content update aligns with new messaging.

The updated title "TOTAL SECURITY" and description clearly communicate the self-custodial nature and passkey authentication features.


30-33: LGTM! Clear messaging about KYC approach.

The "TRUE CONTROL" title and description effectively communicate the selective KYC approach, emphasizing user control and reduced friction.


39-42: LGTM! Support messaging is clear and compelling.

The "24/7 HELP" title and description emphasize human support accessibility, which is a strong differentiator.


53-55: LGTM! Consistent heading alignment.

The updated heading "SECURITY. CONTROL. SUPPORT." with left alignment maintains visual consistency with the content structure.

src/assets/illustrations/index.ts (1)

14-14: LGTM! Clean export addition.

The new LandingCountries export follows the existing pattern and supports the landing page redesign.

src/components/Global/Icons/Icon.tsx (1)

58-58: LGTM! Clean icon implementation.

The new chevron-down icon is properly integrated with correct import, type definition, and component mapping. The implementation follows the established pattern consistently.

Also applies to: 118-118, 183-183

src/components/LandingPage/index.ts (1)

1-1: LGTM! Export replacement aligns with component refactoring.

The export change from businessIntegrate to dropLink is consistent with the component replacement mentioned in the AI summary and maintains the public API structure.

src/components/LandingPage/dropLink.tsx (1)

46-56: LGTM! Smooth animation implementation.

The Framer Motion animation provides a subtle, engaging effect that enhances the user experience without being distracting. The configuration with rotation and translation creates a natural floating appearance.

src/app/page.tsx (3)

5-13: Component import reorganization looks good

The updated imports properly reflect the landing page reorganization, replacing BusinessIntegrate with DropLink and adding the new components.


191-195: Good structural improvements to the landing page flow

The reordering of components creates a better narrative flow:

  1. Hero → NoFees (engagement)
  2. RegulatedRails (trust/compliance)
  3. YourMoney → SecurityBuiltIn (features)
  4. SendInSeconds → DropLink (CTAs)

This structure progressively builds trust while maintaining user engagement through the Marquee separators.


203-203: FAQs component correctly receives marquee prop

The marquee prop is properly passed to the FAQs component with visible: false to disable the internal marquee.

src/components/LandingPage/yourMoney.tsx (1)

53-85: Effective redesign with improved visual hierarchy

The new two-column layout with the overlaid CTA button creates better visual engagement and clearer user action paths. The positioning of the button over the countries illustration is particularly effective.

src/assets/icons/index.ts (1)

10-19: New icon exports properly added

The bank and payment provider icons are correctly exported and align with the landing page requirements.

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

♻️ Duplicate comments (4)
src/components/LandingPage/noFees.tsx (3)

208-216: Improve amount input UX: handle empty input and limit decimals; add step/placeholder

This avoids flickering to 0 while typing empty values and standardizes to 2 decimals. Complements the earlier validation comment.

                             <input
                                 min={0}
                                 value={sourceAmount}
-                                onChange={(e) => {
-                                    const value = parseFloat(e.target.value)
-                                    setSourceAmount(isNaN(value) || value < 0 ? 0 : value)
-                                }}
+                                onChange={(e) => {
+                                    const raw = e.target.value
+                                    if (raw === '') {
+                                        setSourceAmount(0)
+                                        return
+                                    }
+                                    const num = Number(raw)
+                                    if (Number.isFinite(num) && num >= 0) {
+                                        // limit to 2 decimals
+                                        setSourceAmount(Math.round(num * 100) / 100)
+                                    }
+                                }}
                                 type="number"
+                                step="0.01"
+                                placeholder="0.00"
                                 className="w-full bg-transparent outline-none"
                             />

105-116: Make exchange-rate fetch resilient: add try/catch, response.ok check, abort on unmount, and guard against stale/invalid data

Builds on the earlier bot comment. Also avoids race conditions when params change quickly and prevents NaN from reaching state. Prefer toLowerCase() for deterministic casing.

-    useEffect(() => {
-        const fetchExchangeRate = async () => {
-            const _sourceCurrency = sourceCurrency.toLocaleLowerCase()
-            const _destinationCurrency = destinationCurrency.toLocaleLowerCase()
-            const response = await fetch(`/api/bridge/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`)
-            const data = await response.json()
-            setCurrentExchangeRate(data.rates.buy)
-            setDestinationAmount(debouncedSourceAmount * data.rates.buy)
-        }
-
-        fetchExchangeRate()
-    }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])
+    useEffect(() => {
+        let isMounted = true
+        const controller = new AbortController()
+
+        const fetchExchangeRate = async () => {
+            try {
+                const from = sourceCurrency.toLowerCase()
+                const to = destinationCurrency.toLowerCase()
+                if (!from || !to) return
+
+                const response = await fetch(
+                    `/api/bridge/exchange-rate?from=${from}&to=${to}`,
+                    { signal: controller.signal }
+                )
+                if (!response.ok) {
+                    // Keep previous rate, reset destination amount for safety
+                    if (isMounted) {
+                        setCurrentExchangeRate(0)
+                        setDestinationAmount(0)
+                    }
+                    return
+                }
+
+                const data = await response.json()
+                const rate = Number(data?.rates?.buy)
+                if (!Number.isFinite(rate) || rate <= 0) {
+                    if (isMounted) {
+                        setCurrentExchangeRate(0)
+                        setDestinationAmount(0)
+                    }
+                    return
+                }
+
+                if (isMounted) {
+                    setCurrentExchangeRate(rate)
+                    setDestinationAmount(Number.isFinite(debouncedSourceAmount) ? debouncedSourceAmount * rate : 0)
+                }
+            } catch (err: any) {
+                if (err?.name !== 'AbortError') {
+                    console.error('Error fetching exchange rate:', err)
+                }
+                if (isMounted) {
+                    setCurrentExchangeRate(0)
+                    setDestinationAmount(0)
+                }
+            }
+        }
+
+        fetchExchangeRate()
+        return () => {
+            isMounted = false
+            controller.abort()
+        }
+    }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])

272-274: Wire up the Send Money button (and disable when not ready)

Currently non-functional; route to the appropriate page with current params and guard against empty/invalid amounts.

-                    <Button icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold">
+                    <Button
+                        icon="arrow-up-right"
+                        iconSize={13}
+                        shadowSize="4"
+                        className="w-full text-base font-bold"
+                        disabled={!Number.isFinite(destinationAmount) || destinationAmount <= 0}
+                        onClick={() => {
+                            // TODO: confirm destination route
+                            router.push(`/setup?from=${encodeURIComponent(sourceCurrency)}&to=${encodeURIComponent(destinationCurrency)}&amount=${encodeURIComponent(String(sourceAmount))}`)
+                        }}
+                    >
                         Send Money
                     </Button>

If the actual route differs (e.g., /send or /request/pay), please confirm the correct path and I’ll update the handler accordingly.

src/components/LandingPage/Footer.tsx (1)

18-20: Add missing rel="noopener noreferrer" to external link (security).

The Squirrel Labs link opens a new tab without rel, which enables reverse-tabnabbing.

Apply this diff:

-                    <a className="underline" href="https://squirrellabs.dev/" target="_blank">
+                    <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
🧹 Nitpick comments (3)
src/components/LandingPage/noFees.tsx (2)

90-103: Remove unused parameter from createCloudAnimation signature

The top arg isn’t used inside createCloudAnimation (positioning is done via the style prop at call sites). Simplify the signature and call sites.

-    const createCloudAnimation = (side: 'left' | 'right', top: string, width: number, speed: number) => {
+    const createCloudAnimation = (side: 'left' | 'right', width: number, speed: number) => {
         const vpWidth = screenWidth || 1080
         const totalDistance = vpWidth + width
         return {
             initial: { x: side === 'left' ? -width : vpWidth },
             animate: { x: side === 'left' ? vpWidth : -width },
             transition: {
                 ease: 'linear',
                 duration: totalDistance / speed,
                 repeat: Infinity,
             },
         }
     }
@@
-                    {...createCloudAnimation('left', '20%', 200, 35)}
+                    {...createCloudAnimation('left', 200, 35)}
@@
-                    {...createCloudAnimation('right', '60%', 220, 40)}
+                    {...createCloudAnimation('right', 220, 40)}

Also applies to: 127-135


252-256: Format the displayed rate for readability

Show a bounded number of fractional digits.

-                            1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}
+                            1 {sourceCurrency} = {new Intl.NumberFormat(undefined, { maximumFractionDigits: 6 }).format(currentExchangeRate)} {destinationCurrency}
src/components/LandingPage/Footer.tsx (1)

26-29: Social icons are non-interactive; either link them or mark them decorative.

If these are intended to link to your socials, wrap each in with href/rel/aria-label. If they’re purely decorative, set alt="" and aria-hidden="true".

Example (link version):

<a href="https://discord.gg/your-invite" target="_blank" rel="noopener noreferrer" aria-label="Discord">
  <Image src={DISCORD_ICON} alt="" width={20} height={20} />
</a>

Example (decorative version):

-                    <Image src={DISCORD_ICON} alt="Discord" width={20} height={20} />
-                    <Image src={TWITTER_ICON} alt="Twitter" width={20} height={20} />
-                    <Image src={GITHUB_WHITE_ICON} alt="Github" width={20} height={20} />
+                    <Image src={DISCORD_ICON} alt="" width={20} height={20} aria-hidden="true" />
+                    <Image src={TWITTER_ICON} alt="" width={20} height={20} aria-hidden="true" />
+                    <Image src={GITHUB_WHITE_ICON} alt="" width={20} height={20} aria-hidden="true" />

If you keep meaningful alt text later, prefer “GitHub” casing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb701b0 and f13afc1.

⛔ Files ignored due to path filters (1)
  • src/assets/illustrations/landing-countries.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • src/components/LandingPage/Footer.tsx (1 hunks)
  • src/components/LandingPage/RegulatedRails.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/components/LandingPage/yourMoney.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/LandingPage/RegulatedRails.tsx
  • src/components/LandingPage/yourMoney.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
🧬 Code Graph Analysis (1)
src/components/LandingPage/noFees.tsx (3)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/0_Bruddle/Button.tsx (1)
  • Button (75-266)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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/LandingPage/noFees.tsx (1)

233-238: Good fix: make destination amount read-only

Prevents users from overriding the calculated value. This aligns with the calculated flow tied to the exchange rate.

src/components/LandingPage/Footer.tsx (1)

31-64: Nice fix: external links hardened and Terms URL whitespace removed.

Good job adding rel="noopener noreferrer" to all central external links and correcting the Terms URL.

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

♻️ Duplicate comments (7)
src/components/LandingPage/Footer.tsx (3)

67-75: Nice fix: replaced window.open with a semantic, secure, and accessible anchor.

Anchor + rel="noopener noreferrer" + aria-label is the right pattern. Alt="" on the icon is correct since the link has an accessible name.


18-20: Add missing rel="noopener noreferrer" on external link opening in new tab.

Security and lint issue: target="_blank" without rel opens you up to reverse-tabnabbing. All other links are correct; this one is the exception.

Apply this diff:

-                    <a className="underline" href="https://squirrellabs.dev/" target="_blank">
+                    <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">

76-78: Fix a11y and consistency: avoid .src on SVG imports; mark decorative icons properly.

Use the imported SVG modules directly (not .src) and hide decorative imagery from SRs with empty alt and aria-hidden.

Apply this diff:

-                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} />
-                <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} />
-                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} />
+                <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" />
+                <Image src={handMiddleFinger} alt="" width={20} height={20} aria-hidden="true" />
+                <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />

Note: This aligns with your previously stated preference for decorative images using empty alt attributes to avoid layout/SR issues.

src/components/LandingPage/noFees.tsx (4)

297-299: Send Money button needs functionality

The Send Money button doesn't have an onClick handler, making it non-functional. Based on the PR objectives mentioning incomplete API integration, this appears to be pending implementation.

Would you like me to help implement the Send Money button functionality? This could include:

  1. Navigation to a payment flow with pre-filled values
  2. Opening a modal for transaction details
  3. Integration with the payment processing API

Apply this diff as a starting point:

+    import { useRouter } from 'next/navigation'
+    // ... existing imports
+
+    // In the component (already imported at line 13)
+    // const router = useRouter()
+
     <Button 
         icon="arrow-up-right" 
         iconSize={13} 
         shadowSize="4" 
         className="w-full text-base font-bold"
+        onClick={() => {
+            // Navigate to payment flow with pre-filled values
+            const params = new URLSearchParams({
+                from: sourceCurrency,
+                to: destinationCurrency,
+                amount: sourceAmount.toString()
+            })
+            router.push(`/send?${params.toString()}`)
+        }}
     >
         Send Money
     </Button>

25-25: Improve robustness of URL parameter parsing

The current parsing of the amount parameter could result in NaN or negative values if invalid input is provided in the URL. This could lead to unexpected UI behavior.

Apply this diff to add proper validation:

-    const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10
+    const amountParam = searchParams.get('amount')
+    const parsedAmount = amountParam !== null ? parseFloat(amountParam) : 10
+    const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10

116-127: Add error handling for exchange rate API calls

The exchange rate fetch lacks error handling, which could cause the component to display incorrect data or have undefined behavior if the request fails.

Apply this diff to add proper error handling:

     useEffect(() => {
         const fetchExchangeRate = async () => {
+            try {
                 const _sourceCurrency = sourceCurrency
                 const _destinationCurrency = destinationCurrency
                 const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`)
+                
+                if (!response.ok) {
+                    console.error('Failed to fetch exchange rate:', response.status)
+                    // Optionally set an error state or use a fallback rate
+                    return
+                }
+                
                 const data = await response.json()
+                
+                // Validate the response structure
+                if (typeof data.rate !== 'number') {
+                    console.error('Invalid exchange rate response:', data)
+                    return
+                }
+                
                 setCurrentExchangeRate(data.rate)
                 setDestinationAmount(debouncedSourceAmount * data.rate)
+            } catch (error) {
+                console.error('Error fetching exchange rate:', error)
+                // Optionally set an error state to show user feedback
+            }
         }
 
         fetchExchangeRate()
     }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])

220-225: Improve input validation and UX for amount entry

The amount input needs better validation to handle edge cases like empty strings, leading dots, and provide better formatting for currency values.

Apply this diff to improve the input handling:

             <input
                 min={0}
                 value={sourceAmount}
                 onChange={(e) => {
-                    const value = parseFloat(e.target.value)
-                    setSourceAmount(isNaN(value) || value < 0 ? 0 : value)
+                    const value = e.target.value
+                    // Allow empty string for better UX
+                    if (value === '') {
+                        setSourceAmount(0)
+                        return
+                    }
+                    const numValue = parseFloat(value)
+                    if (!isNaN(numValue) && numValue >= 0) {
+                        // Limit to 2 decimal places for currency
+                        setSourceAmount(Math.round(numValue * 100) / 100)
+                    }
                 }}
                 type="number"
+                step="0.01"
+                placeholder="0.00"
                 className="w-full bg-transparent outline-none"
             />
🧹 Nitpick comments (4)
src/components/LandingPage/Footer.tsx (3)

26-29: Clarify intent of social icons (decorative vs. links) and adjust for a11y accordingly.

If these are decorative, hide them from SRs; if actionable, wrap with anchors and provide accessible names.

If decorative, apply:

-                    <Image src={DISCORD_ICON} alt="Discord" width={20} height={20} />
-                    <Image src={TWITTER_ICON} alt="Twitter" width={20} height={20} />
-                    <Image src={GITHUB_WHITE_ICON} alt="Github" width={20} height={20} />
+                    <Image src={DISCORD_ICON} alt="" width={20} height={20} aria-hidden="true" />
+                    <Image src={TWITTER_ICON} alt="" width={20} height={20} aria-hidden="true" />
+                    <Image src={GITHUB_WHITE_ICON} alt="" width={20} height={20} aria-hidden="true" />

If they should link out, I can provide a follow-up diff wrapping each in with target="_blank" rel attrs and visually hidden text.


31-64: Optional: wrap the footer link list in a nav for better semantics.

Improves landmark navigation and a11y without visual changes.

Apply this diff:

-                <div className="flex gap-2">
+                <nav aria-label="Footer links" className="flex gap-2">
   ...
-                </div>
+                </nav>

14-15: Nit: brand logo alt text.

Consider alt="Peanut" (brand name) or alt="" if the logo is purely decorative and adjacent text already names the brand.

src/components/LandingPage/noFees.tsx (1)

277-281: Consider formatting the exchange rate display

The exchange rate display might show many decimal places. Consider formatting it to a fixed number of decimal places for better readability.

Apply this diff to improve the display:

     {destinationAmount > 0 && (
         <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1">
-            1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}
+            1 {sourceCurrency} = {currentExchangeRate.toFixed(4)} {destinationCurrency}
         </div>
     )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f13afc1 and ae450fd.

📒 Files selected for processing (5)
  • src/app/api/exchange-rate/route.ts (1 hunks)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/Footer.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/constants/countryCurrencyMapping.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • src/app/api/exchange-rate/route.ts
  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.

Applied to files:

  • src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (1)
src/app/api/exchange-rate/route.ts (1)
src/app/actions/exchange-rate.ts (1)
  • ExchangeRateResponse (8-15)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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/LandingPage/noFees.tsx (1)

251-255: Good implementation of read-only destination amount

The destination amount input is correctly set as read-only, preventing users from manually editing the calculated value. This ensures the exchange rate calculation logic remains intact.

@Hugo0
Copy link
Contributor

Hugo0 commented Aug 13, 2025

  1. All coderabbit comments have to be resolved
  2. Currency selection widget has issues. See video below:
    Screencast from 2025-08-13 14-19-04.webm
  • a) widget repositions
  • b) it feels slow and not delightful after clicking a different currency; this is because there's no loading state and it waits until it gets the rate and then updates
  • c) on mobile the element gets hidden
image

@Hugo0
Copy link
Contributor

Hugo0 commented Aug 13, 2025

nit: clouds despawn ahead of time
Screencast from 2025-08-13 14-25-05.webm

@Hugo0
Copy link
Contributor

Hugo0 commented Aug 13, 2025

blocking issue: docs leads to peanut.to not peanut.me

blocking issue 2: It is still not clear on first load that the page is scrollable (neither on desktop nor mobile)
image

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

🔭 Outside diff range comments (1)
src/components/LandingPage/hero.tsx (1)

104-123: Remove unused scroll listener and state; it causes unnecessary re-renders

scrollY is never used. The scroll event handler will trigger re-renders needlessly on scroll.

-    const [scrollY, setScrollY] = useState(0)
+    // Removed unused scroll state

@@
-        const handleScroll = () => {
-            setScrollY(window.scrollY)
-        }
@@
-        window.addEventListener('scroll', handleScroll)
+        // Removed scroll listener

@@
-            window.removeEventListener('scroll', handleScroll)
+            // Removed scroll listener cleanup
♻️ Duplicate comments (3)
src/components/LandingPage/Footer.tsx (1)

18-21: Add missing rel attributes to external link (reverse tabnabbing)

Squirrel Labs link opens a new tab without rel. Add rel="noopener noreferrer".

-                    <a className="underline" href="https://squirrellabs.dev/" target="_blank">
+                    <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
src/components/LandingPage/noFees.tsx (2)

22-26: Harden parsing of amount query param to avoid NaN/negative values

Current parsing can propagate NaN and allows negatives. Clamp and default safely.

-    const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10
+    const amountParam = searchParams.get('amount')
+    const parsedAmount = amountParam !== null ? Number(amountParam) : 10
+    const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10

116-127: Fix exchange rate API integration, add loading/error handling, and use numeric rates

Endpoint and shape differ from the Bridge route introduced in this PR. Add basic error handling and loading state; use numeric rate consistently.

+    const [isRateLoading, setIsRateLoading] = useState(false)
@@
-    useEffect(() => {
-        const fetchExchangeRate = async () => {
-            const _sourceCurrency = sourceCurrency
-            const _destinationCurrency = destinationCurrency
-            const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`)
-            const data = await response.json()
-            setCurrentExchangeRate(data.rate)
-            setDestinationAmount(debouncedSourceAmount * data.rate)
-        }
-
-        fetchExchangeRate()
-    }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])
+    useEffect(() => {
+        const fetchExchangeRate = async () => {
+            setIsRateLoading(true)
+            try {
+                const from = sourceCurrency.toLowerCase()
+                const to = destinationCurrency.toLowerCase()
+                // Align with Bridge route added in this PR
+                const response = await fetch(`/api/bridge/exchange-rate?from=${from}&to=${to}`)
+                if (!response.ok) {
+                    console.error('Failed to fetch exchange rate', response.status)
+                    return
+                }
+                const data = await response.json()
+                // Bridge route returns numeric rates (parseFloat applied server-side)
+                const rate = data?.rates?.buy ?? data?.rate
+                if (typeof rate === 'number' && Number.isFinite(rate)) {
+                    setCurrentExchangeRate(rate)
+                    setDestinationAmount(debouncedSourceAmount * rate)
+                } else {
+                    console.error('Unexpected exchange rate payload', data)
+                }
+            } catch (err) {
+                console.error('Error fetching exchange rate', err)
+            } finally {
+                setIsRateLoading(false)
+            }
+        }
+        fetchExchangeRate()
+    }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])

If you prefer keeping /api/exchange-rate, align its response to match the Bridge route shape or adjust here accordingly. I can take either path—say the word.

🧹 Nitpick comments (5)
src/components/LandingPage/Footer.tsx (1)

76-78: Fix a11y and consistency for decorative hand icons

  • Use the imported SVG module directly (not .src).
  • Decorative images should have empty alt and aria-hidden.
-                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} />
-                <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} />
-                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} />
+                <Image src={handThumbsUp} alt="" aria-hidden="true" width={20} height={20} />
+                <Image src={handMiddleFinger} alt="" aria-hidden="true" width={20} height={20} />
+                <Image src={handWaving} alt="" aria-hidden="true" width={25} height={25} />
src/components/LandingPage/CurrencySelect.tsx (1)

139-148: Good: external flag image error-handling present

Hiding the broken image on error avoids broken UI. Consider adding a local fallback later, but this is acceptable for now.

src/components/LandingPage/noFees.tsx (3)

279-283: Include a loading hint for rate fetch

To address “interaction feels slow,” show a lightweight loading state while fetching.

-                    {destinationAmount > 0 && (
+                    {destinationAmount > 0 && (
                         <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1">
-                            1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}
+                            {isRateLoading
+                                ? 'Updating rate...'
+                                : <>1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}</>}
                         </div>
                     )}

219-228: Input UX: allow empty value and constrain decimals

Current handler forces 0 on empty input and doesn’t constrain decimals. Improve UX and numeric safety.

-                            <input
+                            <input
                                 min={0}
                                 value={sourceAmount}
                                 onChange={(e) => {
-                                    const value = parseFloat(e.target.value)
-                                    setSourceAmount(isNaN(value) || value < 0 ? 0 : value)
+                                    const raw = e.target.value
+                                    if (raw === '') {
+                                        setSourceAmount(0)
+                                        return
+                                    }
+                                    const num = Number(raw)
+                                    if (Number.isFinite(num) && num >= 0) {
+                                        // limit to 2 decimals for currency display
+                                        const rounded = Math.round(num * 100) / 100
+                                        setSourceAmount(rounded)
+                                    }
                                 }}
                                 type="number"
-                                className="w-full bg-transparent outline-none"
+                                step="0.01"
+                                placeholder="0.00"
+                                className="w-full bg-transparent outline-none"
                             />

299-307: Pass context when navigating from “Send Money”

Consider preserving the chosen values via query params to reduce friction on the next screen.

-                    <Button
-                        onClick={() => router.push('/setup')}
+                    <Button
+                        onClick={() =>
+                            router.push(`/setup?from=${sourceCurrency}&to=${destinationCurrency}&amount=${sourceAmount}`)
+                        }
                         icon="arrow-up-right"
                         iconSize={13}
                         shadowSize="4"
                         className="w-full text-base font-bold"
                     >
                         Send Money
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae450fd and 9665531.

📒 Files selected for processing (5)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/Footer.tsx (1 hunks)
  • src/components/LandingPage/hero.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/constants/countryCurrencyMapping.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (12)
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/CurrencySelect.tsx
  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.

Applied to files:

  • src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (3)
src/components/LandingPage/hero.tsx (1)
src/components/LandingPage/imageAssets.tsx (2)
  • CloudImages (51-116)
  • HeroImages (118-137)
src/components/LandingPage/CurrencySelect.tsx (2)
src/constants/countryCurrencyMapping.ts (1)
  • countryCurrencyMappings (8-30)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/LandingPage/noFees.tsx (4)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/constants/countryCurrencyMapping.ts (1)
  • countryCurrencyMappings (8-30)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/0_Bruddle/Button.tsx (1)
  • Button (75-266)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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/constants/countryCurrencyMapping.ts (1)

8-30: LGTM: concise dataset and interface fit the use-case

The mapping is well-scoped for the current selector and exports both named and default forms, which matches usage across components.

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

🔭 Outside diff range comments (5)
src/components/0_Bruddle/Button.tsx (5)

98-101: Fix forwardRef handling; current approach breaks with callback refs

Casting ref to React.RefObject and using it directly will break if a consumer passes a function ref (no .current), leading to runtime errors at Lines 108–111 and when assigning ref at Line 239. Merge refs via a stable callback and use a local ref internally.

Apply this diff:

-        const localRef = useRef<HTMLButtonElement>(null)
-        const buttonRef = (ref as React.RefObject<HTMLButtonElement>) || localRef
+        const localRef = useRef<HTMLButtonElement>(null)
+        const setRefs = useCallback(
+            (node: HTMLButtonElement | null) => {
+                localRef.current = node
+                if (typeof ref === 'function') {
+                    ref(node)
+                } else if (ref) {
+                    ;(ref as React.MutableRefObject<HTMLButtonElement | null>).current = node
+                }
+            },
+            [ref]
+        )
@@
-            if (!buttonRef.current) return
-            buttonRef.current.setAttribute('translate', 'no')
-            buttonRef.current.classList.add('notranslate')
+            if (!localRef.current) return
+            localRef.current.setAttribute('translate', 'no')
+            localRef.current.classList.add('notranslate')
         }, [])
@@
-                ref={buttonRef}
+                ref={setRefs}

Also applies to: 107-111, 239-239


186-195: Prevent interactions while loading and add aria-busy/disabled for UX/a11y

Clicks should be ignored while loading to prevent double submits; reflect state in DOM for accessibility.

Apply this diff:

         const handleClick = useCallback(
             (e: React.MouseEvent<HTMLButtonElement>) => {
+                if (loading) {
+                    return
+                }
                 if (longPress && !isLongPressed) {
                     // If long press is enabled but not completed, don't trigger onClick
                     return
                 }
                 onClick?.(e)
             },
-            [longPress, isLongPressed, onClick]
+            [loading, longPress, isLongPressed, onClick]
         )

And on the element:

             <button
                 className={twMerge(buttonClasses, 'notranslate', longPress && 'relative overflow-hidden')}
-                ref={buttonRef}
+                ref={setRefs}
                 translate="no"
+                aria-busy={loading || undefined}
+                disabled={loading || props.disabled}
                 onClick={handleClick}

Note: The ref prop reflects the earlier ref-fix comment.

Also applies to: 237-249


237-249: Default button type should be "button" to avoid accidental form submits

HTML default is type="submit". UI buttons often unintentionally submit forms.

Apply this diff:

             <button
+                type={props.type ?? 'button'}
                 className={twMerge(buttonClasses, 'notranslate', longPress && 'relative overflow-hidden')}

103-106: Use ReturnType<typeof setTimeout/setInterval> for timers to avoid DOM/Node type mismatches

Typing timers as NodeJS.Timeout can cause TypeScript errors in browser builds; use ReturnType / ReturnType to be environment-agnostic.

  • File: src/components/0_Bruddle/Button.tsx (lines ~103–106)

Apply this diff:

-        const [pressTimer, setPressTimer] = useState<NodeJS.Timeout | null>(null)
+        const [pressTimer, setPressTimer] = useState<ReturnType<typeof setTimeout> | null>(null)
@@
-        const [progressInterval, setProgressInterval] = useState<NodeJS.Timeout | null>(null)
+        const [progressInterval, setProgressInterval] = useState<ReturnType<typeof setInterval> | null>(null)

209-218: Fix: Tailwind JIT will purge the dynamic active:translate-y-[${shadowSize}px] — use a static map or safelist

Tailwind won't see the interpolated arbitrary class in src/components/0_Bruddle/Button.tsx and I didn't find a safelist/matchUtilities for these translate-y values in tailwind.config.js. Replace the dynamic interpolation with a small static map (or add an explicit safelist in tailwind.config.js).

Files to update

  • src/components/0_Bruddle/Button.tsx — replace the dynamic arbitrary class
  • tailwind.config.js — (optional) add a safelist if you prefer that approach

Suggested change (apply one of these):

  1. Static map (recommended)
+const activeTranslateByShadow: Record<ShadowSize, string> = {
+    '4': 'active:translate-y-[4px]',
+    '6': 'active:translate-y-[6px]',
+    '8': 'active:translate-y-[8px]',
+}

Then update the classes:

-        const buttonClasses = twMerge(
-            `btn w-full flex items-center gap-2 transition-all duration-100 active:translate-x-[3px] active:translate-y-[${shadowSize}px] active:shadow-none notranslate`,
+        const buttonClasses = twMerge(
+            `btn w-full flex items-center gap-2 transition-all duration-100 active:translate-x-[3px] active:shadow-none notranslate`,
             buttonVariants[variant],
@@
-            shadowSize && buttonShadows[shadowType || 'primary'][shadowSize],
+            shadowSize && buttonShadows[shadowType || 'primary'][shadowSize],
+            shadowSize && activeTranslateByShadow[shadowSize],
  1. Alternative: safelist the three classes in tailwind.config.js (if you prefer):
  • safelist: ['active:translate-y-[4px]', 'active:translate-y-[6px]', 'active:translate-y-[8px]'] (or equivalent regex)
♻️ Duplicate comments (4)
src/components/LandingPage/Footer.tsx (2)

18-20: Add missing security attribute to external link.

The Squirrel Labs link is missing rel="noopener noreferrer" which creates a security vulnerability.

-                    <a className="underline" href="https://squirrellabs.dev/" target="_blank">
+                    <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">

97-99: Fix inconsistent image source usage for hand icons.

The last three hand icons use .src property instead of passing the imported SVG directly like the handPeace icon does. Also, if these are decorative, they should have empty alt text with aria-hidden.

-                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} />
-                <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} />
-                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} />
+                <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" />
+                <Image src={handMiddleFinger} alt="" width={20} height={20} aria-hidden="true" />
+                <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />
src/components/LandingPage/noFees.tsx (2)

125-135: Add error handling for exchange rate API calls.

The API call lacks error handling, which could cause the component to display incorrect data if the request fails. Based on the learning from your conversation with Manu about error states, please ensure this follows the team's consistent error handling pattern.

     useEffect(() => {
         const fetchExchangeRate = async () => {
+            try {
                 const _sourceCurrency = sourceCurrency
                 const _destinationCurrency = destinationCurrency
                 const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`)
+                if (!response.ok) {
+                    console.error('Failed to fetch exchange rate')
+                    return
+                }
                 const data = await response.json()
                 setCurrentExchangeRate(data.rate)
                 setDestinationAmount(debouncedSourceAmount * data.rate)
+            } catch (error) {
+                console.error('Error fetching exchange rate:', error)
+                // Follow team's error handling pattern as discussed with Manu
+            }
         }

         fetchExchangeRate()
     }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])

23-25: Strengthen parsing of the amount query parameter.

The current parsing could allow NaN or negative values to flow into the state. Consider validating and clamping the parsed value.

-    const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10
+    const amountParam = searchParams.get('amount')
+    const parsedAmount = amountParam ? parseFloat(amountParam) : 10
+    const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
🧹 Nitpick comments (2)
src/components/0_Bruddle/Button.tsx (1)

242-247: Unify input handling with Pointer events and add keyboard support for long-press

Handling both mouse and touch separately is error-prone and lacks keyboard accessibility. Prefer Pointer events and support Space/Enter.

Apply this diff for the element handlers:

-                onMouseDown={longPress ? handlePressStart : undefined}
-                onMouseUp={longPress ? handlePressEnd : undefined}
-                onMouseLeave={longPress ? handlePressCancel : undefined}
-                onTouchStart={longPress ? handlePressStart : undefined}
-                onTouchEnd={longPress ? handlePressEnd : undefined}
-                onTouchCancel={longPress ? handlePressCancel : undefined}
+                onPointerDown={longPress ? handlePressStart : undefined}
+                onPointerUp={longPress ? handlePressEnd : undefined}
+                onPointerCancel={longPress ? handlePressCancel : undefined}
+                onKeyDown={
+                    longPress
+                        ? (e) => {
+                              if (e.key === ' ' || e.key === 'Enter') handlePressStart()
+                          }
+                        : undefined
+                }
+                onKeyUp={
+                    longPress
+                        ? (e) => {
+                              if (e.key === ' ' || e.key === 'Enter') handlePressEnd()
+                          }
+                        : undefined
+                }

I can provide a small helper to normalize keys and avoid inline lambdas if you prefer.

src/components/LandingPage/noFees.tsx (1)

228-236: Improve amount input validation and UX.

The current input handling could be improved to handle edge cases better and provide a smoother user experience. Consider allowing empty input states and limiting decimal places for currency amounts.

             <input
                 min={0}
                 value={sourceAmount}
                 onChange={(e) => {
-                    const value = parseFloat(e.target.value)
-                    setSourceAmount(isNaN(value) || value < 0 ? 0 : value)
+                    const value = e.target.value
+                    // Allow empty string for better UX
+                    if (value === '') {
+                        setSourceAmount(0)
+                        return
+                    }
+                    const numValue = parseFloat(value)
+                    if (!isNaN(numValue) && numValue >= 0) {
+                        // Limit to 2 decimal places for currency
+                        setSourceAmount(Math.round(numValue * 100) / 100)
+                    }
                 }}
                 type="number"
+                step="0.01"
+                placeholder="0.00"
                 className="w-full bg-transparent outline-none"
             />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1434d97 and 41f247a.

📒 Files selected for processing (5)
  • src/app/exchange/page.tsx (1 hunks)
  • src/components/0_Bruddle/Button.tsx (1 hunks)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/Footer.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (14)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.

Applied to files:

  • src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (2)
src/app/exchange/page.tsx (1)
src/components/LandingPage/noFees.tsx (1)
  • NoFees (16-320)
src/components/LandingPage/noFees.tsx (4)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/constants/countryCurrencyMapping.ts (1)
  • countryCurrencyMappings (8-30)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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 (8)
src/components/0_Bruddle/Button.tsx (1)

1-1: Client-ifying Button is correct given hooks and interactivity

This component uses React hooks, timers, and DOM APIs; marking it as a client component is appropriate.

src/app/exchange/page.tsx (1)

1-13: LGTM! Clean page structure with proper imports.

The page component is well-structured with appropriate use of the 'use client' directive and proper component composition.

src/components/LandingPage/Footer.tsx (2)

27-50: Social media links look good with proper security attributes.

All social links now include proper rel="noopener noreferrer" attributes and semantic aria-labels for accessibility. Good job on addressing the security concerns!


53-60: Update docs link to peanut.me domain.

The docs link still points to peanut.to instead of peanut.me, which is a blocking issue mentioned in the PR objectives.

-                        href="https://docs.peanut.to/"
+                        href="https://docs.peanut.me/"

Likely an incorrect or invalid review comment.

src/components/LandingPage/noFees.tsx (4)

260-265: Good implementation of read-only destination amount.

The destination amount input is correctly set as read-only, preventing users from directly modifying the calculated value. The fixed decimal formatting ensures consistent display.


307-316: Send Money button correctly navigates to setup page.

The button implementation with onClick handler and navigation to '/setup' looks good and addresses the functionality requirement.


69-75: Good implementation of optimistic UI updates.

The setSourceAmount callback now includes an optimistic update using the current exchange rate, which addresses the UI sluggishness issue mentioned in the PR objectives. This provides immediate feedback to users while the API call completes.


138-138: Consider Portal implementation in CurrencySelect for mobile popover issues.

Based on the PR objectives mentioning that the currency widget gets hidden on mobile, and your previous confirmation that this was fixed using Portal rendering in CurrencySelect, the overflow-hidden here should work correctly with that solution.

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

♻️ Duplicate comments (3)
src/components/LandingPage/noFees.tsx (3)

117-117: Popover clipping: resolved via Portal — keeping overflow-hidden here is fine

Acknowledging the chosen fix. Rendering CurrencySelect in a Portal avoids clipping; no need to change container overflow.


23-27: Harden parsing of the amount query param (avoid NaN/negatives)

Using parseFloat with a fallback can still leak NaN/negative values. Clamp and validate for a better UX.

Apply this diff:

-    const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10
+    const amountParam = searchParams.get('amount')
+    const parsedAmount = amountParam !== null ? Number(amountParam) : 10
+    const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10

300-307: Make “Send Money” navigate with prefilled query params and reflect loading

Preserve the user’s selection and amount when navigating. Also reflect loading state on the button.

Apply this diff:

-                    <Button
-                        onClick={() => router.push('/setup')}
+                    <Button
+                        loading={isLoading}
+                        onClick={() => {
+                            const amount = typeof sourceAmount === 'number' ? sourceAmount : 0
+                            router.push(`/setup?from=${sourceCurrency}&to=${destinationCurrency}&amount=${amount}`)
+                        }}
                         icon="arrow-up-right"
                         iconSize={13}
                         shadowSize="4"
                         className="w-full text-base font-bold"
                     >
                         Send Money
                     </Button>
🧹 Nitpick comments (4)
src/hooks/useExchangeRate.ts (2)

48-51: Round stored numeric destination amount to match displayed precision

You round the display string to 2 decimals, but keep the numeric state unrounded. Aligning both avoids subtle drift and ensures consistent downstream usage.

Apply this diff:

-    const updateDestinationFromCalculation = (calculatedAmount: number) => {
-        setDestinationAmount(calculatedAmount)
-        setDestinationInputValue(calculatedAmount.toFixed(2))
-    }
+    const updateDestinationFromCalculation = (calculatedAmount: number) => {
+        const rounded = parseFloat(calculatedAmount.toFixed(2))
+        setDestinationAmount(rounded)
+        setDestinationInputValue(rounded.toFixed(2))
+    }

65-75: Small cleanup: simplify destination display logic

The final else on Line 74 can only be a number or empty string; the String() fallback is redundant. Tighten the logic.

Apply this diff:

-    const getDestinationDisplayValue = useCallback(() => {
+    const getDestinationDisplayValue = useCallback(() => {
         if (lastEditedField === 'destination') {
             return destinationInputValue
         }
 
         if (destinationAmount === '') {
             return ''
         }
 
-        return typeof destinationAmount === 'number' ? destinationAmount.toFixed(2) : String(destinationAmount)
+        return (destinationAmount as number).toFixed(2)
     }, [lastEditedField, destinationInputValue, destinationAmount])
src/components/LandingPage/noFees.tsx (2)

45-57: Avoid relying on captured searchParams; preserve path when updating URL

URL mutation using the captured searchParams snapshot can be stale if concurrent updates occur; also, replacing only the query omits the path in some contexts. Using usePathname and reconstructing is more robust.

Apply this diff and update imports accordingly:

-    const updateUrlParams = useCallback(
-        (params: { from?: string; to?: string; amount?: number }) => {
-            const newSearchParams = new URLSearchParams(searchParams.toString())
+    const updateUrlParams = useCallback(
+        (params: { from?: string; to?: string; amount?: number }) => {
+            const newSearchParams = new URLSearchParams(searchParams.toString())
 
             if (params.from) newSearchParams.set('from', params.from)
             if (params.to) newSearchParams.set('to', params.to)
             if (params.amount !== undefined) newSearchParams.set('amount', params.amount.toString())
 
-            router.replace(`?${newSearchParams.toString()}`, { scroll: false })
+            router.replace(`${window.location.pathname}?${newSearchParams.toString()}`, { scroll: false })
         },
         [searchParams, router]
     )

Also add the import if you prefer SSR-safe approach:

// Additionally, you can use usePathname for SSR-friendly path retrieval
import { useSearchParams, useRouter, usePathname } from 'next/navigation'
const pathname = usePathname()
// Then replace window.location.pathname with pathname

206-221: Improve numeric input UX (decimal keypad, step, and empty handling)

Small tweaks improve input behavior across browsers and mobile.

Apply this diff:

-                            <input
+                            <input
                                 min={0}
                                 placeholder="0"
                                 value={sourceAmount === '' ? '' : sourceAmount}
                                 onChange={(e) => {
                                     const inputValue = e.target.value
                                     if (inputValue === '') {
                                         handleSourceAmountChange('')
                                     } else {
                                         const value = parseFloat(inputValue)
                                         handleSourceAmountChange(isNaN(value) ? '' : value)
                                     }
                                 }}
-                                type="number"
+                                type="number"
+                                inputMode="decimal"
+                                step="any"
                                 className="w-full bg-transparent outline-none"
                             />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 41f247a and dc383c1.

📒 Files selected for processing (3)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/hooks/useExchangeRate.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (12)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.

Applied to files:

  • src/components/LandingPage/noFees.tsx
🧬 Code Graph Analysis (2)
src/hooks/useExchangeRate.ts (1)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/components/LandingPage/noFees.tsx (5)
src/hooks/useExchangeRate.ts (1)
  • useExchangeRate (23-139)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/constants/countryCurrencyMapping.ts (1)
  • countryCurrencyMappings (8-30)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
🔇 Additional comments (4)
src/hooks/useExchangeRate.ts (1)

41-41: Should 0 be considered a valid amount?

Currently amounts must be > 0. If a user enters 0, you clear computed fields, which can feel jumpy. If 0 should yield 0 on the other side, loosen the guard to allow 0.

If desired, change the predicate:

-    const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount > 0
+    const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount >= 0

Confirm product expectations for zero inputs before applying.

src/components/LandingPage/noFees.tsx (3)

223-238: Guard against missing flag codes; provide fallback UI

If the mapping lacks a flagCode (or it's 'eu' and CDN fails), the image 404s. Render a fallback icon to avoid broken images.

Apply this diff:

-                            <CurrencySelect
+                            <CurrencySelect
                                 selectedCurrency={sourceCurrency}
                                 setSelectedCurrency={setSourceCurrency}
                                 excludeCurrencies={[destinationCurrency]}
                                 trigger={
-                                    <button className="flex w-32 items-center gap-2">
-                                        <Image
-                                            src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`}
-                                            alt={`${sourceCurrencyFlag} flag`}
-                                            width={160}
-                                            height={160}
-                                            className="size-4 rounded-full object-cover"
-                                        />
-                                        {sourceCurrency} <Icon name="chevron-down" className="text-gray-1" size={10} />
-                                    </button>
+                                    <button className="flex w-32 items-center gap-2">
+                                        {sourceCurrencyFlag ? (
+                                            <Image
+                                                src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`}
+                                                alt={`${sourceCurrencyFlag} flag`}
+                                                width={160}
+                                                height={160}
+                                                className="size-4 rounded-full object-cover"
+                                            />
+                                        ) : (
+                                            <Icon name="globe" size={16} className="text-gray-1" />
+                                        )}
+                                        {sourceCurrency} <Icon name="chevron-down" className="text-gray-1" size={10} />
+                                    </button>
                                 }
                             />

Repeat similarly for Lines 266-277 (destination).


246-260: Confirm product decision: allow manual editing of destination amount

Earlier comments suggested making destination read-only to avoid breaking calculations. This UI permits two-way editing and the hook supports it. Align with product: do we want two-way conversion, or source-only input?

If one-way is desired, remove onChange and add readOnly. If two-way is intended, keep as is; the hook logic looks sound.


15-15: Import is valid — no change required

Verified: src/constants/countryCurrencyMapping.ts declares a named export (export const countryCurrencyMappings at line 8) and also re-exports it as the default (export default countryCurrencyMappings at line 32). The default import in src/components/LandingPage/noFees.tsx (line 15) is therefore correct and will not break the build.

Files/locations:

  • src/constants/countryCurrencyMapping.ts — named export at line 8; default export at line 32
  • src/components/LandingPage/noFees.tsx — default import at line 15

Optional: consider removing the duplicate export (pick either named or default) for clarity, but no fix required for this PR.

Likely an incorrect or invalid review comment.

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

🔭 Outside diff range comments (1)
src/components/LandingPage/noFees.tsx (1)

1-347: Blocking — mixed docs domains (docs.peanut.to vs docs.peanut.me); please standardize before merge

Repo search shows mixed references to the docs domain. Please pick the canonical docs host and update the occurrences below:

Also note there are other domain variants in the repo (peanut.me, peanut.to, legacy.peanut.to, api.peanut.me, did:web:peanut.to). After you confirm the canonical domain for docs, update the listed files (and redirects/configs if needed) so all docs links are consistent.

♻️ Duplicate comments (8)
src/app/api/exchange-rate/route.ts (5)

22-29: Add currency-code validation for robustness and to prevent unnecessary external calls

Validate ISO 4217 codes early. This avoids calling upstream APIs with invalid params and aligns with prior guidance.

Apply this diff:

         // Validate required parameters
         if (!from || !to) {
             return NextResponse.json({ error: 'Missing required parameters: from and to' }, { status: 400 })
         }

-        const fromUc = from.toUpperCase()
-        const toUc = to.toUpperCase()
+        const fromUc = from.toUpperCase()
+        const toUc = to.toUpperCase()
+
+        // Basic ISO-4217 validation (3 uppercase letters)
+        const ISO4217 = /^[A-Z]{3}$/
+        if (!ISO4217.test(fromUc) || !ISO4217.test(toUc)) {
+            return NextResponse.json({ error: 'Invalid currency code format' }, { status: 400 })
+        }

135-143: Add timeout to Frankfurter fetch and keep existing SWR caching

Same rationale as Bridge: prevent hanging requests.

Apply this diff:

     const options: RequestInit & { next?: { revalidate?: number } } = {
         method: 'GET',
         next: { revalidate: 300 }, // Cache for 5 minutes
+        signal: AbortSignal.timeout(5000),
     }

69-72: Differentiate timeouts from generic server errors (return 504 on timeout)

Surface a proper 504 for timeouts. Helps client-side UX and diagnostics.

Apply this diff:

     } catch (error) {
-        console.error('Exchange rate API error:', error)
-        return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
+        if ((error as any)?.name === 'AbortError') {
+            console.error('Exchange rate API timeout')
+            return NextResponse.json({ error: 'Request timeout' }, { status: 504 })
+        }
+        console.error('Exchange rate API error:', error)
+        return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
     }

149-153: Validate Frankfurter response shape and numeric rate before using it

Avoid runtime errors when API schema changes or unsupported pairs are requested.

Apply this diff:

-    const data = await response.json()
-
-    const exchangeRate: ExchangeRateResponse = {
-        rate: data.rates[to] * 0.995, // Subtract 50bps
-    }
+    const data = await response.json()
+    const raw = data?.rates?.[to]
+    if (typeof raw !== 'number' || !Number.isFinite(raw) || raw <= 0) {
+        console.error('Invalid Frankfurter response structure or rate value', data)
+        return NextResponse.json({ error: 'Invalid exchange rate data' }, { status: 502 })
+    }
+    const exchangeRate: ExchangeRateResponse = {
+        rate: raw * 0.995, // Subtract 50bps
+    }

75-96: Add request timeout to Bridge fetch to avoid hanging requests

Wrap Bridge requests with a timeout to fail fast and improve perceived reliability. Prefer 5s.

Apply this diff:

     try {
         const url = `https://api.bridge.xyz/v0/exchange_rates?from=${from.toLowerCase()}&to=${to.toLowerCase()}`
         const options: RequestInit & { next?: { revalidate?: number } } = {
             method: 'GET',
             // Bridge expects header name 'Api-Key'
             headers: { 'Api-Key': bridgeAPIKey },
             next: { revalidate: 300 }, // Cache for 5 minutes
+            // Fail fast if upstream is slow
+            signal: AbortSignal.timeout(5000),
         }
src/components/LandingPage/noFees.tsx (2)

23-27: Harden parsing of amount query param to avoid NaN/negative values seeping into state

Current logic falls back to 10 on NaN but allows negative values. Clamp and ensure finite.

Apply this diff:

-    const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10
+    const amountParam = searchParams.get('amount')
+    const parsedAmount = amountParam !== null ? Number(amountParam) : 10
+    const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10

327-335: Preserve user selections when navigating: pass query params to /setup

Carry over currencies and amount to improve flow continuity and address UX feedback.

Apply this diff:

-                    <Button
-                        onClick={() => router.push('/setup')}
+                    <Button
+                        onClick={() =>
+                            router.push(
+                                `/setup?from=${encodeURIComponent(sourceCurrency)}&to=${encodeURIComponent(
+                                    destinationCurrency
+                                )}&amount=${encodeURIComponent(String(sourceAmount ?? ''))}`
+                            )
+                        }
                         icon="arrow-up-right"
                         iconSize={13}
                         shadowSize="4"
                         className="w-full text-base font-bold"
                     >
src/hooks/useExchangeRate.ts (1)

83-93: Harden the fetch: URI-encode params, validate payload, and avoid unnecessary requests

  • Encode user-selected currencies to prevent malformed URLs.
  • Validate payload and return a typed object to avoid downstream NaNs.
  • Skip the network call when currencies are identical via enabled.
  • Add a reasonable retry policy.

Apply this diff:

-    } = useQuery<{ rate: number }>({
-        queryKey: ['exchangeRate', sourceCurrency, destinationCurrency],
-        queryFn: async () => {
-            const res = await fetch(`/api/exchange-rate?from=${sourceCurrency}&to=${destinationCurrency}`)
-            if (!res.ok) throw new Error('Failed to fetch exchange rate')
-            return res.json()
-        },
-        staleTime: 2 * 60 * 1000, // 2 minutes
-        gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes
-        refetchOnWindowFocus: false,
-    })
+    } = useQuery<{ rate: number }>({
+        queryKey: ['exchangeRate', sourceCurrency, destinationCurrency],
+        queryFn: async () => {
+            const from = encodeURIComponent(sourceCurrency)
+            const to = encodeURIComponent(destinationCurrency)
+            const res = await fetch(`/api/exchange-rate?from=${from}&to=${to}`)
+            if (!res.ok) throw new Error(`Failed to fetch exchange rate (${res.status})`)
+            const data = await res.json()
+            const rate = Number(data?.rate)
+            if (!Number.isFinite(rate) || rate <= 0) {
+                throw new Error('Invalid exchange rate payload')
+            }
+            return { rate }
+        },
+        enabled: Boolean(sourceCurrency && destinationCurrency && sourceCurrency !== destinationCurrency),
+        staleTime: 2 * 60 * 1000, // 2 minutes
+        gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes
+        refetchOnWindowFocus: false,
+        retry: 2,
+    })
🧹 Nitpick comments (15)
src/app/api/exchange-rate/route.ts (1)

13-16: Optional: add basic rate limiting to protect the route in production

A simple in-memory limiter helps prevent abuse. Consider moving to a shared store (KV/Upstash) if deployed across instances.

Apply this diff:

 // Currency pairs that should use Bridge API (USD to these currencies only)
 const BRIDGE_PAIRS = new Set(['USD-EUR', 'USD-MXN', 'USD-BRL'])
 
+// Simple in-memory rate limiting (global). For multi-instance, use a shared store.
+const RATE_LIMIT_WINDOW_MS = 60_000
+const RATE_LIMIT_MAX = 60
+const rateHits: Map<string, number[]> = new Map()
+
 export async function GET(request: NextRequest) {
     try {
+        // naive IP detection; refine as needed
+        const ip =
+            request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
+            request.headers.get('x-real-ip') ||
+            'global'
+        const now = Date.now()
+        const hits = (rateHits.get(ip) || []).filter((t) => now - t < RATE_LIMIT_WINDOW_MS)
+        if (hits.length >= RATE_LIMIT_MAX) {
+            return NextResponse.json({ error: 'Too Many Requests' }, { status: 429 })
+        }
+        hits.push(now)
+        rateHits.set(ip, hits)
scripts/compare-rates.mjs (3)

67-75: Add timeouts to Bridge fetch to avoid hanging script runs

Fail fast when Bridge is slow or unresponsive.

Apply this diff:

-    const res = await fetch(url, {
+    const res = await fetch(url, {
         method: 'GET',
         headers: { 'Api-Key': BRIDGE_API_KEY },
-    })
+        signal: AbortSignal.timeout(5000),
+    })

80-89: Add timeout to Frankfurter fetch

Consistency with Bridge; improves UX on network hiccups.

Apply this diff:

-    const res = await fetch(url, { method: 'GET' })
+    const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(5000) })

91-104: Use timeout and encode query params when querying local API

Safer and more resilient on slow/invalid endpoints.

Apply this diff:

-        const url = `${apiArg}?from=${from}&to=${to}`
-        const res = await fetch(url, { method: 'GET' })
+        const url = `${apiArg}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
+        const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(5000) })
src/constants/countryCurrencyMapping.ts (1)

32-35: LGTM. Dataset is up-to-date (no HRK), and comingSoon flags are explicit

The mapping avoids HRK and uses EUR for Croatia implicitly via Eurozone. CHF appears once (Switzerland) which is fine; if we need to surface Liechtenstein later, we can document the duplicate usage of CHF in the consuming UI.

Optional: add a comment noting CHF is also used by Liechtenstein for clarity in downstream UIs.

src/components/LandingPage/CurrencySelect.tsx (2)

115-130: Apply the same stable key in the All currencies section

Consistency with the Popular section.

Apply this diff:

-                                            <CurrencyBox
-                                                key={`${currency.countryCode}-${currency.country}-${index}`}
+                                            <CurrencyBox
+                                                key={currency.currency}
                                                 countryCode={currency.countryCode}
                                                 country={currency.country}
                                                 currency={currency.currency}
                                                 currencyName={currency.currencyName}
                                                 comingSoon={currency.comingSoon}
                                                 selected={currency.currency === selectedCurrency}

83-112: Consider rendering a single unified list when searching to reduce jitter

When searchTerm is set, you render both “popular” and “all” groups (without headers), which may appear as a split list. Consider showing a single filtered list when searching.

src/components/LandingPage/noFees.tsx (4)

242-251: Add error handling/fallback for external flag images (source currency)

If the mapping doesn’t resolve or flagcdn fails, the UI shows a broken image. Hide the image on error or render a placeholder.

Apply this diff:

                                         <Image
-                                            src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`}
-                                            alt={`${sourceCurrencyFlag} flag`}
+                                            src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`}
+                                            alt={`${sourceCurrencyFlag ?? 'flag'} flag`}
                                             width={160}
                                             height={160}
                                             className="size-4 rounded-full object-cover"
+                                            onError={(e) => {
+                                                e.currentTarget.style.display = 'none'
+                                            }}
                                         />

286-295: Add error handling/fallback for external flag images (destination currency)

Same rationale as for source currency image.

Apply this diff:

                                         <Image
-                                            src={`https://flagcdn.com/w320/${destinationCurrencyFlag}.png`}
-                                            alt={`${destinationCurrencyFlag} flag`}
+                                            src={`https://flagcdn.com/w320/${destinationCurrencyFlag}.png`}
+                                            alt={`${destinationCurrencyFlag ?? 'flag'} flag`}
                                             width={160}
                                             height={160}
                                             className="size-4 rounded-full object-cover"
+                                            onError={(e) => {
+                                                e.currentTarget.style.display = 'none'
+                                            }}
                                         />

210-236: Visible loading state added: good step; consider optimistic updates for snappier UX

Skeletons help, but the selector still “waits for the rate” before updating. If your hook doesn’t already do it, optimistically compute destination amount using the last known rate on input change to reduce perceived latency.

If you want, I can propose a small refactor to useExchangeRate that maintains a ref of the last confirmed rate and uses it for immediate conversions while fetch is in-flight.

Also applies to: 256-281, 301-311


124-136: Potential clipping of overlays due to overflow-hidden (clouds/popovers)

You fixed currency popover clipping by using Portal (good). Note that overflow-hidden here can still cause background animation elements (clouds) to “despawn” early on some viewports. Consider overflow-visible on the outer section or increase cloud widths/speeds to minimize early exits.

src/hooks/useExchangeRate.ts (4)

41-41: Treat non-finite numbers as invalid amounts

Prevents NaN or Infinity from slipping through as “valid.” This is defensive and avoids edge-case glitches.

Apply this diff:

-    const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount > 0
+    const isValidAmount = (amount: InputValue): amount is number =>
+        typeof amount === 'number' && Number.isFinite(amount) && amount > 0

94-96: Improve UX: handle same-currency pairs and refine loading semantics

  • Use a deterministic rate of 1 when source === destination.
  • Don’t show a loading state during background refetches after the first rate is loaded.

Apply this diff:

-    const exchangeRate = rateData?.rate ?? 0
-    const isLoading = isFetching
+    const exchangeRate = sourceCurrency === destinationCurrency ? 1 : (rateData?.rate ?? 0)
+    const isLoading = !rateData && isFetching

131-134: Prop-driven initial amount changes may not recompute if destination was last edited

If initialSourceAmount changes while lastEditedField === 'destination', the destination won’t be recomputed from the new source. Resetting lastEditedField ensures the “initial load” path runs.

Apply this diff:

 useEffect(() => {
     setSourceAmount(initialSourceAmount)
+    setLastEditedField(null)
 }, [initialSourceAmount])

Can you confirm whether initialSourceAmount changes dynamically (e.g., when switching presets or currencies)? If so, this change will make the behavior consistent.


83-93: Optional: keep previous rate while fetching a new pair to avoid layout jank

Keeping the previous data until the new rate arrives prevents reflows/repositioning in the currency widget. Pair with an isFetching-based subtle loader to signal updating.

If you are on react-query v5, add placeholderData to keep previous data:

     } = useQuery<{ rate: number }>({
         queryKey: ['exchangeRate', sourceCurrency, destinationCurrency],
         queryFn: async () => {
             // ...
         },
+        placeholderData: (prev) => prev,
         enabled: Boolean(sourceCurrency && destinationCurrency && sourceCurrency !== destinationCurrency),
         staleTime: 2 * 60 * 1000, // 2 minutes
         gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes
         refetchOnWindowFocus: false,
         retry: 2,
     })

If on v4, an equivalent is keepPreviousData: true.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dc383c1 and a708c99.

📒 Files selected for processing (7)
  • scripts/compare-rates.mjs (1 hunks)
  • src/app/api/exchange-rate/route.ts (1 hunks)
  • src/components/LandingPage/CurrencySelect.tsx (1 hunks)
  • src/components/LandingPage/RegulatedRails.tsx (1 hunks)
  • src/components/LandingPage/noFees.tsx (4 hunks)
  • src/constants/countryCurrencyMapping.ts (1 hunks)
  • src/hooks/useExchangeRate.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/LandingPage/RegulatedRails.tsx
🧰 Additional context used
🧠 Learnings (15)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.

Applied to files:

  • scripts/compare-rates.mjs
  • src/components/LandingPage/noFees.tsx
  • src/app/api/exchange-rate/route.ts
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/LandingPage/noFees.tsx
  • src/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
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/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.

Applied to files:

  • src/components/LandingPage/noFees.tsx
  • src/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T12:46:10.663Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:282-285
Timestamp: 2025-08-14T12:46:10.663Z
Learning: In the peanutprotocol/peanut-ui project, loading states are implemented only after they have been properly designed by the design team to ensure consistency across the application.

Applied to files:

  • src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.

Applied to files:

  • src/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2025-08-14T14:42:54.399Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.399Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/constants/countryCurrencyMapping.ts
🧬 Code Graph Analysis (3)
src/hooks/useExchangeRate.ts (1)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/components/LandingPage/CurrencySelect.tsx (2)
src/constants/countryCurrencyMapping.ts (1)
  • countryCurrencyMappings (9-35)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (186-195)
src/app/api/exchange-rate/route.ts (2)
src/app/actions/exchange-rate.ts (1)
  • ExchangeRateResponse (8-15)
scripts/compare-rates.mjs (4)
  • url (67-67)
  • url (81-81)
  • url (94-94)
  • rate (87-87)
⏰ 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 (3)
src/components/LandingPage/CurrencySelect.tsx (1)

59-68: Good fix: rendering Popover content in a Portal avoids mobile clipping

This addresses the widget being hidden due to overflow-hidden ancestors.

src/hooks/useExchangeRate.ts (2)

25-35: Good separation of concerns; query-cached fetch + compute-only effect is the right direction

Swapping to react-query for fetch/caching and keeping a pure compute effect is a solid improvement over the prior single-effect approach. This directly addresses earlier race and over-fetching concerns.


89-91: No change needed — repo uses @tanstack/react-query v5 (gcTime is valid)

package.json shows @tanstack/react-query@5.8.4, and the hook uses gcTime — that's correct for v5, so no change required.

  • File: src/hooks/useExchangeRate.ts — lines ~89–91 (staleTime, gcTime, refetchOnWindowFocus)

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

♻️ Duplicate comments (2)
src/components/LandingPage/Footer.tsx (2)

18-20: Add missing rel="noopener noreferrer" to external link (security).

This external link opens a new tab but lacks rel attributes, exposing reverse-tabnabbing risk and tripping the linter. This was flagged earlier and appears unresolved here.

Apply this diff:

-                    <a className="underline" href="https://squirrellabs.dev/" target="_blank">
+                    <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">

97-107: Fix a11y for decorative icons and remove unnecessary .src usage on SVGs.

  • Decorative images should use alt="" and aria-hidden="true".
  • For the linked middle-finger icon, rely on the parent anchor’s aria-label and set the image alt="".
  • Pass the imported SVG modules directly to Next/Image (src={module}), not .src, for consistency.
  • Mirrors prior guidance and aligns with your established pattern (see retrieved learnings).

Apply this diff:

-                <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} />
+                <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" />
@@
-                    <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} />
+                    <Image src={handMiddleFinger} alt="" width={20} height={20} />
@@
-                <Image src={handWaving.src} alt="Hand waving" width={25} height={25} />
+                <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />
🧹 Nitpick comments (2)
src/components/LandingPage/Footer.tsx (2)

24-50: Optional: centralize social URLs and improve label wording.

  • Consider importing canonical social URLs (e.g., from src/components/Global/Footer/consts.ts) to avoid duplication and drift across components.
  • Minor a11y/copy tweak: “X (Twitter)” is clearer for users.

Example changes:

-                        aria-label="Follow us on Twitter"
+                        aria-label="Follow us on X (Twitter)"

And optionally replace hardcoded hrefs with imported constants:

// At the top of the file
// import { DISCORD_URL, X_URL, GITHUB_URL } from '@/components/Global/Footer/consts'

<a href={DISCORD_URL} ...>...</a>
<a href={X_URL} ...>...</a>
<a href={GITHUB_URL} ...>...</a>

14-15: Nit: consider making the brand logo a home link.

Common footer/header pattern is to link the logo to “/” for discoverability. Purely optional.

Example:

-                <div className="flex">
-                    <Image src={PEANUT_LOGO} alt="Peanut Logo" width={110} height={40} />
-                </div>
+                <a href="/" className="flex" aria-label="Peanut home">
+                    <Image src={PEANUT_LOGO} alt="Peanut" width={110} height={40} />
+                </a>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a708c99 and 969847d.

📒 Files selected for processing (1)
  • src/components/LandingPage/Footer.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.

Applied to files:

  • src/components/LandingPage/Footer.tsx
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx

[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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/LandingPage/Footer.tsx (1)

52-85: Docs domain fix and link hardening look good.

  • Docs now points to docs.peanut.me (resolves the blocking issue).
  • All these external links correctly include rel="noopener noreferrer".

@Hugo0 Hugo0 merged commit ee6e749 into peanut-wallet-dev Aug 14, 2025
4 of 6 checks passed
This was referenced Aug 15, 2025
jjramirezn added a commit that referenced this pull request Aug 21, 2025
* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Hugo0 added a commit that referenced this pull request Aug 21, 2025
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
jjramirezn added a commit that referenced this pull request Aug 25, 2025
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
jjramirezn added a commit that referenced this pull request Aug 29, 2025
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

* fix: bank claim flow runtime error (#1138)

* hotfix: make iban non optional (#1139)

* fix: bank claim flow runtime error

* fix: dont have iban as optional

* fix: merge conflicts

* fix: merge external account with bank details (#1140)

* fix: add id to external account (#1142)

* added tg footer (#1144)

* Hotfix : add missing countries - claim as guest flow (#1146)

* fix: add missing countries

* remove duplicate comment

* fix: show error on dynamic bank account form (#1147)

* fix: Invalid IBAN for UK (#1151)

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Co-authored-by: Hugo Montenegro <hugo@peanut.to>
jjramirezn added a commit that referenced this pull request Sep 3, 2025
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

* fix: bank claim flow runtime error (#1138)

* hotfix: make iban non optional (#1139)

* fix: bank claim flow runtime error

* fix: dont have iban as optional

* fix: merge conflicts

* fix: merge external account with bank details (#1140)

* fix: add id to external account (#1142)

* added tg footer (#1144)

* Hotfix : add missing countries - claim as guest flow (#1146)

* fix: add missing countries

* remove duplicate comment

* fix: show error on dynamic bank account form (#1147)

* fix: Invalid IBAN for UK (#1151)

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Co-authored-by: Hugo Montenegro <hugo@peanut.to>
jjramirezn added a commit that referenced this pull request Sep 4, 2025
* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* Prod to staging (#1124)

* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

* fix: dates in receipts (#1105)

* [TASK-13865] fix: add tx info on receipt (#1109)

* fix: add tx info on receipt

* feat: use address explorer url for depositor address

* fix(history): check befroe creating address explorer url

* Fix: logged in users have to re-login after installing PWA (#1103)

* store `LOCAL_STORAGE_WEB_AUTHN_KEY` in cookies

* ensure backward compatibility

* refactor: move syncLocalStorageToCookie call into useEffect for better lifecycle management

* feat: links v2.1 req fulfilment flows (#1085)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* feat: req fulfillment exchange flow

* fix: header title

* feat: req fulfillment using connected external wallet

* fix: navigation and ui

* fix: file name

* feat: abstract reusbale components from onramp flow for bank fulfilment

* feat: handle onramp creation for request fulfilment

* feat: reusable verification component

* feat: handle bank req fulfilment for peanut users

* fix: show all supported countries in req/claim bank flow

* feat: show google-pay/apple-pay based on users device

* fix: resolve pr review comments

* fix: exhange rate hook fallback value

* fix: resolve pr comments

* Feat: Collect tg username (#1110)

* feat: collect tg username

* update animations

* fix api route

* add thinking peanut gif

* fix typescript errors

* fix typo and reset telegramHandle field on logout

* fix: spacing and describe regex rules

* add missing export

* feat: add sound in success views (#1127)

* feat: handle history ui changes for links v2.1 (#1106)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* feat: req fulfillment exchange flow

* fix: header title

* feat: req fulfillment using connected external wallet

* fix: navigation and ui

* fix: file name

* feat: abstract reusbale components from onramp flow for bank fulfilment

* feat: handle onramp creation for request fulfilment

* feat: reusable verification component

* feat: handle bank req fulfilment for peanut users

* fix: show all supported countries in req/claim bank flow

* feat: show google-pay/apple-pay based on users device

* feat: handle bank send link claim hisotry for peanut users

* feat: handle history ui changes for request fulfillment using bank accounts

* fix: resolve pr review comments

* fix: exhange rate hook fallback value

* fix: resolve pr comments

* fix: review comments

* feat: badges updates (#1119)

* feat: badges updates and hook to check for interactions

* feat: handle badges for receipts and drawer header

* feat: handle badges on request and send flow

* feat: tooltip for badges

* fix: tooltip positioning

* fix: associate a external wallet claim to user if logged in (#1126)

* fix: associate a external wallet claim to user if logged in

* chore: fix comments

* [TASK-14113] fix: handle rpc outage when creating sendlinks (#1120)

* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* fix: handle rpc outage when creating sendlinks

* fix: formatting

* fix: parallelize geting deposit index

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>

* Integrate Daimo Pay (#1104)

* add daimo pay

* minor improvements

* cleanup and add success state

* resolve dependency issues

* fix: formatting

* fix: recent methods redirection

* add functions for daimo payment in request fulfilment flow

* Integrate daimo in request fulfilment flow

* remove hardcoded address

* add separate arbitrum usdc flow for deposits

* Add risk modal

* fix overlay blur

* Enhance loading state indication in payment process

* Add payer's address in deposit history entry

* Add validation

* add error handling

* remove action and move logic to API route

* fix errors

* fix: request flow

* fix: validation

* fixes

* add daimo flow in country specific method

* fix: slider not working on first attempt

* filter supported networks

* create reusable daimo button

* remove space

* remove route.ts file and move logic to server actions

* fix: infinite loading edge case

* update api and remove delay

* fix: layout shift

* fix: shadow

* update function name

* fix: success receipt (#1129)

* fix: roboto font not working (#1130)

* fix: allow cancel link from the claim page

* fix: allow canceling links from the shared receipt (#1134)

* fix: send flow cta (#1133)

* fix: send flow ctas

* fix: success sound on send flow

* fix: disabled btn on req pay flow

* Fix Daimo bugs (#1132)

* fix: bugs

* fix cross chain deposit details not correct

* fix: request screen UI

* add loading state

* remove old daimo button

* fix: missing dependencies and dead code

* add try catch finally block

* remove clear Daimo errors inside the balance-check effect

* fix copy

* minor fixes

* move ACTION_METHODS to constants file to remove circular dependency

* fix: circular dependency

* fix ts error

* update daimo version

* [TASK-14095] feat: add fallback transport to viem clients (#1131)

* feat: add fallback transport to viem clients

Use viem fallback transport to handle RPC errors and fallback to other
providers.

* style: Apply prettier formatting

* test: add fallback to viem mock

* fix: external claim links history ui + badges fix (#1136)

* fix: external claim links history ui + badges fix

* fix: resolve codderrabbit suggestions

* fix: coderrabbit comment on state stale

* Fix: disable add money button on default state + disable sound on IOS (#1145)

* fix: add money success screen shows usernmae

* disable add money button in default state

* disable sound on IOS

* Fix: daimo bugs part2 (#1149)

* fix: black screen on IOS

* fix: sucess screen showed without paying - add money flow

* fix currency and double $ in  txn history

* fix: x-chan token size and add API to get missing token icons

* fix: req fulfilment

* add default value to tokenData

* fix: move useeffect above transaction null check

* format amount

* fix: space between currency and amount (#1135)

* Lock token to USDC arb for peanut ens username (#1128)

* Lock token to USDC arb for peanut ens username

* add comment

* revert variable declaration for sanitizedValue in GeneralRecipientInput component

* fix add regex to strip only from the end

* [TASK-13900] Feat/kyc modal changes (#1137)

* fix: pointer events

* fix: modal btns not working on mobile

* add missing dependency

* remove close button

* Chore/prod to dev 106 (#1152)

* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

* fix: bank claim flow runtime error (#1138)

* hotfix: make iban non optional (#1139)

* fix: bank claim flow runtime error

* fix: dont have iban as optional

* fix: merge conflicts

* fix: merge external account with bank details (#1140)

* fix: add id to external account (#1142)

* added tg footer (#1144)

* Hotfix : add missing countries - claim as guest flow (#1146)

* fix: add missing countries

* remove duplicate comment

* fix: show error on dynamic bank account form (#1147)

* fix: Invalid IBAN for UK (#1151)

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Co-authored-by: Hugo Montenegro <hugo@peanut.to>

* [TASK-13950] Fix: incorrect token amount on second withdraw (#1150)

* fix: incorrect token amount on second withdraw

* move `resetTokenContextProvider()` to unmount callback

* fix: transaction explorer url for deposits

* fix: history skeleton copy

* save token and chain details for cross chain req-fulfilments (#1154)

* fix: send links history ui for senders pov when claimed to bank accounts (#1156)

* fix: sort action list methods

* fix: send links claimed to bank accounts history ui for senders pov

* fix: issues for request link paying with bank (#1158)

- Specify recipient when creating onramp for request fulfillment
- Use correct amount depending on currency

* fix: stop cleaning error by bic field (#1159)

We now always clear before starting submission and also bic field will
always show, so that logic is not needed anymore.

* fix: claim country currency and amount, fallback to $  (#1164)

* feat: show local bank currency incase of bank claims

* fix: activity rows for sender's send link history

* fix: verification modal when claiming

* fix: state issue when new user tries to claim to bank

* fix: request pay copy (#1165)

* fix: close kyc modal btn (#1166)

* Fix testing github action (#1167)

* chore: remove prettier action

When commiting it clashes with signature verification rules

* chore: update test action setup version

* fix: install first

* fix: actually make sure that cancelledDate is a Date (#1170)

* fix: icon and margin (#1171)

* Hide pay with wallet button in Daimo component (#1172)

* hide pay with wallet button

* improve targeted css approach

* Fix: Daimo bug and activity receipt bug (#1175)

* sligify chain name

* fix: stale state issue

* hide row if tokenData is not present

* Fix/conflicts (#1177)

* HOTFIX - IBAN country detection and incorrect bank acc details (#1094)

* Fix: Iban country detection and incorrect bank acc details

* Fix: update IBAN country validation to use correct locale string comparison

* add validations for US and mexican bank accounts

* fix typo

* fix claim flow and create a reusable function for getting 3 letter code

* fix country code mismatch

* fix: show error below input field

* remove unnecessary checks

* remove unnecessary CLABE check

* Prod LP v2.1 (#1098)

* feat: lpv2.1

* fix: gigaclouds, font and exchange widget

* fixes and improvements

* remove duplicate export

* remove unused component

* Fix: Landing page hero section responsiveness issue (#1107)

* fix: hero section responsiveness issue

* fix: stars position

* fix height on desktop

* remove unused code

* fix margins (#1113)

* [TASK-14052] Prod release 105 (#1122)

* feat: handle send link claims to bank account for peanut users (#1078)

* reafactor: create reusable country list component and use it for all the flows

* feat: reusable user accounts components

* feat: handle different cases based on kyc status for bank claim

* fix: account creation

* chore: add docstring to hooks

* chore: better comments for bank flow manager

* fix: kyc modal closing after tos acceptance issue

* fix: remove bank acc caching from withdraw flow

* fix: update confirm claim modal copy

* fix: remove bank acc caching from claim flow

* fix: navheader title

* remove duplicate debounce code and use `useDebounce` hook instead (#1079)

* Landing page v2.1 (#1089)

* lpv2.1 part 1

* Add exchange widget

* add and integrate exchange API

* add yourMoney component bg

* update landing countries svg

* integrate frankfurter API

* fixes and improvements

* decrease hero section height

* allow max 2 decimal places

* Add `/exchange` route

* fix: overlay

* make destination amount editable and bugg fixes

* some fixes & currency improvements

* crucial commit

* fix checkmark, font size and weight

---------

Co-authored-by: Hugo Montenegro <h@hugo0.com>

* [TASK-13186] refactor: use networkName instead of axelarChainName (#1095)

* refactor: use networkName instead of axelarChainName

* fix: types

* fix: onramp currency (#1096)

* fix: stretched favicon (#1099)

* [TASK-13971] fix: scientific notation in eip681 parsing (#1097)

* fix: scientific notation in eip681 parsing

* fix: qr handling tests

* fix: peanut sdk mock

* pull iban hotfix (#1100)

* fix: claim flow bugs (#1102)

* fix: cross chain claim

* fix: full name issue on confirm bank claim view

* fix: back navigation on desktop views

* Fix back button not working on /profile (#1101)

* Fix back button not working

* fix public profile page

* extract internal navigation logic to utility function

* fix: send link claims to us bank accounts (#1108)

* fix: usa bank account claims

* fix: show bank account details in confirm claim view

* Sync Landing page changes (#1111)

* reduce clouds size and update font

* fix: hero section responsiveness issue

* fix: formatting errors

* add currency animation

* fix: us bank claims after kyc for logged in users (#1112)

* fix: trim account form inputs for spaces (#1114)

* [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115)

* fix: don't allow claiming on xChain if route is not found

* fix(claim): use correct decimals for min receive amount

* feat: handle redirect uri when on unsupported browsers (#1117)

* feat: handle redirect uri when on unsupported browsers

* fix: confirm bank claim ui rows for iban guest claim

* remove animation (#1118)

* fix: formatting

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>

* fix: bank claim flow runtime error (#1138)

* hotfix: make iban non optional (#1139)

* fix: bank claim flow runtime error

* fix: dont have iban as optional

* fix: merge conflicts

* fix: merge external account with bank details (#1140)

* fix: add id to external account (#1142)

* added tg footer (#1144)

* Hotfix : add missing countries - claim as guest flow (#1146)

* fix: add missing countries

* remove duplicate comment

* fix: show error on dynamic bank account form (#1147)

* fix: Invalid IBAN for UK (#1151)

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Co-authored-by: Hugo Montenegro <hugo@peanut.to>

---------

Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Co-authored-by: Hugo Montenegro <h@hugo0.com>
Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com>
Co-authored-by: Juan José Ramírez <artjjrn@gmail.com>
Co-authored-by: Hugo Montenegro <hugo@peanut.to>
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.

3 participants