Skip to content

Prod to staging#993

Closed
jjramirezn wants to merge 5 commits intopeanut-wallet-devfrom
peanut-wallet
Closed

Prod to staging#993
jjramirezn wants to merge 5 commits intopeanut-wallet-devfrom
peanut-wallet

Conversation

@jjramirezn
Copy link
Contributor

No description provided.

jjramirezn and others added 5 commits July 7, 2025 17:12
* fix: update desktop navigation

* fix: support page
* feat: beta changes

- Banner that redirects to support page
- Funds warning modal

* refactor(banner): remove from landing and add hand thumbs up
* feat: abstract squid route fetching

Stop using the skd and use the squid API directly, this give us more
control and access to all the data that returns squid (for example,
we now have access to the fees and don't have to recalculate them
ourselves)

* refactor: use parseunits

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* refactor: remove console.dir

* feat: handle very large numbers with careful scaling

* refactor: use const for squid api url

* feat: add cross-chain action card

Use coral through squid to get the cross-chain route for the different
flows. This enables xchain withdraw for peanut wallet

* refactor: use decimals from token price instead of hardcoded

* style: Apply prettier formatting

* feat: show min received for cross-chain payments

* chore: clean unused variables

* fix: tests ach_pull to ach

* fix: pr comments

* fix: withdrawData and pulsate animation

* fix: cursor placement in token amount input

* fix: delete unused code

* fix: add camera perm modal

* fix: enable mexico bank offramp

* refactor: better comments for direct usd payments

* feat: add qr scanning for xchain

* fix: remove malicious code

* fix: handle mxn in offramp destination details (#945)

* fix: show share receipt option in direct send status view (#946)

* [TASK-12678] fix: coral withdraw fixes (#949)

* fix: coral withdraw fixes

- Stop refreshing the page after successful payment
- Better error message for unsupported token pairs

* chore: add squid env vars

* refactor: better copy for non rfq routes from peanut wallet

Also send warning to sentry

* fix: show amount in USD for withdrawals to other tokens to other tokens

* fix: lock orientation to portrait mode (#947)

* fix: lock orienatation to protrait mode

* style(fix): add new line

* style: format

* fix: if condition

* fix: update payment form for crosschain add money (#954)

* [TASK-12682] fix: show token selector for guest flow (#955)

* fix: show token selector for guest flow

* docs: add comment to explain url request flow

* implemented Context instead of sessionStorage in onrampData to avoid discrepancies with the Offramp logic (#953)

* refactor: fetch external wallet balances from mobula (#956) (#958)

* [TASK-12645] copy button for individual fields (#960)

* copy button for individual fields

* changed getOnrampCurrencyConfig (deprecated)to getCurrencyConfig + user now copies raw IBAN value

* fix: gas estimation for external wallets (#961)

We were re-rendering unnecesarily the prepare transaction, also now we
catch error if any

* [TASK-12603] reset send flow state when navigating to/from payment link creation (#964)

* reset send flow state when navigating to/from payment link creation

* linting (formatting)

* [TASK-12312] ENS/eth address network error being displayed on the frontend (#963)

* ENS/eth address network error being displayed on the frontend

* linting

* fix: centre align views on mobile (#950)

* [TASK-12542] added formatIban function for correct display (#952)

* added formatIban function for correct display

* coderabbit suggestion

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* updated formatting

* added iban formatting in all pages

---------

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* [TASK-12672] feat: show sponsored by peanut message (#959)

* feat: show sponsored by peanut message

This message is shown when we are making a transaction from the peanut
wallet. If the amount is more than one cent we also show the fee that
the user is saving by using peanut.

* fix(fees): show correct ammounts for externalwallet

* refactor: rename estimatedGasCostUsd to estimatedGasCostUsdUsd

* fix: correct approval owner and base rpc (#965)

Two things here:

1. We were using the peanut address for approval checking on deposits,
   this didn't affect because if the approval call failed we just
   continued with the deposit
2: The base rpc url was the sepolia one, not the mainnet. Big oversight
   there

* feat: add alachemy as fallback rpc url (#948)

* feat: add alachemy as fallback rpc url

* fix: remove commented line

* fix: address pr review comments

* [TASK-12866] fix: small fixes around crosschain ui (#966)

* fix: small fixes around crosschain ui

* fix: avoid negative expiry time

* removed white spaces before processing to BIC (#951)

* feat: resuable slider component (#968)

* fix: update desktop navigation with add/withdraw paths (#969)

* [TASK-11884] request via link with comment and attachment is creating 2 txs in the history (#943)

* PATCH implemented in frontend

* comment input state updating correctly (onBlur)

* freezing amount after updating comments/files input

* debounce attachment options and update requests on file change (avoiding race conditions when attaching files!)

* style: Apply prettier formatting

* removed malicious code

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* PATCH method using withFormData

* better onBlur logic

* bug fixes

* blur logic fixed

* nit pick comments

* code rabbit suggestion

* replaced useEffect setting state with derived state and debouncing

* disabling amount input after request creation

* linting

---------

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* fix: support page staging (#972)

* fix: update desktop navigation with add/withdraw paths

* fix: support page

* fix: ui comments (#973)

* Support and beta changes to staging (#978)

* refactor: fetch external wallet balances from mobula (#956)

* fix: update desktop navigation (#970)

* fix: support page prod (#971)

* fix: update desktop navigation

* fix: support page

* [TASK-12746] feat: beta changes (#975)

* feat: beta changes

- Banner that redirects to support page
- Funds warning modal

* refactor(banner): remove from landing and add hand thumbs up

---------

Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com>

* [TASK-12312] Bugfix/ens error withdrawing (#976)

* ENS/eth address network error being displayed on the frontend

* linting

* fixed andre QA observations

* deleting console.log

* formatting

* logic fixed

* fixed testst

---------

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* fix: coral small issues (#979)

* chore: enable usdt in mainnet (#980)

* fix: slider tap bug logic (#983)

* fix: slider tap bug logic

* fix: tap bug on slider

* fix: some issues and comments (#984)

* [TASK-12771] Feat/new landing page (#974)

* removed legacy button and changed try now button colors

* try now button color and hero description

* YourMoney component

* NoFees component first part

* stars done

* scribble in zero fees

* no hidden fees section done

* securityBuiltIn done

* sendInSeconds progress

* sendInSeconds done

* updated try now button to be a link

* business integration initiation

* businessIntegrate done

* order and marquee reusability

* order fixed

* background animations in Landing Page compoejnnts

* changed image for text in hero section

* figma details

* landing page responsiveness improvements

* landing page responsiveness done

* coderabbit suggestion

* business integration button

* fixed iphone screenshots

* CTA button animation

* added debugging for CTA button animation

* changed animation since react gave errors

* arrows in hero section

* NoFees new Zero

* no fees responsiveness fixed

* sendInSeconds details done

* coderabbit suggestions

* formatting

* hand-token wasnt pushed? forgot to stage or sum? anyways fixed

* build issues fixed

* coderabbit optimization

* code formatting

* arrows and ZERO thickness

* shade in try now button

* progress in button animation

* undoing button changes

* small detail fixes

* added asset

* changed peanut business to svg

* integrate peanut component done

* added pay-zero-fees.svg

* added new illustrations

* uout money anywhere svg

* securitybuiltin component SVGs

* adding YourMoney new SVGs

* your money anywhere component

* instantly send & receive svg

* arrows fixed

* button and arrows done

* desktop sendinseconds button done

* removed arrows disappearing effect

* MOBILE: hero button done

* added mobile svg

* yourMoney responsive component done

* added mobile-zero-fees.svg

* added no-hidden-fees.svg

* noFees mobile svg

* noFees desktop + mobile improvements

* noFees done

* mobile security built in SVG

* securityprivacy mobile done

* mobile-send-in-seconds.svg

* sendInSeconds mobile done

* business integrate mobile

* business integrate button and final details

* removed footer

* formatting

* removed pino-pretty

* fixed button instead of sticky/floating

---------

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>

* [TASK-13136] feat: allow uppercase in url (#986)

* feat: allow uppercase in url

* fix: allow uppercase in recipient

* fix: inform create request type when creating from UI (#987)

* fix: balance warning modal (#988)

- Slider instead of button
- Expiry for local storage instead of session storage
- Warning icon color

* chore: remove unused file (#990)

* chore: re-add removed code for gas estimation

---------

Signed-off-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Co-authored-by: facundobozzi <72771544+FacuBozzi@users.noreply.github.com>
@vercel
Copy link

vercel bot commented Jul 16, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
peanut-wallet ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 16, 2025 9:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

Walkthrough

This update introduces new environment variables, enhances cross-chain routing and swap logic, and adds several new UI components and features. It refactors wallet and payment flows for clarity, adds a balance warning modal, updates landing page animations and content, and restructures state management for onramp and withdraw flows. Numerous utility and configuration changes support these features.

Changes

Files/Groups Change Summary
.env.example, package.json Added new env variables for Alchemy, Squid, and balance warnings; added @radix-ui/react-slider dependency.
src/constants/general.consts.ts, src/constants/zerodev.consts.ts, src/config/justaname.config.tsx Refactored RPC URL management, switched to env-based configuration, added constants for Squid, USDT, and revert topics.
src/app/(mobile-ui)/home/page.tsx, src/components/Global/BalanceWarningModal/index.tsx, src/components/Slider/index.tsx, src/components/Global/Banner/index.tsx Added balance warning modal, slider UI, and refactored banner logic to use feedback and maintenance banners.
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx, src/context/OnrampFlowContext.tsx, src/components/AddMoney/components/AddMoneyBankDetails.tsx Switched onramp data management to context state, updated UI copy functionality, and improved currency config usage.
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, src/hooks/wallet/useWallet.ts Simplified withdraw logic, added sendMoney to wallet hook, improved IBAN formatting, and CLABE support.
src/app/(mobile-ui)/withdraw/crypto/page.tsx, src/hooks/usePaymentInitiator.ts, src/components/Withdraw/views/Confirm.withdraw.view.tsx, src/services/swap.ts Enhanced cross-chain routing, error handling, unified route expiry logic, and added comprehensive swap service.
src/app/(mobile-ui)/withdraw/page.tsx Added withdrawal state reset effect on mount.
src/app/(mobile-ui)/layout.tsx, src/app/layout.tsx, src/components/Global/ScreenOrientationLocker.tsx Added screen orientation lock and dynamic feedback banner.
src/app/[...recipient]/client.tsx, src/app/[...recipient]/page.tsx, src/lib/validation/recipient.ts, src/components/Global/GeneralRecipientInput/index.tsx Updated recipient handling: normalized to lowercase, improved ENS/username logic for withdrawals, and error messages.
src/components/Payment/PaymentForm/index.tsx, src/components/Payment/Views/Confirm.payment.view.tsx, src/components/Payment/Views/Initial.payment.view.tsx Refactored wallet connection state, renamed direct payment flags, improved cross-chain and fee display logic.
src/components/Payment/Views/Status.payment.view.tsx Added receipt link generation in transaction drawer.
src/components/Request/link/views/Create.request.link.view.tsx, src/services/requests.ts Refactored request link creation and update logic, added PATCH support, and improved error handling.
src/app/actions/tokens.ts, src/utils/balance.utils.ts Replaced old balance fetching with cached Mobula-based wallet balance logic, added transaction cost estimation.
src/app/actions/clients.ts, src/components/Create/useCreateLink.tsx, src/redux/slices/send-flow-slice.ts, src/redux/types/send-flow.types.ts Removed fee estimation logic and related state.
src/app/actions/ibanToBic.ts Preprocessed IBAN input by trimming whitespace.
src/app/actions/tokens.ts, src/utils/index.ts Added new utility exports and Mobula API types.
src/app/api/proxy/withFormData/[...slug]/route.ts Added PATCH handler and refactored form data handling.
src/app/api/walletconnect/fetch-wallet-balance/route.ts Deleted WalletConnect balance API route.
src/components/Global/DirectSendQR/index.tsx Added camera permission modal before QR scan, generalized payment confirmation modal.
src/components/Global/FileUploadInput/index.tsx, src/components/Global/TokenAmountInput/index.tsx Added onBlur prop support for file and token inputs.
src/components/Global/PeanutActionDetailsCard/index.tsx, src/components/Global/RouteExpiryTimer/index.tsx Added cross-chain timer support and new transaction type for add money bank account.
src/components/LandingPage/*, src/app/page.tsx Added new landing page sections, animated scroll logic, marquee, and business integration components.
src/components/TransactionDetails/TransactionCard.tsx, src/components/TransactionDetails/TransactionDetailsDrawer.tsx Improved stablecoin and IBAN display logic.
src/components/Send/link/LinkSendFlowManager.tsx, src/components/Send/views/SendRouter.view.tsx, src/components/Send/link/views/Initial.link.send.view.tsx, src/components/Send/link/views/Success.link.send.view.tsx Refined send flow state management and improved layout and error handling.
src/components/Global/Layout/index.tsx Removed footer rendering.
src/components/Global/WalletNavigation/index.tsx Split "Cashout" into "Add" and "Withdraw" navigation entries.
src/components/Home/HomeHistory.tsx Conditional rendering of KYC status.
src/components/Payment/PaymentInfoRow.tsx Added copy-to-clipboard functionality.
src/components/AddMoney/consts/index.ts Added Mexico to enabled countries, but bank transfer is still "soon" for MX.
src/components/AddWithdraw/*, src/components/AddMoney/views/CryptoDepositQR.view.tsx Adjusted layout classes for min-height and spacing.
src/components/CrispChat.tsx Simplified click handler to always redirect to support page.
src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx, src/lib/validation/recipient.test.ts Added/updated tests for withdrawal recipient logic and error messages.
src/utils/bridge.utils.ts, src/utils/__tests__/bridge.utils.test.ts Updated payment rail identifiers and related tests.
src/utils/general.utils.ts, src/utils/sdk.utils.ts Enhanced local storage expiry, provider fallback logic, and added transaction revert detection.
tailwind.config.js Added new pulse-strong animation.
src/assets/illustrations/index.ts Updated exported illustration assets.

Possibly related PRs

  • peanutprotocol/peanut-ui#919: Both PRs modify cross-chain withdrawal and routing logic, integrating cross-chain route data, timer handling, and swap enhancements.
  • peanutprotocol/peanut-ui#975: Both PRs extend the balance warning modal feature, add environment variables, and coordinate modal visibility logic on the home page.
  • peanutprotocol/peanut-ui#974: Both PRs add and export new landing page subcomponents (BusinessIntegrate, Marquee, NoFees, SecurityBuiltIn, SendInSeconds, YourMoney) and update the Hero and landing page for new animations and content.

Suggested labels

enhancement

Suggested reviewers

  • Hugo0
  • FacuBozzi
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch peanut-wallet

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Jul 16, 2025
@jjramirezn jjramirezn closed this Jul 16, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (30)
src/components/Global/ScreenOrientationLocker.tsx (1)

29-29: Use optional chaining for consistency.

Consider using optional chaining for better consistency with the rest of the code.

-        if (screen.orientation && screen.orientation.addEventListener) {
+        if (screen.orientation?.addEventListener) {
src/services/requests.ts (1)

39-58: LGTM! Clean implementation of update method.

The new update method follows good patterns:

  • Proper FormData handling with undefined value filtering
  • Correct use of PATCH method for partial updates
  • Consistent API endpoint construction

Consider applying the same enhanced error handling pattern used in the create method for consistency:

 if (!response.ok) {
+    let errorMessage = `Failed to update request: ${response.statusText}`
+    
+    try {
+        const errorData = await response.json()
+        if (errorData.error) {
+            errorMessage = errorData.error
+        }
+    } catch (parseError) {
+        console.warn('Could not parse error response:', parseError)
+    }
+    
-    throw new Error(`Failed to update request: ${response.statusText}`)
+    throw new Error(errorMessage)
 }
src/components/0_Bruddle/Button.tsx (1)

250-257: Consider making the progress bar colors themeable.

The hardcoded purple gradient might not work well with all button variants (e.g., green, yellow buttons). Consider making the progress bar colors configurable or variant-aware.

You could enhance the progress bar styling based on the button variant:

-                        className="absolute inset-0 bg-gradient-to-r from-purple-400 to-purple-600 opacity-30 transition-all duration-75 ease-out"
+                        className={twMerge(
+                            "absolute inset-0 opacity-30 transition-all duration-75 ease-out",
+                            variant === 'green' && "bg-gradient-to-r from-green-400 to-green-600",
+                            variant === 'yellow' && "bg-gradient-to-r from-yellow-400 to-yellow-600",
+                            variant === 'dark' && "bg-gradient-to-r from-gray-600 to-gray-800",
+                            !['green', 'yellow', 'dark'].includes(variant) && "bg-gradient-to-r from-purple-400 to-purple-600"
+                        )}
src/components/CrispChat.tsx (1)

9-11: Consider removing unused Crisp chat script.

The click handler now always redirects to /support instead of opening the Crisp chat widget. However, the CrispChat component still loads the Crisp chat script. If the chat functionality is no longer needed, consider removing the script loading as well to avoid loading unused resources.

If the Crisp chat script is no longer needed, you could remove the entire CrispChat component:

-export default function CrispChat() {
-    return (
-        <Script strategy="afterInteractive">
-            {`
-        window.$crisp=[];
-        window.CRISP_WEBSITE_ID="916078be-a6af-4696-82cb-bc08d43d9125";
-        (function(){
-          d=document;
-          s=d.createElement("script");
-          s.src="https://client.crisp.chat/l.js";
-          s.async=1;
-          d.getElementsByTagName("head")[0].appendChild(s);
-        })();
-        window.$crisp.push(["safe", true]);
-      `}
-        </Script>
-    )
-}
src/components/AddMoney/consts/index.ts (1)

2144-2144: Clarify the logic for Mexico's bank transfer availability.

Mexico is added to the enabled countries set but the bank transfer add method is still marked as "soon" (unavailable). This creates potentially confusing logic where Mexico is both enabled and disabled.

Consider adding a comment to explain this is intentional (e.g., for phased rollout or backend-only enablement).

-                newMethod.isSoon = !isCountryEnabledForBankTransfer(countryCode) || countryCode === 'MX'
+                // Mexico is enabled for backend but not yet available to users (phased rollout)
+                newMethod.isSoon = !isCountryEnabledForBankTransfer(countryCode) || countryCode === 'MX'
src/components/LandingPage/businessIntegrate.tsx (5)

8-8: Consider using CSS custom properties for better maintainability.

The hardcoded background color constant could be moved to a CSS custom property or theme configuration for better maintainability and consistency across the application.

-const businessBgColor = '#90A8ED'
+// Consider moving this to your theme configuration or CSS custom properties
+const businessBgColor = '#90A8ED'

32-36: Inline styles should be avoided in favor of CSS classes.

The inline fontWeight: 300 style could be replaced with a Tailwind CSS class for better consistency and performance.

-                            style={{ fontWeight: 300 }}
+                            className="relative translate-x-[2px] font-knerd-filled text-[4rem] text-white font-light md:translate-x-[3px] md:text-8xl"

43-43: Consider removing the unoptimized prop unless specifically needed.

The unoptimized prop on the Image component disables Next.js image optimization. Unless there's a specific reason for this (like SVG incompatibility), it's better to let Next.js handle the optimization.

-                            unoptimized

52-52: Inline styles should be moved to CSS classes.

The inline styles for font weight and letter spacing could be replaced with Tailwind CSS classes for better maintainability.

-                    style={{ fontWeight: 500, letterSpacing: '-0.5px' }}
+                    className="mb-8 max-w-xs font-roboto text-xl font-medium leading-tight tracking-tight md:mb-16 md:max-w-none md:text-4xl"

63-63: Inline style should be replaced with CSS class.

The inline fontWeight: 900 style could be replaced with a Tailwind CSS class.

-                    style={{ fontWeight: 900 }}
+                    className="btn-shadow-primary-4 inline-block rounded-sm border-[1px] border-n-1 bg-white px-8 py-4 font-roboto text-base font-black text-n-1 hover:bg-grey-2 focus:outline-none md:px-10 md:py-4 md:text-lg"

Note: The font-black class already provides font-weight: 900, so the inline style is redundant.

src/utils/sdk.utils.ts (1)

22-22: Minor typo in comment.

There's a small typo in the comment.

-    // f we have specific RPC URLs, use them with a FallbackProvider for resiliency.
+    // If we have specific RPC URLs, use them with a FallbackProvider for resiliency.
src/components/LandingPage/yourMoney.tsx (1)

12-19: Improve TypeScript interface precision.

The interface uses any types for image-related properties, which reduces type safety.

Consider using more specific types:

interface Feature {
    id: number
    title: string
-    titleSvg: any
+    titleSvg: string | StaticImageData
    description: string
-    imageSrc: any
+    imageSrc: string | StaticImageData
    imageAlt: string
}

Import the StaticImageData type:

+import type { StaticImageData } from 'next/image'
src/components/LandingPage/noFees.tsx (2)

14-14: Consider using a custom hook for screen width tracking.

The screen width state management could be extracted into a reusable custom hook to avoid code duplication across landing page components. This would also improve testability and maintainability.


63-112: Consider consolidating repetitive star animations.

The star animations have very similar patterns with only slight variations in positioning, rotation, and delay. This could be refactored into a more maintainable data-driven approach.

Consider creating a star configuration array:

+const starConfigs = [
+  { className: "absolute -right-36 -top-12", translateY: 20, translateX: 5, rotate: 22, delay: 0.2 },
+  { className: "absolute -right-58 top-30", translateY: 28, translateX: -5, rotate: -17, delay: 0.4 },
+  // ... other configs
+]

-<motion.img src={Star.src} alt="Floating Star" width={50} height={50} className="absolute -right-36 -top-12" initial={{ opacity: 0, translateY: 20, translateX: 5, rotate: 22 }} whileInView={{ opacity: 1, translateY: 0, translateX: 0, rotate: 22 }} transition={{ type: 'spring', damping: 5, delay: 0.2 }} />
+{starConfigs.map((config, index) => (
+  <motion.img
+    key={index}
+    src={Star.src}
+    alt="Floating Star"
+    width={50}
+    height={50}
+    className={config.className}
+    initial={{ opacity: 0, translateY: config.translateY, translateX: config.translateX, rotate: config.rotate }}
+    whileInView={{ opacity: 1, translateY: 0, translateX: 0, rotate: config.rotate }}
+    transition={{ type: 'spring', damping: 5, delay: config.delay }}
+  />
+))}
src/components/Slider/index.tsx (1)

72-79: Consider accessibility improvements.

The thumb element could benefit from proper ARIA attributes and keyboard navigation support for better accessibility.

Add accessibility attributes:

 <SliderPrimitive.Thumb
     onPointerDown={() => setIsDragging(true)}
-    className="flex h-full w-12 cursor-pointer items-center justify-center rounded-r-sm border-2 border-purple-1 bg-primary-1 py-3 ring-offset-black transition-colors focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50 "
+    className="flex h-full w-12 cursor-pointer items-center justify-center rounded-r-sm border-2 border-purple-1 bg-primary-1 py-3 ring-offset-black transition-colors focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50"
+    aria-label="Slide to proceed"
 >
src/components/LandingPage/securityBuiltIn.tsx (2)

12-19: Type safety improvement: Use proper typing for SVG imports.

The titleSvg and iconSrc properties are typed as any, which reduces type safety. Consider using a more specific type for SVG imports.

 interface Feature {
     id: number
     title: string
-    titleSvg: any
+    titleSvg: StaticImageData
     description: string
-    iconSrc: any
+    iconSrc: StaticImageData
     iconAlt: string
 }

You'll need to import the type:

+import { StaticImageData } from 'next/image'

88-95: Consider extracting magic numbers for max-width values.

The hardcoded max-width values (260px, max-w-sm) could be extracted into constants or design tokens for better maintainability.

src/components/Global/RouteExpiryTimer/index.tsx (2)

107-117: Review useEffect dependencies for potential optimization.

The dependency array is quite extensive and could potentially cause unnecessary re-renders. Consider if all dependencies are truly needed or if some can be memoized.

Consider memoizing the callbacks:

+const memoizedOnNearExpiry = useCallback(onNearExpiry, [])
+const memoizedOnExpired = useCallback(onExpired, [])

// Then use memoized versions in the dependency array

This would reduce the frequency of effect re-runs when parent components re-render.


142-147: Consider performance optimization for pulse calculation.

The shouldPulse calculation runs on every render. Consider if this needs to be memoized or if the dependencies could be reduced.

The current implementation is acceptable, but if performance becomes an issue, consider memoizing with fewer dependencies:

 const shouldPulse = useMemo((): boolean => {
     if (isLoading) return true
     if (!timeRemaining) return false
     return (progressPercentage >= 85 || timeRemaining.totalMs <= nearExpiryThresholdMs) && timeRemaining.totalMs > 0
-}, [progressPercentage, timeRemaining, isLoading, nearExpiryThresholdMs])
+}, [progressPercentage, timeRemaining?.totalMs, isLoading, nearExpiryThresholdMs])
src/constants/general.consts.ts (1)

3-3: Remove unused import

The baseSepolia chain is imported but not used in the rpcUrls configuration.

-import { mainnet, arbitrum, arbitrumSepolia, polygon, optimism, base, bsc, scroll, baseSepolia } from 'viem/chains'
+import { mainnet, arbitrum, arbitrumSepolia, polygon, optimism, base, bsc, scroll } from 'viem/chains'
src/components/LandingPage/sendInSeconds.tsx (2)

11-21: Consider debouncing resize handler for performance

The resize handler updates state on every resize event, which could cause excessive re-renders during window resizing. Consider debouncing to improve performance.

+import { debounce } from 'lodash' // or implement a simple debounce

 export function SendInSeconds() {
     const [screenWidth, setScreenWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1200)

     useEffect(() => {
-        const handleResize = () => {
+        const handleResize = debounce(() => {
             setScreenWidth(window.innerWidth)
-        }
+        }, 100)

         handleResize()
         window.addEventListener('resize', handleResize)
-        return () => window.removeEventListener('resize', handleResize)
+        return () => {
+            handleResize.cancel()
+            window.removeEventListener('resize', handleResize)
+        }
     }, [])

107-241: Consider performance optimization for animations

The component has many simultaneous animations (4 clouds, 4 stars) which might impact performance on lower-end devices. Consider adding a reduced motion preference check or limiting animations based on device capabilities.

Add reduced motion support:

const prefersReducedMotion = typeof window !== 'undefined' 
  ? window.matchMedia('(prefers-reduced-motion: reduce)').matches 
  : false;

// Then conditionally apply animations:
{!prefersReducedMotion && (
  <motion.img
    // ... animation props
  />
)}
src/components/Payment/PaymentForm/index.tsx (2)

513-554: Consider simplifying button disabled logic

The isButtonDisabled logic is comprehensive but complex. Consider extracting conditions into named functions for better readability and maintainability.

const isValidAmount = (amount: string): boolean => {
    const numericAmount = parseFloat(amount);
    return !isNaN(numericAmount) && numericAmount > 0;
};

const hasRequiredTokenData = (): boolean => {
    return !!selectedTokenAddress && !!selectedChainID;
};

const isButtonDisabled = useMemo(() => {
    if (isProcessing || !!error) return true;
    
    if (!isValidAmount(inputTokenAmount) && !isAddMoneyFlow) return true;
    
    if (isAddMoneyFlow) {
        if (!isExternalWalletConnected) return false; // Allow "Connect Wallet"
        return !isValidAmount(inputTokenAmount) || !hasRequiredTokenData() || isProcessing;
    }
    
    if (!isPintaReq && (!isConnected || !hasRequiredTokenData())) return true;
    
    return false;
}, [/* dependencies */]);

1-747: Consider decomposing this large component

This component has grown to 700+ lines with complex state management and conditional rendering. Consider extracting logical sections into smaller components for better maintainability:

  • Extract balance validation logic into a custom hook
  • Create separate components for different payment flows (Pinta, AddMoney, Regular)
  • Extract button state logic into a separate hook
  • Consider splitting the form sections into sub-components
src/components/LandingPage/hero.tsx (1)

136-136: Consider removing or documenting commented sparkle code.

The sparkle rendering is commented out but the renderSparkle helper function is still defined. If this is intentionally kept for future use, consider adding a TODO comment explaining why. Otherwise, remove both the commented line and the helper function.

src/app/page.tsx (1)

70-159: Well-implemented scroll animation with proper state management.

The scroll-driven animation logic is comprehensive with good use of refs for performance-critical values and proper cleanup. The phase transitions (freeze → grow → shrink) are clearly defined.

Consider extracting magic numbers as named constants for better maintainability:

+const STICKY_BUTTON_OFFSET = 60
+const SCROLL_THRESHOLD = 50
+const MAX_SHRINK_DISTANCE = 200
+const MAX_VIRTUAL_SCROLL = 500
+const SCALE_MULTIPLIER = 0.5
+const MAX_SCALE = 1.5

 // In the scroll handler:
-const shouldFreeze = targetRect.top <= stickyButtonBottom - 60 && ...
+const shouldFreeze = targetRect.top <= stickyButtonBottom - STICKY_BUTTON_OFFSET && ...
src/app/(mobile-ui)/withdraw/crypto/page.tsx (1)

259-276: Verify cross-chain detection logic

The cross-chain detection logic correctly identifies when source and destination chains differ. The console.log statements should be removed for production.

Remove debug console.log statements:

-        console.log('Cross-chain check:', {
-            fromChainId,
-            toChainId,
-            isPeanutWallet,
-            isCrossChain: fromChainId !== toChainId,
-        })
-
        return fromChainId !== toChainId
src/components/Global/PeanutActionDetailsCard/index.tsx (1)

180-190: Complex conditional logic for amount display

The nested conditionals for formatting the amount display are correct but could be simplified for better readability.

Consider extracting the display logic to a helper function:

const formatAmountDisplay = (amount: string, tokenSymbol: string, transactionType: string, currencySymbol?: string) => {
    if (transactionType === 'ADD_MONEY' && currencySymbol) {
        return `${currencySymbol} ${amount}`
    }
    
    if (tokenSymbol.toLowerCase() === PEANUT_WALLET_TOKEN_SYMBOL.toLowerCase()) {
        return `$ ${amount}`
    }
    
    const suffix = tokenSymbol.toLowerCase() === 'pnt' 
        ? (Number(amount) === 1 ? 'Beer' : 'Beers') 
        : tokenSymbol
    return `${amount} ${suffix}`
}
src/components/Request/link/views/Create.request.link.view.tsx (1)

304-310: Consider debouncing file updates

Immediate file updates on every change could lead to excessive API calls if users rapidly change files. Consider adding a small debounce.

// If file was added/changed and we have a request, update it immediately
if (requestId && options.rawFile !== lastSavedAttachmentRef.current.rawFile) {
-    updateRequestLink(options)
+    // Debounce file updates to avoid rapid API calls
+    setTimeout(() => {
+        if (requestId) {
+            updateRequestLink(options)
+        }
+    }, 500)
}
src/hooks/usePaymentInitiator.ts (1)

646-654: Extract complex condition for better readability.

The condition for determining when to show the confirm view is quite complex and could benefit from extraction into a descriptive variable.

Extract the condition into a named variable:

+               const shouldShowConfirmView = 
+                   chargeCreated &&
+                   (payload.isPintaReq ||
+                    payload.isAddMoneyFlow ||
+                    !isPeanutWallet ||
+                    (isPeanutWallet &&
+                     (!areEvmAddressesEqual(determinedChargeDetails.tokenAddress, PEANUT_WALLET_TOKEN) ||
+                      determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString())))
+
                // 2. handle charge state
-               if (
-                   chargeCreated &&
-                   (payload.isPintaReq ||
-                       payload.isAddMoneyFlow ||
-                       !isPeanutWallet ||
-                       (isPeanutWallet &&
-                           (!areEvmAddressesEqual(determinedChargeDetails.tokenAddress, PEANUT_WALLET_TOKEN) ||
-                               determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString())))
-               ) {
+               if (shouldShowConfirmView) {

Comment on lines +97 to +101
// Clean state
useEffect(() => {
setAmountToWithdraw('')
setWithdrawData(null)
}, [])
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify if cleanup effects are redundant.

The new cleanup effect resets specific withdrawal state, but there's already a resetWithdrawFlow() call in the earlier useEffect (lines 42-44). Consider whether this additional cleanup is necessary or if resetWithdrawFlow() should handle all state resetting to avoid redundancy.


🏁 Script executed:

#!/bin/bash
# Check if resetWithdrawFlow already handles the same cleanup
ast-grep --pattern 'resetWithdrawFlow = () => {
  $$$
}'

Length of output: 58


The initial ast-grep didn’t find the hook implementation. Let’s locate the useWithdrawFlow hook and the resetWithdrawFlow function definition and references across the codebase:


🏁 Script executed:

#!/bin/bash
# Find where the withdraw hook is defined or referenced
rg -n "useWithdrawFlow" .

# Find all occurrences of resetWithdrawFlow
rg -n "resetWithdrawFlow" .

Length of output: 2353


Let’s inspect the body of resetWithdrawFlow to see which state it resets:


🏁 Script executed:

#!/bin/bash
# Show resetWithdrawFlow implementation with surrounding context
rg -n -C10 "const resetWithdrawFlow" src/context/WithdrawFlowContext.tsx

Length of output: 946


We need more context from page.tsx to understand the two useEffect hooks and their placement. Let’s pull the relevant sections around both effects:


🏁 Script executed:

#!/bin/bash
# Display lines 1-120 to capture both useEffect hooks
sed -n '1,120p' 'src/app/(mobile-ui)/withdraw/page.tsx'

Length of output: 3995


Remove redundant mount cleanup – rely on resetWithdrawFlow

The resetWithdrawFlow() effect already resets both amountToWithdraw and withdrawData, so the later “Clean state” useEffect at lines 97–101 in src/app/(mobile-ui)/withdraw/page.tsx is duplicating that logic. Please remove the redundant hook to avoid unnecessary renders and keep all reset logic centralized.

  • File: src/app/(mobile-ui)/withdraw/page.tsx
  • Delete the block:
    // Clean state
    useEffect(() => {
        setAmountToWithdraw('')
        setWithdrawData(null)
    }, [])
    
🤖 Prompt for AI Agents
In src/app/(mobile-ui)/withdraw/page.tsx at lines 97 to 101, remove the entire
useEffect hook that resets amountToWithdraw and withdrawData on mount, as this
cleanup is already handled by the resetWithdrawFlow effect. Deleting this
redundant hook will centralize the reset logic and prevent unnecessary renders.

Comment on lines +28 to +46
const sendMoney = useCallback(
async (toAddress: Address, amountInUsd: string) => {
const amountToSend = parseUnits(amountInUsd, PEANUT_WALLET_TOKEN_DECIMALS)

const txData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [toAddress, amountToSend],
})

const transaction: peanutInterfaces.IPeanutUnsignedTransaction = {
to: PEANUT_WALLET_TOKEN,
data: txData,
}

return await sendTransactions([transaction], PEANUT_WALLET_CHAIN.id.toString())
},
[handleSendUserOpEncoded]
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the useCallback dependency array.

The function correctly implements ERC-20 transfer encoding and uses proper amount conversion. However, the dependency array is missing sendTransactions which is used inside the function.

Apply this fix to include the missing dependency:

-        [handleSendUserOpEncoded]
+        [sendTransactions]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sendMoney = useCallback(
async (toAddress: Address, amountInUsd: string) => {
const amountToSend = parseUnits(amountInUsd, PEANUT_WALLET_TOKEN_DECIMALS)
const txData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [toAddress, amountToSend],
})
const transaction: peanutInterfaces.IPeanutUnsignedTransaction = {
to: PEANUT_WALLET_TOKEN,
data: txData,
}
return await sendTransactions([transaction], PEANUT_WALLET_CHAIN.id.toString())
},
[handleSendUserOpEncoded]
)
const sendMoney = useCallback(
async (toAddress: Address, amountInUsd: string) => {
const amountToSend = parseUnits(amountInUsd, PEANUT_WALLET_TOKEN_DECIMALS)
const txData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [toAddress, amountToSend],
})
const transaction: peanutInterfaces.IPeanutUnsignedTransaction = {
to: PEANUT_WALLET_TOKEN,
data: txData,
}
return await sendTransactions([transaction], PEANUT_WALLET_CHAIN.id.toString())
},
[sendTransactions]
)
🤖 Prompt for AI Agents
In src/hooks/wallet/useWallet.ts around lines 28 to 46, the useCallback hook for
the sendMoney function is missing sendTransactions in its dependency array. To
fix this, add sendTransactions to the dependency array so that the callback
updates correctly when sendTransactions changes, ensuring proper memoization and
avoiding stale closures.

Comment on lines +30 to +34
const handleValueChange = (newValue: number[]) => {
if (isDragging) {
setSlidingValue(newValue)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential issue: handleValueChange only updates during dragging.

The handleValueChange function only updates slidingValue when isDragging is true, but the dragging state is set in the onPointerDown event of the thumb (line 73). This means programmatic value changes or keyboard interactions might not work properly.

Consider updating the logic to handle all value changes:

 const handleValueChange = (newValue: number[]) => {
-    if (isDragging) {
-        setSlidingValue(newValue)
-    }
+    setSlidingValue(newValue)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleValueChange = (newValue: number[]) => {
if (isDragging) {
setSlidingValue(newValue)
}
}
const handleValueChange = (newValue: number[]) => {
setSlidingValue(newValue)
}
🤖 Prompt for AI Agents
In src/components/Slider/index.tsx around lines 30 to 34, the handleValueChange
function currently updates slidingValue only when isDragging is true, which
prevents updates from programmatic changes or keyboard interactions. Modify
handleValueChange to update slidingValue on all value changes regardless of the
isDragging state to ensure consistent behavior across all input methods.

Comment on lines +38 to +105
// Button helper functions adapted from hero.tsx
const getInitialAnimation = () => ({
opacity: 0,
translateY: 4,
translateX: 0,
rotate: 0.75,
})

const getAnimateAnimation = (buttonVisible: boolean, buttonScale: number = 1) => ({
opacity: buttonVisible ? 1 : 0,
translateY: buttonVisible ? 0 : 20,
translateX: buttonVisible ? 0 : 20,
rotate: buttonVisible ? 0 : 1,
scale: buttonScale,
pointerEvents: buttonVisible ? ('auto' as const) : ('none' as const),
})

const getHoverAnimation = () => ({
translateY: 6,
translateX: 0,
rotate: 0.75,
})

const transitionConfig = { type: 'spring', damping: 15 } as const

const getButtonClasses = () =>
`btn bg-white fill-n-1 text-n-1 hover:bg-white/90 px-9 md:px-11 py-4 md:py-10 text-lg md:text-2xl btn-shadow-primary-4`

const renderSparkle = () => (
<img
src={Sparkle.src}
className="absolute -right-4 -top-4 h-auto w-5 sm:-right-5 sm:-top-5 sm:w-6"
alt="Sparkle"
/>
)

const renderArrows = () => (
<>
<Image
src="/arrows/small-arrow.svg"
alt="Arrow pointing to button"
width={48}
height={24}
className="absolute -left-13 -top-7 block -translate-y-1/2 transform md:hidden"
/>
<Image
src="/arrows/small-arrow.svg"
alt="Arrow pointing to button"
width={48}
height={24}
className="absolute -right-13 -top-7 block -translate-y-1/2 scale-x-[-1] transform md:hidden"
/>
<Image
src="/arrows/small-arrow.svg"
alt="Arrow pointing to button"
width={64}
height={32}
className="absolute -left-18 -top-7 hidden -translate-y-1/2 transform md:block"
/>
<Image
src="/arrows/small-arrow.svg"
alt="Arrow pointing to button"
width={64}
height={32}
className="absolute -right-18 -top-7 hidden -translate-y-1/2 scale-x-[-1] transform md:block"
/>
</>
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extract shared animation helpers to avoid duplication

The comment indicates these helper functions are adapted from hero.tsx. To maintain DRY principles and ensure consistency, consider extracting these shared functions to a common utility file.

Create a shared utility file for landing page animations:

// src/components/LandingPage/utils/animations.ts
export const getInitialAnimation = () => ({
    opacity: 0,
    translateY: 4,
    translateX: 0,
    rotate: 0.75,
})

export const getAnimateAnimation = (buttonVisible: boolean, buttonScale: number = 1) => ({
    opacity: buttonVisible ? 1 : 0,
    translateY: buttonVisible ? 0 : 20,
    translateX: buttonVisible ? 0 : 20,
    rotate: buttonVisible ? 0 : 1,
    scale: buttonScale,
    pointerEvents: buttonVisible ? ('auto' as const) : ('none' as const),
})

export const getHoverAnimation = () => ({
    translateY: 6,
    translateX: 0,
    rotate: 0.75,
})

export const transitionConfig = { type: 'spring', damping: 15 } as const

// ... other shared functions

Then import and use these functions in both hero.tsx and sendInSeconds.tsx.

🤖 Prompt for AI Agents
In src/components/LandingPage/sendInSeconds.tsx lines 38 to 105, the animation
helper functions are duplicated from hero.tsx. To fix this, extract these shared
animation helpers into a new utility file, for example
src/components/LandingPage/utils/animations.ts, and export them from there. Then
update sendInSeconds.tsx and hero.tsx to import and use these shared functions
instead of defining them locally, ensuring DRY code and consistency.

Comment on lines +11 to 20
export const getPublicClient = async (chainId: ChainId): Promise<PublicClient> => {
let client: PublicClient | undefined = PUBLIC_CLIENTS_BY_CHAIN[chainId]?.client
if (client) return client
const chain: Chain = extractChain({ chains: allChains, id: chainId })
if (!chain) throw new Error(`No chain found for chainId ${chainId}`)
return createPublicClient({
transport: http(rpcUrls[chainId][0]),
chain,
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for RPC URL configuration access.

The function accesses rpcUrls[chainId][0] without verifying that the chainId exists in the rpcUrls object or that the array has at least one element. This could cause a runtime error.

Consider adding validation:

 export const getPublicClient = async (chainId: ChainId): Promise<PublicClient> => {
     let client: PublicClient | undefined = PUBLIC_CLIENTS_BY_CHAIN[chainId]?.client
     if (client) return client
     const chain: Chain = extractChain({ chains: allChains, id: chainId })
     if (!chain) throw new Error(`No chain found for chainId ${chainId}`)
+    const chainRpcUrls = rpcUrls[chainId]
+    if (!chainRpcUrls || chainRpcUrls.length === 0) {
+        throw new Error(`No RPC URLs configured for chainId ${chainId}`)
+    }
     return createPublicClient({
-        transport: http(rpcUrls[chainId][0]),
+        transport: http(chainRpcUrls[0]),
         chain,
     })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getPublicClient = async (chainId: ChainId): Promise<PublicClient> => {
let client: PublicClient | undefined = PUBLIC_CLIENTS_BY_CHAIN[chainId]?.client
if (client) return client
const chain: Chain = extractChain({ chains: allChains, id: chainId })
if (!chain) throw new Error(`No chain found for chainId ${chainId}`)
return createPublicClient({
transport: http(rpcUrls[chainId][0]),
chain,
})
}
export const getPublicClient = async (chainId: ChainId): Promise<PublicClient> => {
let client: PublicClient | undefined = PUBLIC_CLIENTS_BY_CHAIN[chainId]?.client
if (client) return client
const chain: Chain = extractChain({ chains: allChains, id: chainId })
if (!chain) throw new Error(`No chain found for chainId ${chainId}`)
const chainRpcUrls = rpcUrls[chainId]
if (!chainRpcUrls || chainRpcUrls.length === 0) {
throw new Error(`No RPC URLs configured for chainId ${chainId}`)
}
return createPublicClient({
transport: http(chainRpcUrls[0]),
chain,
})
}
🤖 Prompt for AI Agents
In src/app/actions/clients.ts around lines 11 to 20, the code accesses
rpcUrls[chainId][0] without checking if rpcUrls contains the chainId key or if
the array at that key has at least one element. To fix this, add validation to
confirm rpcUrls[chainId] exists and is a non-empty array before accessing the
first element. If the validation fails, throw an appropriate error to prevent
runtime exceptions.

Comment on lines +244 to +259
const response = await fetchWithSentry(`${SQUID_API_URL}/v2/route`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-integrator-id': process.env.SQUID_INTEGRATOR_ID!,
},
body: JSON.stringify(params),
})

if (!response.ok) {
throw new Error(`Failed to get route: ${response.status}`)
}

const data = await response.json()
return data as SquidRouteResponse
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add timeout handling for API requests

The fetch request to Squid API should include a timeout to prevent hanging requests.

Add timeout support:

async function getSquidRouteRaw(params: SquidGetRouteParams): Promise<SquidRouteResponse> {
+    const controller = new AbortController()
+    const timeoutId = setTimeout(() => controller.abort(), 30000) // 30 second timeout
+    
+    try {
        const response = await fetchWithSentry(`${SQUID_API_URL}/v2/route`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'x-integrator-id': process.env.SQUID_INTEGRATOR_ID!,
            },
            body: JSON.stringify(params),
+            signal: controller.signal,
        })

        if (!response.ok) {
            throw new Error(`Failed to get route: ${response.status}`)
        }

        const data = await response.json()
        return data as SquidRouteResponse
+    } finally {
+        clearTimeout(timeoutId)
+    }
}
🤖 Prompt for AI Agents
In src/services/swap.ts around lines 244 to 259, the fetch request to the Squid
API lacks timeout handling, which can cause the request to hang indefinitely.
Modify the fetchWithSentry call to include a timeout mechanism, such as using an
AbortController to abort the request if it exceeds a specified duration.
Implement the timeout logic before the fetch call and ensure the fetch is
aborted and handled properly if the timeout is reached.

Comment on lines +63 to +68
const minReceived = useMemo<string | null>(() => {
if (!xChainRoute || !resolvedTokenSymbol) return null
const amount = formatUnits(BigInt(xChainRoute.rawResponse.route.estimate.toAmountMin), token.decimals)
return isStableCoin(resolvedTokenSymbol) ? `$ ${amount}` : `${amount} ${resolvedTokenSymbol}`
}, [xChainRoute, resolvedTokenSymbol])

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider null safety for token decimals

The minReceived calculation assumes token.decimals is always defined. Add a safety check.

const minReceived = useMemo<string | null>(() => {
    if (!xChainRoute || !resolvedTokenSymbol) return null
+    if (token.decimals === undefined) {
+        console.error('Token decimals not defined')
+        return null
+    }
    const amount = formatUnits(BigInt(xChainRoute.rawResponse.route.estimate.toAmountMin), token.decimals)
    return isStableCoin(resolvedTokenSymbol) ? `$ ${amount}` : `${amount} ${resolvedTokenSymbol}`
}, [xChainRoute, resolvedTokenSymbol, token.decimals])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const minReceived = useMemo<string | null>(() => {
if (!xChainRoute || !resolvedTokenSymbol) return null
const amount = formatUnits(BigInt(xChainRoute.rawResponse.route.estimate.toAmountMin), token.decimals)
return isStableCoin(resolvedTokenSymbol) ? `$ ${amount}` : `${amount} ${resolvedTokenSymbol}`
}, [xChainRoute, resolvedTokenSymbol])
const minReceived = useMemo<string | null>(() => {
if (!xChainRoute || !resolvedTokenSymbol) return null
if (token.decimals === undefined) {
console.error('Token decimals not defined')
return null
}
const amount = formatUnits(
BigInt(xChainRoute.rawResponse.route.estimate.toAmountMin),
token.decimals
)
return isStableCoin(resolvedTokenSymbol)
? `$ ${amount}`
: `${amount} ${resolvedTokenSymbol}`
}, [xChainRoute, resolvedTokenSymbol, token.decimals])
🤖 Prompt for AI Agents
In src/components/Withdraw/views/Confirm.withdraw.view.tsx around lines 63 to
68, the code uses token.decimals without checking if it is defined, which can
cause runtime errors. Add a null or undefined check for token.decimals before
using it in formatUnits. If token.decimals is not defined, return null or handle
the case appropriately to ensure safe execution.

Comment on lines +346 to 352
useMemo(() => {
if (isConnected && address) {
setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
}
}, [
debouncedAttachmentOptions,
debouncedTokenValue,
isValidRecipient,
isCreatingLink,
generatedLink,
_tokenValue,
recipientAddress,
])

// check for token value debouncing
const isDebouncing =
(hasAttachment &&
attachmentOptions?.message &&
(!debouncedAttachmentOptions?.message ||
debouncedAttachmentOptions.message !== attachmentOptions.message)) ||
(hasAttachment && _tokenValue !== debouncedTokenValue)
}, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use useEffect instead of useMemo for side effects

Setting state in useMemo is an anti-pattern. Use useEffect for this side effect.

-    // Set wallet defaults when connected
-    useMemo(() => {
-        if (isConnected && address) {
-            setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
-            setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
-        }
-    }, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
+    // Set wallet defaults when connected
+    useEffect(() => {
+        if (isConnected && address) {
+            setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
+            setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
+        }
+    }, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useMemo(() => {
if (isConnected && address) {
setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
}
}, [
debouncedAttachmentOptions,
debouncedTokenValue,
isValidRecipient,
isCreatingLink,
generatedLink,
_tokenValue,
recipientAddress,
])
// check for token value debouncing
const isDebouncing =
(hasAttachment &&
attachmentOptions?.message &&
(!debouncedAttachmentOptions?.message ||
debouncedAttachmentOptions.message !== attachmentOptions.message)) ||
(hasAttachment && _tokenValue !== debouncedTokenValue)
}, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
// Set wallet defaults when connected
useEffect(() => {
if (isConnected && address) {
setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
}
}, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
🤖 Prompt for AI Agents
In src/components/Request/link/views/Create.request.link.view.tsx around lines
346 to 352, the code uses useMemo to set state, which is an anti-pattern since
useMemo is for memoizing values, not side effects. Replace useMemo with
useEffect to perform the state updates when isConnected or address change,
ensuring the side effect is handled correctly.

if (decimals === undefined) {
decimals = json.data.contracts.find((contract) => contract.blockchainId === chainId)!.decimals
}
const decimals = json.data.contracts.find((contract) => contract.blockchainId === chainId)!.decimals
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for contract lookup to prevent runtime errors.

The non-null assertion on the contract lookup could throw if the Mobula API response doesn't contain a contract for the requested chain ID. This would cause the entire function to fail instead of returning undefined.

Replace the non-null assertion with proper null checking:

-                const decimals = json.data.contracts.find((contract) => contract.blockchainId === chainId)!.decimals
+                const contract = json.data.contracts.find((contract) => contract.blockchainId === chainId)
+                if (!contract) {
+                    console.log('No contract found for chainId:', chainId)
+                    return undefined
+                }
+                const decimals = contract.decimals
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const decimals = json.data.contracts.find((contract) => contract.blockchainId === chainId)!.decimals
const contract = json.data.contracts.find((contract) => contract.blockchainId === chainId)
if (!contract) {
console.log('No contract found for chainId:', chainId)
return undefined
}
const decimals = contract.decimals
🤖 Prompt for AI Agents
In src/app/actions/tokens.ts at line 123, the code uses a non-null assertion on
the result of finding a contract by chainId, which can cause runtime errors if
no matching contract is found. Modify the code to first check if the contract
exists before accessing its decimals property. If the contract is not found,
return undefined or handle the case gracefully to prevent the function from
throwing an error.

Comment on lines +476 to +487
{isAddMoneyFlow && (
<PaymentInfoRow
label="From"
value={
<AddressLink
isLink={false}
address={wagmiAddress!}
className="text-black no-underline"
/>
}
/>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for wagmiAddress to prevent runtime errors.

The non-null assertion on wagmiAddress could cause runtime errors if the wallet is disconnected or not yet connected when this component renders.

Add a conditional render or default value:

                    {isAddMoneyFlow && (
+                       wagmiAddress && (
                        <PaymentInfoRow
                            label="From"
                            value={
                                <AddressLink
                                    isLink={false}
-                                   address={wagmiAddress!}
+                                   address={wagmiAddress}
                                    className="text-black no-underline"
                                />
                            }
                        />
+                       )
                    )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{isAddMoneyFlow && (
<PaymentInfoRow
label="From"
value={
<AddressLink
isLink={false}
address={wagmiAddress!}
className="text-black no-underline"
/>
}
/>
)}
{isAddMoneyFlow && (
wagmiAddress && (
<PaymentInfoRow
label="From"
value={
<AddressLink
isLink={false}
address={wagmiAddress}
className="text-black no-underline"
/>
}
/>
)
)}
🤖 Prompt for AI Agents
In src/components/Payment/Views/Confirm.payment.view.tsx around lines 476 to
487, the code uses a non-null assertion on wagmiAddress which can cause runtime
errors if wagmiAddress is null or undefined. To fix this, add a null check
before rendering the AddressLink component or provide a default value for the
address prop. This ensures the component only renders when wagmiAddress is
valid, preventing potential crashes.

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.

2 participants