Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TASK-6955] feat: populate token selector with squid supported token #539

Merged
merged 23 commits into from
Nov 22, 2024

Conversation

jjramirezn
Copy link
Contributor

@jjramirezn jjramirezn commented Nov 20, 2024

Main focus

Instead of relaying only on our sdk list of supported chain and tokens, the token selector is now populated mainly from the supported tokens from squid, in the flows of link and request creation we also use the sdk list because we want users to be able to create those even if those wont be able to be fulfilled crosschain

Other changes

  • Cache the list of squid supported tokens for 12 hours, we don't expect this to change much
  • On request pay dont show the token selector if the token doesnt support xchain
  • Lazy load the images on the token selector (the lists are bigger now with all the squid tokens)

Should also fix

  • [TASK-6241]
  • [TASK-6859]
  • [TASK-6581]
  • [TASK-6580]
  • [TASK-6568]

Copy link

vercel bot commented Nov 20, 2024

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

Name Status Preview Comments Updated (UTC)
peanut-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 21, 2024 5:25pm

Copy link
Contributor

coderabbitai bot commented Nov 20, 2024

📝 Walkthrough

Walkthrough

The changes in this pull request introduce a new file for fetching and caching squid chains and tokens, utilizing the @squirrel-labs/peanut-sdk. The Claim component and related files have been modified to remove cross-chain details, streamlining the handling of tokens and chains. New context properties and state variables have been added to support cross-chain functionality, and several components have been updated to reflect these changes. The overall structure is enhanced for better performance and clarity in token selection and management.

Changes

File Path Change Summary
src/app/actions/squid.ts Introduced functions for fetching and caching squid chains and tokens using @squirrel-labs/peanut-sdk. Defined caching functions with a 12-hour revalidation period. Exported getSquidChainsAndTokens.
src/components/Claim/Claim.consts.ts Removed crossChainDetails property from IClaimScreenProps interface, affecting type safety and functionality.
src/components/Claim/Claim.tsx Removed crossChainDetails state and associated logic. Eliminated getCrossChainDetails function and its invocation in checkLink. Updated FlowManager props accordingly.
src/components/Claim/Link/Initial.view.tsx Added tokenSupportsXChain using useMemo. Removed crossChainDetails prop. Updated error handling logic and rendering based on supportedSquidChainsAndTokens.
src/components/Claim/Link/Onchain/Confirm.view.tsx Expanded destructuring to include supportedSquidChainsAndTokens. Removed mappedData variable and updated rendering logic to use the new context data.
src/components/Global/TokenSelector/TokenSelector.consts.ts Added showOnlySquidSupported optional property to TokenSelectorProps interface.
src/components/Global/TokenSelector/TokenSelector.tsx Introduced supportedSquidChainsAndTokens context property. Updated rendering logic for token selection with virtual scrolling. Modified function signature to include showOnlySquidSupported.
src/components/Request/Pay/Views/Initial.view.tsx Added supportedSquidChainsAndTokens to context. Introduced tokenSupportsXChain for conditional rendering of the TokenSelector. Updated rendering logic for payment request messages based on token support.
src/context/tokenSelector.context.tsx Added supportedSquidChainsAndTokens to context state. Imported and utilized getSquidChainsAndTokens in a useEffect hook to fetch supported chains and tokens on mount.

Possibly related PRs

Suggested reviewers

  • Hugo0

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

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

🧹 Outside diff range and nitpick comments (16)
src/components/Global/TokenSelector/TokenSelector.consts.ts (1)

10-10: Add JSDoc documentation for the new property.

The new property is well-placed and aligns with the PR objectives. However, it would benefit from documentation to explain its purpose and behavior.

Consider adding JSDoc documentation:

 export interface TokenSelectorProps {
     classNameButton?: string
     shouldBeConnected?: boolean
+    /** 
+     * When true, displays only tokens that are supported by Squid for cross-chain operations.
+     * @since 1.0.0
+     */
     showOnlySquidSupported?: boolean
     onReset?: () => void
 }
src/app/actions/squid.ts (1)

48-71: Remove commented out code.

The commented-out version of the function is no longer needed and should be removed to maintain code cleanliness.

-/*
-export async function getSquidChainsAndTokens(): Promise<Record<string, interfaces.ISquidChain & {tokens: interfaces.ISquidToken[]}>> {
-  console.log('======= Fetching squid chains and tokens =======')
-  const chains = await getSquidChainsCache()
-  const tokens = await getSquidTokensCache()
-
-  const chainsById = chains.filter(chain => 'evm' === chain.chainType)
-    .reduce((acc, chain) => {
-      acc[chain.chainId] = chain
-      return acc
-    }, {} as Record<string, any>)
-
-  tokens.forEach(token => {
-    if (token.chainId in chainsById) {
-      if (!chainsById[token.chainId].tokens) {
-        chainsById[token.chainId].tokens = []
-      }
-      chainsById[token.chainId].tokens.push(token)
-    }
-  })
-
-  return chainsById
-}
-*/
src/context/tokenSelector.context.tsx (1)

58-60: Consider adding loading state management.

While the state initialization is correct, consider adding a loading state to handle the initial data fetch of Squid chains and tokens. This would help prevent potential UI flickers and provide better user feedback.

+    const [isLoadingSquidData, setIsLoadingSquidData] = useState(true)

     const [supportedSquidChainsAndTokens, setSupportedSquidChainsAndTokens] = useState<
         Record<string, interfaces.ISquidChain & { tokens: interfaces.ISquidToken[] }>
     >({})
src/components/Claim/Claim.tsx (5)

Line range hint 47-50: Consider memoizing context values

To optimize performance, consider memoizing the context values using useMemo if they're being passed down to multiple child components.

-const { setSelectedChainID, setSelectedTokenAddress } = useContext(context.tokenSelectorContext)
+const memoizedContextValue = useMemo(() => {
+  const { setSelectedChainID, setSelectedTokenAddress } = useContext(context.tokenSelectorContext)
+  return { setSelectedChainID, setSelectedTokenAddress }
+}, [])

Line range hint 52-73: Add error boundaries for screen navigation

The screen navigation handlers are well-implemented, but consider adding error boundaries to gracefully handle any potential state corruption during navigation.

 const handleOnCustom = (screen: _consts.ClaimScreens) => {
+    try {
       setStep(() => ({
           screen: screen,
           idx: _consts.CLAIM_SCREEN_FLOW.indexOf(screen),
       }))
+    } catch (error) {
+        console.error('Navigation failed:', error)
+        // Reset to initial state or show error UI
+    }
 }

Line range hint 74-116: Refactor checkLink for better separation of concerns

The checkLink function handles multiple responsibilities. Consider breaking it down into smaller, focused functions for better maintainability and testing.

+const setupTokenPrice = async (tokenAddress: string, chainId: number) => {
+    const tokenPrice = await utils.fetchTokenPrice(tokenAddress.toLowerCase(), chainId)
+    return tokenPrice?.price || 0
+}

+const setupEstimatedPoints = async (params: {
+    address: string
+    chainId: number
+    amountUSD: number
+}) => {
+    return estimatePoints({
+        address: params.address,
+        chainId: params.chainId,
+        amountUSD: params.amountUSD,
+        actionType: ActionType.CLAIM,
+    })
+}

 const checkLink = async (link: string) => {
     try {
         const linkDetails: interfaces.ILinkDetails = await peanut.getLinkDetails({
             link,
         })
-        const attachmentInfo = await getAttachmentInfo(linkDetails.link)
-        setAttachment({
-            message: attachmentInfo?.message,
-            attachmentUrl: attachmentInfo?.fileUrl,
-        })
+        await setupAttachment(linkDetails.link)
         
         setClaimLinkData(linkDetails)
         setSelectedChainID(linkDetails.chainId)
         setSelectedTokenAddress(linkDetails.tokenAddress)

         if (linkDetails.claimed) {
             setLinkState(_consts.claimLinkStateType.ALREADY_CLAIMED)
             return
         }

-        const tokenPrice = await utils.fetchTokenPrice(linkDetails.tokenAddress.toLowerCase(), linkDetails.chainId)
-        tokenPrice && setTokenPrice(tokenPrice?.price)
+        const price = await setupTokenPrice(linkDetails.tokenAddress, linkDetails.chainId)
+        setTokenPrice(price)

         if (address) {
             setRecipient({ name: '', address })

-            const estimatedPoints = await estimatePoints({
-                address: address ?? '',
-                chainId: linkDetails.chainId,
-                amountUSD: Number(linkDetails.tokenAmount) * (tokenPrice?.price ?? 0),
-                actionType: ActionType.CLAIM,
-            })
+            const estimatedPoints = await setupEstimatedPoints({
+                address,
+                chainId: linkDetails.chainId,
+                amountUSD: Number(linkDetails.tokenAmount) * price,
+            })
             setEstimatedPoints(estimatedPoints)
         }

Line range hint 118-123: Improve useEffect implementation

The useEffect hook should include proper cleanup and dependencies.

-useEffect(() => {
+useEffect(() => {
+    let mounted = true
     const pageUrl = typeof window !== 'undefined' ? window.location.href : ''
     if (pageUrl) {
-        checkLink(pageUrl)
+        checkLink(pageUrl).catch(error => {
+            if (mounted) {
+                console.error('Failed to check link:', error)
+                setLinkState(_consts.claimLinkStateType.NOT_FOUND)
+            }
+        })
     }
-}, [])
+    return () => {
+        mounted = false
+    }
+}, [checkLink])

Line range hint 125-199: Extract loading state into separate component

Consider extracting the loading state into a separate component for better reusability and maintainability.

+const LoadingSpinner = () => (
+    <div className="relative flex w-full items-center justify-center">
+        <div className="animate-spin">
+            <img src={assets.PEANUTMAN_LOGO.src} alt="logo" className="h-6 sm:h-10" />
+            <span className="sr-only">Loading...</span>
+        </div>
+    </div>
+)

 return (
     <div className="card">
-        {linkState === _consts.claimLinkStateType.LOADING && (
-            <div className="relative flex w-full items-center justify-center">
-                <div className="animate-spin">
-                    <img src={assets.PEANUTMAN_LOGO.src} alt="logo" className="h-6 sm:h-10" />
-                    <span className="sr-only">Loading...</span>
-                </div>
-            </div>
-        )}
+        {linkState === _consts.claimLinkStateType.LOADING && <LoadingSpinner />}
src/components/Claim/Link/Onchain/Confirm.view.tsx (1)

Line range hint 89-108: Enhance error handling for cross-chain scenarios

The current error handling uses a generic error handler. Consider adding specific error messages for cross-chain scenarios to improve the user experience.

Example enhancement:

 } catch (error) {
+    let errorString
+    if (selectedRoute) {
+        errorString = error instanceof Error && error.message.includes('bridge')
+            ? 'Cross-chain transfer failed. Please try again.'
+            : utils.ErrorHandler(error)
+    } else {
+        errorString = utils.ErrorHandler(error)
+    }
     setErrorState({
         showError: true,
-        errorMessage: errorString,
+        errorMessage: errorString
     })
 }
src/components/Request/Pay/Views/Initial.view.tsx (2)

129-135: Consider adding type safety improvements.

The tokenSupportsXChain implementation is logically correct, but could benefit from explicit typing:

    const tokenSupportsXChain = useMemo(() => {
+       type SquidToken = { address: string }
        return (
-           supportedSquidChainsAndTokens[requestLinkData.chainId]?.tokens.some((token) =>
+           supportedSquidChainsAndTokens[requestLinkData.chainId]?.tokens.some((token: SquidToken) =>
                areTokenAddressesEqual(token.address, requestLinkData.tokenAddress)
            ) ?? false
        )
    }, [requestLinkData.tokenAddress, requestLinkData.chainId, supportedSquidChainsAndTokens])

377-389: Document the new TokenSelector prop.

The conditional rendering and messaging look good. However, the new prop showOnlySquidSupported on TokenSelector should be documented to explain its purpose and impact.

Consider adding a comment above the TokenSelector to explain the prop:

+    {/* Only show tokens that are supported by Squid for cross-chain transfers */}
    {tokenSupportsXChain && <TokenSelector onReset={resetTokenAndChain} showOnlySquidSupported />}
src/components/Claim/Link/Initial.view.tsx (2)

144-150: Consider caching optimization for token support check

The tokenSupportsXChain memo's dependency array includes the entire supportedSquidChainsAndTokens object, which could trigger unnecessary recalculations.

Consider extracting only the required chain's tokens:

const tokenSupportsXChain = useMemo(() => {
+   const chainTokens = supportedSquidChainsAndTokens[claimLinkData.chainId]?.tokens
    return (
-     supportedSquidChainsAndTokens[claimLinkData.chainId]?.tokens.some((token) =>
+     chainTokens?.some((token) =>
        areTokenAddressesEqual(token.address, claimLinkData.tokenAddress)
      ) ?? false
    )
-  }, [claimLinkData.tokenAddress, claimLinkData.chainId, supportedSquidChainsAndTokens])
+  }, [claimLinkData.tokenAddress, claimLinkData.chainId, supportedSquidChainsAndTokens[claimLinkData.chainId]?.tokens])

Line range hint 179-196: Improve error handling for unsupported tokens

The error handling for unsupported tokens could be more informative to users.

Consider providing more specific error messages:

if (!tokenSupportsXChain) {
    setErrorState({
        showError: true,
-       errorMessage: 'offramp unavailable',
+       errorMessage: `This token (${claimLinkData.tokenSymbol}) on ${chainName || 'this chain'} does not support cross-chain transfers.`,
    })
    return
}
src/components/Global/TokenSelector/TokenSelector.tsx (3)

115-125: Use Next.js Image component for consistent image handling

At line 115~, you're using a standard <img> tag to display the chain icon. To maintain consistency and leverage Next.js image optimization features, consider replacing it with the Image component from next/image.

Apply this refactor to use the Image component:

- <img
-     src={supportedSquidChainsAndTokens[balance.chainId]?.chainIconURI}
-     className="absolute -top-1 left-3 h-4 w-4 rounded-full"
-     alt="logo"
-     onError={(_e) => {
-         setChainPlaceholders((prev) => ({
-             ...prev,
-             [`${balance.address}_${balance.chainId}`]: true,
-         }))
-     }}
- />
+ <Image
+     src={supportedSquidChainsAndTokens[balance.chainId]?.chainIconURI || ''}
+     className="absolute -top-1 left-3 h-4 w-4 rounded-full"
+     alt="Chain logo"
+     width={16}
+     height={16}
+     unoptimized
+     onError={() => {
+         setChainPlaceholders((prev) => ({
+             ...prev,
+             [`${balance.address}_${balance.chainId}`]: true,
+         }))
+     }}
+ />

This ensures consistent image handling across your application and benefits from Next.js optimizations.


Line range hint 238-246: Memoize the filter function to prevent unnecessary re-renders

While the calculation in byChainAndText is simple, memoizing the function with useCallback can optimize performance by preventing unnecessary re-renders of the filtered list.

Apply this refactor to memoize the byChainAndText function:

- const byChainAndText = ({ name, symbol, chainId }: IUserBalance): boolean =>
+ const byChainAndText = useCallback(({ name, symbol, chainId }: IUserBalance): boolean => {
    (
        (!hasUserChangedChain || selectedChainID === chainId) &&
        (filterValue.length === 0 ||
            name.toLowerCase().includes(filterValue.toLowerCase()) ||
            symbol.toLowerCase().includes(filterValue.toLowerCase()))
    )
+ }, [hasUserChangedChain, selectedChainID, filterValue])

This can improve performance, especially with larger datasets.


30-59: Consider using a virtualization library for lists

The custom virtual scrolling implementation may not handle all edge cases efficiently. Using a proven library like react-window can improve performance and reliability when displaying large lists.

Install react-window:

npm install react-window

Refactor TokenList to use FixedSizeList from react-window:

import { FixedSizeList as List } from 'react-window';

const TokenList = ({ balances, setToken }) => {
    // ...

    return (
        <List
            height={400}
            itemCount={balances.length}
            itemSize={56}
            width="100%"
        >
            {({ index, style }) => {
                const balance = balances[index];
                return (
                    <div
                        style={style}
                        onClick={() => setToken(balance)}
                        // ...
                    >
                        {/* Render token item */}
                    </div>
                );
            }}
        </List>
    );
};

This enhances performance and user experience with minimal effort.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 96e291d and bc821fb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • src/app/actions/squid.ts (1 hunks)
  • src/components/Claim/Claim.consts.ts (0 hunks)
  • src/components/Claim/Claim.tsx (1 hunks)
  • src/components/Claim/Link/Initial.view.tsx (7 hunks)
  • src/components/Claim/Link/Onchain/Confirm.view.tsx (3 hunks)
  • src/components/Global/TokenSelector/TokenSelector.consts.ts (1 hunks)
  • src/components/Global/TokenSelector/TokenSelector.tsx (6 hunks)
  • src/components/Request/Pay/Views/Initial.view.tsx (3 hunks)
  • src/context/tokenSelector.context.tsx (5 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Claim/Claim.consts.ts
🧰 Additional context used
📓 Learnings (2)
src/components/Global/TokenSelector/TokenSelector.tsx (2)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-11-12T09:39:20.720Z
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.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#424
File: src/components/Global/TokenSelector/TokenSelector.tsx:197-211
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Global/TokenSelector/TokenSelector.tsx`, when the calculation within functions like `byChainAndText` is not computationally expensive, it's acceptable to avoid using `useCallback` for memoization.
src/context/tokenSelector.context.tsx (1)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-11-12T09:39:20.720Z
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.
🔇 Additional comments (14)
src/app/actions/squid.ts (2)

1-7: LGTM! Imports and server directive are properly configured.

The server-side directive and imports are correctly set up for the intended functionality.


8-13: Consider the implications of using unstable_cache API.

While the caching implementation is good, there are two points to consider:

  1. The unstable_cache API from Next.js is experimental and might change in future releases.
  2. The cache keys ('getSquidChains', 'getSquidTokens') could be more specific to prevent potential collisions.

Consider making these changes:

-const getSquidChainsCache = unstable_cache(async () => await getSquidChains({ isTestnet: false }), ['getSquidChains'], {
+const getSquidChainsCache = unstable_cache(async () => await getSquidChains({ isTestnet: false }), ['squid:chains:v1'], {
     revalidate: 3600 * 12,
 })
-const getSquidTokensCache = unstable_cache(async () => await getSquidTokens({ isTestnet: false }), ['getSquidTokens'], {
+const getSquidTokensCache = unstable_cache(async () => await getSquidTokens({ isTestnet: false }), ['squid:tokens:v1'], {
     revalidate: 3600 * 12,
 })
✅ Verification successful

Cache key naming is safe, but consider documenting unstable API usage

The cache keys are actually well-isolated and used consistently:

  • getSquidChains and getSquidTokens are only used as cache keys in squid.ts
  • A separate cache key getSquidChainsAndTokens is used for the combined data
  • The caching implementation forms a proper hierarchy where base cached data is used by the combined cache

While the current cache key naming is safe from collisions, the use of unstable_cache should be documented as it's an experimental Next.js API.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are other uses of these cache keys
rg -l "getSquidChains|getSquidTokens" --type ts

Length of output: 112


Script:

#!/bin/bash
# Check the usage context of these functions in both files
rg "getSquidChains|getSquidTokens" -C 3 --type ts

Length of output: 3249

src/context/tokenSelector.context.tsx (4)

7-8: LGTM! Imports are correctly structured.

The new imports for Squid interfaces and actions align with the PR objectives to enhance token selector functionality.


30-30: LGTM! Context interface is properly extended.

The new supportedSquidChainsAndTokens property is well-typed and initialized appropriately.


158-158: LGTM! Context value is properly updated.

The new supportedSquidChainsAndTokens value is correctly added to the provider.


8-8: Verify caching implementation in getSquidChainsAndTokens.

The PR objectives mention caching Squid tokens for 12 hours. Let's verify the implementation:

✅ Verification successful

Caching is correctly implemented with 12-hour revalidation

The verification confirms that getSquidChainsAndTokens is wrapped with unstable_cache and configured with a 12-hour revalidation period (revalidate: 3600 * 12), which aligns with the PR objectives for caching Squid tokens.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if getSquidChainsAndTokens implements 12-hour caching

# Search for caching implementation
rg -A 10 "getSquidChainsAndTokens" src/app/actions/squid.ts

# Look for cache-related code
rg -l "cache|persist|store" src/app/actions/

Length of output: 1201

src/components/Claim/Claim.tsx (1)

Line range hint 17-45: Well-structured state management

The state declarations are well-organized and the removal of crossChainDetails aligns with the PR's objective of simplifying token selection and management.

src/components/Claim/Link/Onchain/Confirm.view.tsx (2)

32-34: LGTM: Context usage is well-structured

The addition of supportedSquidChainsAndTokens from the token selector context aligns with the PR objectives and follows React best practices.


Line range hint 109-124: Review commented file type detection logic

There's commented-out code for file type detection that might affect attachment display functionality. Please verify if this code should be removed or implemented.

src/components/Request/Pay/Views/Initial.view.tsx (1)

Line range hint 141-171: Verify error handling for Squid-specific scenarios.

The error handling in estimateTxFee looks good for general cross-chain cases, but let's verify it handles Squid-specific edge cases.

✅ Verification successful

Let me analyze the error handling patterns further by checking for specific error constants and error handling patterns in the codebase.


Based on the analysis of the error handling patterns in the codebase, I can now generate the final response.

Error handling for Squid integration is robust and properly implemented.

The error handling in the code follows consistent patterns and covers Squid-specific scenarios:

  • Proper error handling for route finding with ERR_NO_ROUTE constant
  • Comprehensive error state management with setErrorState
  • Appropriate cleanup of state variables on error (slippage, unsigned transactions, fees)
  • Graceful fallback behavior with user-friendly error messages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for Squid-specific error handling patterns in the codebase

# Look for Squid-related error handling patterns
rg -A 3 "squid|Squid" --type ts

Length of output: 22470


Script:

#!/bin/bash
# Look for error constants and error handling patterns in the codebase
rg -A 3 "ERR_NO_ROUTE|setErrorState|catch.*error" --type ts

Length of output: 77556

src/components/Claim/Link/Initial.view.tsx (2)

81-81: LGTM: Context usage aligns with PR objectives

The addition of supportedSquidChainsAndTokens from context aligns with the PR's goal of utilizing Squid's supported token list.


436-436: Verify chain name consistency

The code uses different sources for chain names: supportedPeanutChains for source chain and supportedSquidChainsAndTokens for destination chain.

Let's verify the chain name consistency:

Also applies to: 481-495

✅ Verification successful

Let me gather more information about how these chain names are used and defined.


Let me check one more thing to understand how these chain names are mapped or validated between Peanut and Squid.


Based on the gathered information, I can now provide a final response:

Chain name handling is properly implemented and consistent

The code correctly handles chain names between Peanut and Squid implementations:

  • For source chains, it uses supportedPeanutChains.find(chain => chain.chainId === chainId)?.name which ensures chain ID-based lookup
  • For destination chains, it uses supportedSquidChainsAndTokens[chainId]?.axelarChainName which is appropriate for Squid/Axelar integration
  • The code includes proper validation and mapping through getBridgeChainName function
  • Chain IDs are consistently used as the source of truth for lookups in both cases
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential chain name mismatches between Peanut and Squid implementations
ast-grep --pattern 'supportedPeanutChains.find((chain) => chain.chainId === $_.chainId)?.name'

# Check Squid chain name usage
ast-grep --pattern 'supportedSquidChainsAndTokens[$_]?.axelarChainName'

Length of output: 1617


Script:

#!/bin/bash
# Check the definition and usage of supportedPeanutChains
ast-grep --pattern 'const supportedPeanutChains = $_'

# Check the definition and usage of supportedSquidChainsAndTokens
ast-grep --pattern 'const supportedSquidChainsAndTokens = $_'

# Check imports of these constants
rg "import.*supportedPeanutChains|import.*supportedSquidChainsAndTokens" -A 2

Length of output: 1071


Script:

#!/bin/bash
# Check for any chain name mapping or validation logic
rg "chainName|chainId" src/components/Claim/Link/Initial.view.tsx -B 2 -A 2

# Check the constants file for chain definitions
fd "constants.ts" --exec cat {}

Length of output: 3391

src/components/Global/TokenSelector/TokenSelector.tsx (2)

350-356: Verify TypeScript types for chainsToDisplay

At lines 350~ to 356~, ensure that the chainsToDisplay prop passed to ChainSelector has the correct type, especially after filtering supportedPeanutChains.

Confirm that the filtered supportedPeanutChains array matches the expected type for ChainSelector. If necessary, adjust the types or provide type assertions to prevent type errors.


170-175: 🛠️ Refactor suggestion

Avoid setTimeout for state synchronization

Using setTimeout to synchronize state after closing the modal can lead to timing issues and race conditions. Instead, utilize the useEffect hook to reset state based on the visible state.

Apply this refactor to reset state when the modal visibility changes:

function setToken(balance: IUserBalance): void {
    setSelectedChainID(balance.chainId)
    setSelectedTokenAddress(balance.address)
    setVisible(false)
-   setTimeout(() => {
-       setFilterValue('')
-       setUserChangedChain(false)
-   }, 200) // the modal takes 200ms to close (duration-200 on leave)
}

+ useEffect(() => {
+     if (!visible) {
+         setFilterValue('')
+         setUserChangedChain(false)
+     }
+ }, [visible])

This approach ensures state is reset immediately after the modal closes, improving reliability.

Likely invalid or redundant comment.

src/app/actions/squid.ts Outdated Show resolved Hide resolved
src/context/tokenSelector.context.tsx Show resolved Hide resolved
src/components/Claim/Claim.tsx Show resolved Hide resolved
src/components/Claim/Link/Onchain/Confirm.view.tsx Outdated Show resolved Hide resolved
src/components/Claim/Link/Initial.view.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
src/app/actions/squid.ts (1)

19-40: Add input validation and improve cache key naming.

The concurrent API calls and type-safe data processing are well implemented. However, there are a few improvements that could make the function more robust.

Consider these enhancements:

 export const getSquidChainsAndTokens = unstable_cache(
     async (): Promise<Record<string, interfaces.ISquidChain & { tokens: interfaces.ISquidToken[] }>> => {
+        if (process.env.NODE_ENV === 'development') {
+            console.debug('Fetching Squid chains and tokens...')
+        }
+
         const [chains, tokens] = await Promise.all([getSquidChainsCache(), getSquidTokensCache()])
 
+        if (!chains?.length || !tokens?.length) {
+            throw new Error('Failed to fetch Squid data: Empty chains or tokens list')
+        }
+
         const chainsById = chains
             .filter(supportedByPeanut)
             .reduce<Record<string, interfaces.ISquidChain & { tokens: interfaces.ISquidToken[] }>>((acc, chain) => {
                 acc[chain.chainId] = { ...chain, tokens: [] }
                 return acc
             }, {})
 
+        if (Object.keys(chainsById).length === 0) {
+            console.warn('No supported chains found in Squid data')
+        }
+
         tokens.forEach((token) => {
             if (token.active && token.chainId in chainsById) {
                 chainsById[token.chainId].tokens.push(token)
             }
         })
 
         return chainsById
     },
-    ['getSquidChainsAndTokens'],
+    ['squid:chains-and-tokens:v1'],
     { revalidate: 3600 * 12 }
 )
src/components/Claim/Link/Onchain/Confirm.view.tsx (1)

210-218: Consider optimizing chain information lookup

The same chain information is looked up multiple times. Consider storing the chain name in a variable to avoid repeated object property access.

+ const destinationChainName = supportedSquidChainsAndTokens[selectedRoute.route.params.toChain]?.axelarChainName;
  // ...
- {supportedSquidChainsAndTokens[selectedRoute.route.params.toChain]?.axelarChainName}
+ {destinationChainName}
  <MoreInfo
      text={`You are bridging ${claimLinkData.tokenSymbol.toLowerCase()} on ${
          consts.supportedPeanutChains.find(
              (chain) => chain.chainId === selectedRoute.route.params.fromChain
          )?.name
-     } to ${selectedRoute.route.estimate.toToken.symbol.toLowerCase()} on  ${
-         supportedSquidChainsAndTokens[selectedRoute.route.params.toChain]
-             ?.axelarChainName
+     } to ${selectedRoute.route.estimate.toToken.symbol.toLowerCase()} on ${
+         destinationChainName
      }.`}
  />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bc821fb and ba54373.

📒 Files selected for processing (2)
  • src/app/actions/squid.ts (1 hunks)
  • src/components/Claim/Link/Onchain/Confirm.view.tsx (3 hunks)
🔇 Additional comments (4)
src/app/actions/squid.ts (2)

15-18: LGTM! Well-structured helper function.

The supportedByPeanut function is well-implemented with clear logic and efficient chain filtering.


1-40: Verify implementation against PR objectives.

The implementation aligns well with the PR objectives, particularly:

  1. Fetching Squid-supported tokens
  2. Implementing 12-hour caching
  3. Supporting lazy loading of token data

Let's verify the token selector integration:

✅ Verification successful

Implementation successfully integrates with token selector and meets objectives

The verification confirms that:

  1. The getSquidChainsAndTokens function is properly integrated into the token selector context (tokenSelector.context.tsx)
  2. The implementation includes proper state management for chains and tokens through supportedSquidChainsAndTokens
  3. The data is fetched on component mount and cached for 12 hours as required
  4. The context provides the necessary interface for token selection and chain management
  5. Token data is lazily loaded through the context's useEffect hook, avoiding unnecessary fetches

The code structure effectively supports the PR objectives of populating the token selector with Squid-supported tokens while maintaining efficient caching and lazy loading patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify token selector component integration

# Check if the token selector component imports this function
rg -l "getSquidChainsAndTokens"

# Look for lazy loading implementation in token selector
rg "loading=('lazy'|\"lazy\")" --type tsx

Length of output: 170


Script:

#!/bin/bash
# Check token selector context implementation
cat src/context/tokenSelector.context.tsx

# Look for lazy loading with correct file extension
rg "loading=('lazy'|\"lazy\")" -t ts -t tsx

# Check if there are any other files using the token selector
rg -l "useTokenSelector"

Length of output: 7464

src/components/Claim/Link/Onchain/Confirm.view.tsx (2)

32-34: LGTM: Context usage aligns with PR objectives

The addition of supportedSquidChainsAndTokens from the token selector context is consistent with the PR's goal of utilizing Squid-supported tokens for the selector functionality.


174-174: Previous review comment still applies

The optional chaining usage for accessing axelarChainName is consistent with previous review recommendations.

src/app/actions/squid.ts Show resolved Hide resolved
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 and nitpick comments (2)
sentry.edge.config.ts (2)

15-21: Optimize header removal while maintaining security

Good security practice to remove sensitive headers. However, the current implementation uses the delete operator which can impact performance.

Consider using undefined assignment instead:

  beforeSend(event) {
    if (event.request?.headers) {
-     delete event.request.headers['Authorization']
-     delete event.request.headers['api-key']
+     event.request.headers['Authorization'] = undefined
+     event.request.headers['api-key'] = undefined
    }
    return event
  },
🧰 Tools
🪛 Biome

[error] 17-17: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 18-18: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


14-22: Consider adding specific error monitoring for token selector

Since this PR introduces new token selector functionality with Squid integration and caching, consider adding specific error monitoring.

Suggestions for monitoring:

  1. Track failed Squid token fetching attempts
  2. Monitor cache hits/misses for token list
  3. Track token selector rendering errors
  4. Monitor cross-chain support validation errors

Would you like assistance in implementing these monitoring points?

🧰 Tools
🪛 Biome

[error] 17-17: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 18-18: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ba54373 and faeefa1.

📒 Files selected for processing (1)
  • sentry.edge.config.ts (1 hunks)
🧰 Additional context used
🪛 Biome
sentry.edge.config.ts

[error] 17-17: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 18-18: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

@jjramirezn
Copy link
Contributor Author

@jjramirezn
Copy link
Contributor Author

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 and nitpick comments (4)
src/components/Global/TokenSelector/TokenSelector.tsx (4)

30-49: Consider enhancing virtual scrolling implementation.

The virtual scrolling implementation is good, but could be improved:

  1. Define ITEM_HEIGHT as a named constant outside the component
  2. Consider making buffer size configurable
  3. Add ResizeObserver to handle container size changes
+ const TOKEN_LIST_ITEM_HEIGHT = 56;
+ const BUFFER_SIZE = 3;

const TokenList = ({ balances, setToken }: { balances: IUserBalance[]; setToken: (address: IUserBalance) => void }) => {
-   const ITEM_HEIGHT = 56
+   const ITEM_HEIGHT = TOKEN_LIST_ITEM_HEIGHT
    // ... rest of the code
    const handleScroll = () => {
        if (!containerRef.current) return

        const element = containerRef.current
        const scrollTop = element.scrollTop
        const viewportHeight = element.clientHeight

        const start = Math.floor(scrollTop / ITEM_HEIGHT)
        const visibleCount = Math.ceil(viewportHeight / ITEM_HEIGHT)
-       const end = Math.min(start + visibleCount + 3, balances.length)
+       const end = Math.min(start + visibleCount + BUFFER_SIZE, balances.length)

        setVisibleRange({
-           start: Math.max(0, start - 3),
+           start: Math.max(0, start - BUFFER_SIZE),
            end,
        })
    }

115-125: Use Next.js Image component for chain icons.

For consistency and better performance, use the Next.js Image component for chain icons as well.

- <img
-     src={supportedSquidChainsAndTokens[balance.chainId]?.chainIconURI}
-     className="absolute -top-1 left-3 h-4 w-4 rounded-full"
-     alt="logo"
-     onError={(_e) => {
-         setChainPlaceholders((prev) => ({
-             ...prev,
-             [`${balance.address}_${balance.chainId}`]: true,
-         }))
-     }}
- />
+ <Image
+     src={supportedSquidChainsAndTokens[balance.chainId]?.chainIconURI}
+     className="absolute -top-1 left-3 h-4 w-4 rounded-full"
+     alt={`${supportedSquidChainsAndTokens[balance.chainId]?.axelarChainName ?? 'Chain'} logo`}
+     width={16}
+     height={16}
+     unoptimized
+     onError={(_e) => {
+         setChainPlaceholders((prev) => ({
+             ...prev,
+             [`${balance.address}_${balance.chainId}`]: true,
+         }))
+     }}
+ />

Line range hint 266-279: Simplify and improve selectedBalance update logic.

The current implementation could be more robust and easier to maintain.

 useEffect(() => {
-    if (selectedBalance) return
-
-    if (_balancesToDisplay.length > 0) {
-        setSelectedBalance(
-            _balancesToDisplay.find(
-                (balance) =>
-                    areTokenAddressesEqual(balance.address, selectedTokenAddress) &&
-                    balance.chainId === selectedChainID
-            )
-        )
-    } else {
-        setSelectedBalance(undefined)
-    }
+    const matchingBalance = _balancesToDisplay.find(
+        (balance) =>
+            areTokenAddressesEqual(balance.address, selectedTokenAddress) &&
+            balance.chainId === selectedChainID
+    );
+    
+    if (!selectedBalance || 
+        !areTokenAddressesEqual(selectedBalance.address, selectedTokenAddress) ||
+        selectedBalance.chainId !== selectedChainID) {
+        setSelectedBalance(matchingBalance ?? undefined);
+    }
 }, [_balancesToDisplay, selectedTokenAddress, selectedChainID])

This change:

  1. Removes the early return which could prevent necessary updates
  2. Simplifies the logic by combining conditions
  3. Updates selectedBalance when chain or token address changes

353-359: Simplify chain filtering logic.

The nested ternary and filter conditions can be simplified for better readability.

- chainsToDisplay={
-     showOnlySquidSupported
-         ? supportedPeanutChains.filter(
-               (chain) => !!supportedSquidChainsAndTokens[chain.chainId]
-           )
-         : supportedPeanutChains
- }
+ chainsToDisplay={supportedPeanutChains.filter(
+     (chain) => !showOnlySquidSupported || !!supportedSquidChainsAndTokens[chain.chainId]
+ )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1fe6c2f and fb27dbb.

📒 Files selected for processing (1)
  • src/components/Global/TokenSelector/TokenSelector.tsx (7 hunks)
🧰 Additional context used
📓 Learnings (1)
src/components/Global/TokenSelector/TokenSelector.tsx (2)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#424
File: src/components/Global/TokenSelector/TokenSelector.tsx:197-211
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Global/TokenSelector/TokenSelector.tsx`, when the calculation within functions like `byChainAndText` is not computationally expensive, it's acceptable to avoid using `useCallback` for memoization.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-11-12T09:39:20.720Z
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.

Copy link
Contributor

@Hugo0 Hugo0 left a comment

Choose a reason for hiding this comment

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

@@ -0,0 +1,40 @@
'use server'

import { getSquidChains, getSquidTokens } from '@squirrel-labs/peanut-sdk'
Copy link
Contributor

Choose a reason for hiding this comment

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

Thought: we might want to integrate across.to or a tron bridge in the future. Might want to add an abstraction layer here (ie not SquidChains, but X-chain supported chains)

Comment on lines -13 to +12
import { IToken, IUserBalance } from '@/interfaces'
import { IUserBalance, IToken } from '@/interfaces'
Copy link
Contributor

Choose a reason for hiding this comment

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

lol? how did this happen? Automated formatting?

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

🧹 Outside diff range and nitpick comments (13)
src/components/Global/ConfirmDetails/Index.tsx (1)

4-9: Props interface changes improve component reusability

The updated interface with direct token and chain information makes the component more modular and reduces coupling to external token/chain lookup logic.

This change follows good architectural principles by:

  • Reducing component coupling
  • Making the component more reusable
  • Simplifying the data flow
src/components/Global/ChainSelector/index.tsx (2)

102-115: Improve code readability and performance

The chain items implementation could be improved in two ways:

  1. Simplify the nested valuePerChain logic
  2. Consider memoizing the chain items to prevent unnecessary re-renders
+const getChainValue = (chain: Chain, valuePerChain: any[], showValue: boolean) => {
+    if (!showValue) return undefined;
+    return valuePerChain.find((value) => value.chainId === chain.chainId)?.valuePerChain;
+};

+const memoizedChainItems = useMemo(() => 
+    _chainsToDisplay.map(
         (chain) =>
             chain.mainnet &&
             chainItem({
                 chain,
                 setChain: () => setChain(chain.chainId),
-                valuePerChain: !chainsToDisplay
-                    ? valuePerChain.find((value) => value.chainId === chain.chainId)
-                          ?.valuePerChain
-                    : undefined,
+                valuePerChain: getChainValue(chain, valuePerChain, !chainsToDisplay),
             })
-    )
+    ),
+    [_chainsToDisplay, chainsToDisplay, valuePerChain]
+);

Line range hint 136-144: Fix key prop placement

The key prop should be placed before other props to avoid React warnings and ensure proper component reconciliation.

 <Menu.Item
+    key={chain.name}
     as="button"
     onClick={setChain}
     className="flex h-12 w-full items-center justify-between gap-2 px-4 text-sm font-bold transition-colors last:mb-0 hover:bg-n-3/10 dark:hover:bg-white/20"
-    key={chain.name}
 >
src/components/Global/TokenSelector/Components/AdvancedButton.tsx (1)

76-88: Consider adding request cancellation for token symbol fetches.

While the mounted flag prevents state updates after unmount, rapid changes to selectedTokenAddress or selectedChainID could lead to out-of-order responses. Consider using AbortController to cancel pending requests.

 useEffect(() => {
     let isMounted = true
+    const controller = new AbortController()
     if (!tokenSymbol) {
-        fetchTokenSymbol(selectedTokenAddress, selectedChainID).then((symbol) => {
+        fetchTokenSymbol(selectedTokenAddress, selectedChainID, controller.signal).then((symbol) => {
             if (isMounted) {
                 _setTokenSymbol(symbol)
             }
         })
     }
     return () => {
         isMounted = false
+        controller.abort()
     }
 }, [tokenSymbol, selectedTokenAddress, selectedChainID])
src/hooks/useBalance.tsx (2)

24-31: Add cleanup and optimize dependencies

The disconnection handling is well-implemented, but could be enhanced with:

  1. A cleanup function to cancel pending operations
  2. Adding refetchBalances to the dependency array

Consider updating the effect:

 useEffect(() => {
     if (!isConnected) {
         setBalances([])
     } else {
         refetchBalances()
     }
+    return () => {
+        // Cleanup any pending operations
+    }
-}, [isConnected])
+}, [isConnected, refetchBalances])

Line range hint 120-127: Consider externalizing chain ID mappings

The hardcoded chain ID mappings could be moved to a configuration file for better maintainability and reusability.

Consider:

  1. Moving the chain ID mappings to a separate configuration file
  2. Adding TypeScript types for the chain IDs
  3. Implementing a mapping utility function

Example structure:

// chainConfig.ts
export const CHAIN_ID_MAPPINGS = {
  '8508132': '534352', // Base Sepolia -> Base
  '81032': '81457',    // Shibuya -> Astar
  '59160': '59144'     // Linea testnet -> Linea
} as const;

// utils/chains.ts
export const mapChainId = (chainId: string): string => 
  CHAIN_ID_MAPPINGS[chainId] || chainId;
src/components/Create/Link/Confirm.view.tsx (1)

207-208: Add error handling for sharing operations.

The email and SMS sharing operations should include error handling to manage potential failures gracefully.

-                if (createType === 'email_link') shareToEmail(recipient.name ?? '', link[0], usdValue)
-                if (createType === 'sms_link') shareToSms(recipient.name ?? '', link[0], usdValue)
+                try {
+                    if (createType === 'email_link') await shareToEmail(recipient.name ?? '', link[0], usdValue)
+                    if (createType === 'sms_link') await shareToSms(recipient.name ?? '', link[0], usdValue)
+                } catch (error) {
+                    console.error('Failed to share link:', error)
+                    // Consider showing a user-friendly notification that sharing failed
+                    // but the link was still created successfully
+                }
src/components/Global/TokenSelector/TokenSelector.tsx (4)

30-49: Enhance virtual scrolling implementation

The virtual scrolling implementation is good but could be improved for better maintainability and flexibility.

Consider these improvements:

+const TOKEN_LIST_ITEM_HEIGHT = 56
+const BUFFER_SIZE = 3
+
 const TokenList = ({ balances, setToken }: { balances: IUserBalance[]; setToken: (address: IUserBalance) => void }) => {
     const containerRef = useRef<HTMLDivElement>(null)
     const [visibleRange, setVisibleRange] = useState({ start: 0, end: 10 })
-    const ITEM_HEIGHT = 56
 
     const handleScroll = () => {
         if (!containerRef.current) return
 
         const element = containerRef.current
         const scrollTop = element.scrollTop
         const viewportHeight = element.clientHeight
 
-        const start = Math.floor(scrollTop / ITEM_HEIGHT)
-        const visibleCount = Math.ceil(viewportHeight / ITEM_HEIGHT)
-        const end = Math.min(start + visibleCount + 3, balances.length)
+        const start = Math.floor(scrollTop / TOKEN_LIST_ITEM_HEIGHT)
+        const visibleCount = Math.ceil(viewportHeight / TOKEN_LIST_ITEM_HEIGHT)
+        const end = Math.min(start + visibleCount + BUFFER_SIZE, balances.length)
 
         setVisibleRange({
-            start: Math.max(0, start - 3),
+            start: Math.max(0, start - BUFFER_SIZE),
             end,
         })
     }

Also consider adding a resize observer to handle viewport changes:

useEffect(() => {
    const element = containerRef.current
    if (element) {
        const resizeObserver = new ResizeObserver(handleScroll)
        resizeObserver.observe(element)
        return () => resizeObserver.disconnect()
    }
}, [])

116-120: Simplify chain icon URL resolution

The current nested ternary operation for resolving chain icons is hard to read and maintain.

Consider extracting the logic into a helper function:

+const getChainIconUrl = (chainId: number, supportedSquidChainsAndTokens: any, supportedPeanutChains: any) => {
+    const squidChain = supportedSquidChainsAndTokens[chainId]
+    if (squidChain?.chainIconURI) return squidChain.chainIconURI
+    
+    const peanutChain = supportedPeanutChains.find((c) => c.chainId === chainId)
+    return peanutChain?.icon.url
+}
+
-src={
-    supportedSquidChainsAndTokens[balance.chainId]?.chainIconURI ??
-    supportedPeanutChains.find((c) => c.chainId === balance.chainId)?.icon
-        .url
-}
+src={getChainIconUrl(balance.chainId, supportedSquidChainsAndTokens, supportedPeanutChains)}

292-309: Simplify chain display logic

The chain resolution logic is duplicated and could be simplified using a common interface.

Consider this refactor:

+interface DisplayChain {
+    name: string;
+    iconURI: string;
+    chainId: number;
+}
+
+const getDisplayedChain = (
+    chainId: number | undefined,
+    supportedSquidChainsAndTokens: Record<number, any>,
+    supportedPeanutChains: any[]
+): DisplayChain | undefined => {
+    if (!chainId) return undefined
+
+    const squidChain = supportedSquidChainsAndTokens[chainId]
+    if (squidChain) {
+        return {
+            name: squidChain.axelarChainName,
+            iconURI: squidChain.chainIconURI,
+            chainId: squidChain.chainId,
+        }
+    }
+
+    const peanutChain = supportedPeanutChains.find((chain) => chain.chainId === chainId)
+    return peanutChain ? {
+        name: peanutChain.name,
+        iconURI: peanutChain.icon.url,
+        chainId: peanutChain.chainId,
+    } : undefined
+}
+
-const displayedChain = useMemo(() => {
-    if (!selectedChainID) return undefined
-    if (supportedSquidChainsAndTokens[selectedChainID]) {
-        const chain = supportedSquidChainsAndTokens[selectedChainID]
-        return {
-            name: chain.axelarChainName,
-            iconURI: chain.chainIconURI,
-            chainId: chain.chainId,
-        }
-    } else {
-        const chain = supportedPeanutChains.find((chain) => chain.chainId === selectedChainID)
-        return {
-            name: chain?.name,
-            iconURI: chain?.icon.url,
-            chainId: chain?.chainId,
-        }
-    }
-}, [selectedChainID, supportedSquidChainsAndTokens])
+const displayedChain = useMemo(
+    () => getDisplayedChain(selectedChainID, supportedSquidChainsAndTokens, supportedPeanutChains),
+    [selectedChainID, supportedSquidChainsAndTokens]
+)

378-384: Simplify chain filtering logic

The chain filtering logic could be more readable and maintainable.

Consider extracting the filtering logic:

+const getDisplayableChains = (
+    showOnlySquidSupported: boolean,
+    supportedPeanutChains: any[],
+    supportedSquidChainsAndTokens: Record<number, any>
+) => {
+    if (!showOnlySquidSupported) return supportedPeanutChains
+    return supportedPeanutChains.filter((chain) => !!supportedSquidChainsAndTokens[chain.chainId])
+}
+
-chainsToDisplay={
-    showOnlySquidSupported
-        ? supportedPeanutChains.filter(
-              (chain) => !!supportedSquidChainsAndTokens[chain.chainId]
-          )
-        : supportedPeanutChains
-}
+chainsToDisplay={getDisplayableChains(
+    showOnlySquidSupported,
+    supportedPeanutChains,
+    supportedSquidChainsAndTokens
+)}
src/components/Claim/Link/Initial.view.tsx (2)

434-454: Consider adding loading state for token support check

The token selector implementation looks good, but there's no loading state while checking token support. This could lead to a flash of content if the supportedSquidChainsAndTokens data is still loading.

Consider adding a loading state:

 {recipientType !== 'iban' && recipientType !== 'us' ? (
+  supportedSquidChainsAndTokens[claimLinkData.chainId] ? (
     tokenSupportsXChain ? (
       <TokenSelector
         shouldBeConnected={false}
         showOnlySquidSupported
         onReset={() => {
           setSelectedChainID(claimLinkData.chainId)
           setSelectedTokenAddress(claimLinkData.tokenAddress)
         }}
       />
     ) : (
       <label className="text-h7 font-light">
         This token does not support cross-chain claims...
       </label>
     )
+  ) : (
+    <div className="text-h7 font-light">Loading token support...</div>
+  )
 ) : null}

620-621: Consider adding error handling for reset operation

The reset operation in the error state looks good, but it might benefit from error handling to ensure the chain ID and token address are valid.

Add validation:

-setSelectedChainID(claimLinkData.chainId)
-setSelectedTokenAddress(claimLinkData.tokenAddress)
+if (claimLinkData?.chainId && claimLinkData?.tokenAddress) {
+  setSelectedChainID(claimLinkData.chainId)
+  setSelectedTokenAddress(claimLinkData.tokenAddress)
+} else {
+  console.error('Invalid claim link data for reset')
+  setErrorState({
+    showError: true,
+    errorMessage: 'Unable to reset token selection',
+  })
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fb27dbb and 1295578.

📒 Files selected for processing (8)
  • src/components/Claim/Generic/AlreadyClaimed.view.tsx (0 hunks)
  • src/components/Claim/Link/Initial.view.tsx (8 hunks)
  • src/components/Create/Link/Confirm.view.tsx (10 hunks)
  • src/components/Global/ChainSelector/index.tsx (3 hunks)
  • src/components/Global/ConfirmDetails/Index.tsx (2 hunks)
  • src/components/Global/TokenSelector/Components/AdvancedButton.tsx (5 hunks)
  • src/components/Global/TokenSelector/TokenSelector.tsx (7 hunks)
  • src/hooks/useBalance.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Claim/Generic/AlreadyClaimed.view.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Global/TokenSelector/TokenSelector.tsx (2)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-11-12T09:39:20.720Z
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.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#424
File: src/components/Global/TokenSelector/TokenSelector.tsx:197-211
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Global/TokenSelector/TokenSelector.tsx`, when the calculation within functions like `byChainAndText` is not computationally expensive, it's acceptable to avoid using `useCallback` for memoization.
🔇 Additional comments (13)
src/components/Global/ConfirmDetails/Index.tsx (1)

Line range hint 1-53: Overall implementation looks good

The component is well-structured, type-safe, and implements the required functionality cleanly. The changes align well with the PR objectives of improving token selector functionality.

src/components/Global/ChainSelector/index.tsx (1)

82-89: LGTM! Well-implemented transition effects

The transition implementation provides smooth animation with appropriate timing for both enter and leave states.

src/components/Global/TokenSelector/Components/AdvancedButton.tsx (3)

43-46: LGTM! Good handling of wallet connection state.

The addition of isConnected from useAccount hook and proper state initialization improves the component's ability to handle disconnected wallet states.


65-74: LGTM! Proper handling of balance updates.

The effect hook correctly manages balance states:

  • Sets balance to undefined when disconnected
  • Only fetches balance when necessary
  • Includes all required dependencies

128-128: LGTM! Clean UI state handling.

The UI updates properly handle all states:

  • Uses managed token symbol state
  • Correctly displays loading state when fetching balance
  • Properly handles disconnected state

Also applies to: 132-140

src/hooks/useBalance.tsx (1)

14-14: LGTM: Clean addition of connection status tracking

The addition of isConnected from useAccount hook is well-integrated and follows React hooks best practices.

src/components/Create/Link/Confirm.view.tsx (4)

7-17: LGTM! Well-organized utility imports.

The new utility imports are properly structured and align with the component's responsibilities for token handling, storage management, and error handling.


Line range hint 157-189: LGTM! Well-structured storage operations.

The local storage operations are properly implemented with comprehensive data storage for both direct sends and created links. The peanut preferences update maintains user settings for future use.

Also applies to: 211-215


Line range hint 220-224: LGTM! Proper error handling implementation.

The error handling using the ErrorHandler utility is well-implemented with clear error state management.


236-237: LGTM! Clean and consistent UI updates.

The UI changes for displaying addresses, token/chain information, and transaction costs are well-implemented and maintain consistency throughout the component.

Also applies to: 250-253, 307-307, 311-311

src/components/Global/TokenSelector/TokenSelector.tsx (1)

Line range hint 1-400: Overall implementation looks good!

The TokenSelector component successfully implements the required functionality with proper error handling, virtual scrolling, and cross-chain support. The suggested improvements focus on code organization and maintainability rather than functional issues.

src/components/Claim/Link/Initial.view.tsx (2)

145-151: Good implementation of token support check using useMemo

The memoization of tokenSupportsXChain is a good performance optimization since it only recomputes when the dependencies change.


Line range hint 493-507: Verify chain name fallback handling

The code assumes supportedSquidChainsAndTokens[chainId]?.axelarChainName will always be available. Consider adding a fallback chain name.

Let's verify the chain name availability:

Add a fallback:

-supportedSquidChainsAndTokens[selectedRoute.route.params.toChain]?.axelarChainName
+supportedSquidChainsAndTokens[selectedRoute.route.params.toChain]?.axelarChainName ?? 'Unknown Chain'

Comment on lines +44 to +49
{chainIconUri ? (
<img src={chainIconUri} className="h-6 w-6" />
) : (
<Icon name="chain_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-sm font-bold text-gray-1">{chainName}</label>
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 accessibility attributes and lazy loading to chain image

The chain icon image needs the same improvements as the token icon.

Apply these improvements:

-<img src={chainIconUri} className="h-6 w-6" />
+<img 
+  src={chainIconUri} 
+  className="h-6 w-6" 
+  alt={`${chainName} chain icon`}
+  loading="lazy"
+/>
📝 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
{chainIconUri ? (
<img src={chainIconUri} className="h-6 w-6" />
) : (
<Icon name="chain_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-sm font-bold text-gray-1">{chainName}</label>
{chainIconUri ? (
<img
src={chainIconUri}
className="h-6 w-6"
alt={`${chainName} chain icon`}
loading="lazy"
/>
) : (
<Icon name="chain_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-sm font-bold text-gray-1">{chainName}</label>

Comment on lines +28 to +34
{tokenIconUri ? (
<img src={tokenIconUri} className="h-6 w-6" />
) : (
<Icon name="token_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-h5 sm:text-h3">
{formatTokenAmount(Number(tokenAmount))}{' '}
{data
? data
.find((chain: any) => chain.chainId === selectedChainID)
?.tokens.find((token: any) =>
areTokenAddressesEqual(token.address, selectedTokenAddress)
)?.symbol
: getTokenSymbol(selectedTokenAddress, selectedChainID)}
{formatTokenAmount(Number(tokenAmount))} {tokenSymbol}
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 accessibility attributes and lazy loading to image

While the fallback logic is good, the image tag needs accessibility improvements and lazy loading as mentioned in the PR objectives.

Apply these improvements:

-<img src={tokenIconUri} className="h-6 w-6" />
+<img 
+  src={tokenIconUri} 
+  className="h-6 w-6" 
+  alt={`${tokenSymbol} token icon`}
+  loading="lazy"
+/>
📝 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
{tokenIconUri ? (
<img src={tokenIconUri} className="h-6 w-6" />
) : (
<Icon name="token_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-h5 sm:text-h3">
{formatTokenAmount(Number(tokenAmount))}{' '}
{data
? data
.find((chain: any) => chain.chainId === selectedChainID)
?.tokens.find((token: any) =>
areTokenAddressesEqual(token.address, selectedTokenAddress)
)?.symbol
: getTokenSymbol(selectedTokenAddress, selectedChainID)}
{formatTokenAmount(Number(tokenAmount))} {tokenSymbol}
{tokenIconUri ? (
<img
src={tokenIconUri}
className="h-6 w-6"
alt={`${tokenSymbol} token icon`}
loading="lazy"
/>
) : (
<Icon name="token_placeholder" className="h-6 w-6" fill="#999" />
)}
<label className="text-h5 sm:text-h3">
{formatTokenAmount(Number(tokenAmount))} {tokenSymbol}

Comment on lines 71 to 74
src={_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.icon.url}
alt={''}
className="h-6 w-6"
/>
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 descriptive alt text for accessibility

The chain icon's alt text is empty, which impacts screen reader accessibility. Consider adding a descriptive alt text that includes the chain name.

 <img
     src={_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.icon.url}
-    alt={''}
+    alt={`${_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.name} chain icon`}
     className="h-6 w-6"
 />
📝 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
src={_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.icon.url}
alt={''}
className="h-6 w-6"
/>
src={_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.icon.url}
alt={`${_chainsToDisplay.find((chain) => chain.chainId === selectedChainID)?.name} chain icon`}
className="h-6 w-6"
/>

Comment on lines +91 to +100
<div className="sticky top-0 bg-white p-2 dark:bg-n-1">
<Search
className="px-1"
placeholder="Search by chain name"
value={filterValue}
onChange={(e: any) => setFilterValue(e.target.value)}
onSubmit={() => {}}
medium
/>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Fix type safety and remove unused prop

  1. The Search component accepts onChange and onSubmit props with any type, but onSubmit is never used in the implementation
  2. The onChange handler should use the proper React input event type
  3. Since onSubmit is unused, it should be removed from the component props
 type SearchProps = {
     className?: string
     placeholder: string
     value: string
-    onChange: any
-    onSubmit: any
+    onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
     large?: boolean
     medium?: boolean
     border?: boolean
 }

-const Search = ({ className, placeholder, value, onChange, onSubmit, large, medium, border }: SearchProps) => {
+const Search = ({ className, placeholder, value, onChange, large, medium, border }: SearchProps) => {
🔗 Analysis chain

Improve type safety and verify empty handler

Two potential improvements in the Search component implementation:

  1. The event type is set to 'any' which bypasses TypeScript's type checking
  2. There's an empty onSubmit handler which might need implementation
 <Search
     className="px-1"
     placeholder="Search by chain name"
     value={filterValue}
-    onChange={(e: any) => setFilterValue(e.target.value)}
+    onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFilterValue(e.target.value)}
     onSubmit={() => {}}
     medium
 />
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Search component's onSubmit is used elsewhere
rg "onSubmit.*=.*{.*}" --type typescript

Length of output: 156


Script:

#!/bin/bash
# Check if Search component's onSubmit is used elsewhere with proper escaping
rg "onSubmit\s*=\s*\{" --type typescript

# Also check the Search component's implementation
ast-grep --pattern 'interface $_ {
  $$$
  onSubmit$_
  $$$
}'

Length of output: 142


Script:

#!/bin/bash
# Check if Search component's onSubmit is used elsewhere with proper escaping
rg "onSubmit\s*=\s*\{" -t ts -t tsx

# Find Search component definition
fd "Search.(ts|tsx)" --exec cat {}

Length of output: 104


Script:

#!/bin/bash
# Search for files containing Search component
fd -e ts -e tsx | xargs rg "onSubmit.*=" -l

# Find Search component definition
find . -type f -name "Search.ts" -o -name "Search.tsx" -o -name "search.ts" -o -name "search.tsx" | xargs cat

Length of output: 800


Script:

#!/bin/bash
# Check the Search component implementation
cat src/components/Global/Search/index.tsx

# Check how onSubmit is used in other components for context
rg "onSubmit.*=" -A 2 src/components/Global/Search/index.tsx

Length of output: 2071

Comment on lines +79 to +112
const selectedChain = useMemo(() => {
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
return {
name: chain.axelarChainName,
iconUri: chain.chainIconURI,
}
} else {
const chain = supportedPeanutChains.find((chain) => chain.chainId === selectedChainID)
return {
name: chain?.name,
iconUri: chain?.icon.url,
}
}
}, [supportedSquidChainsAndTokens, selectedChainID])

const selectedToken = useMemo(() => {
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
const token = chain.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return {
symbol: token?.symbol,
iconUri: token?.logoURI,
}
} else {
const token = peanutTokenDetails
.find((tokenDetails) => tokenDetails.chainId === selectedChainID)
?.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return {
symbol: token?.symbol,
iconUri: token?.logoURI,
}
}
}, [selectedChainID, selectedTokenAddress, supportedSquidChainsAndTokens])
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 null checks and type safety to prevent potential runtime errors.

The memoization logic for selectedChain and selectedToken could encounter undefined values in edge cases. Consider adding proper null checks and type safety:

 const selectedChain = useMemo(() => {
     if (supportedSquidChainsAndTokens[selectedChainID]) {
         const chain = supportedSquidChainsAndTokens[selectedChainID]
         return {
-            name: chain.axelarChainName,
-            iconUri: chain.chainIconURI,
+            name: chain?.axelarChainName ?? 'Unknown Chain',
+            iconUri: chain?.chainIconURI ?? '',
         }
     } else {
         const chain = supportedPeanutChains.find((chain) => chain.chainId === selectedChainID)
         return {
-            name: chain?.name,
-            iconUri: chain?.icon.url,
+            name: chain?.name ?? 'Unknown Chain',
+            iconUri: chain?.icon?.url ?? '',
         }
     }
 }, [supportedSquidChainsAndTokens, selectedChainID])

 const selectedToken = useMemo(() => {
+    const defaultToken = {
+        symbol: 'Unknown Token',
+        iconUri: '',
+    }
     if (supportedSquidChainsAndTokens[selectedChainID]) {
         const chain = supportedSquidChainsAndTokens[selectedChainID]
         const token = chain.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
-        return {
-            symbol: token?.symbol,
-            iconUri: token?.logoURI,
-        }
+        return token ? {
+            symbol: token.symbol ?? defaultToken.symbol,
+            iconUri: token.logoURI ?? defaultToken.iconUri,
+        } : defaultToken
     } else {
         const token = peanutTokenDetails
             .find((tokenDetails) => tokenDetails.chainId === selectedChainID)
             ?.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
-        return {
-            symbol: token?.symbol,
-            iconUri: token?.logoURI,
-        }
+        return token ? {
+            symbol: token.symbol ?? defaultToken.symbol,
+            iconUri: token.logoURI ?? defaultToken.iconUri,
+        } : defaultToken
     }
 }, [selectedChainID, selectedTokenAddress, supportedSquidChainsAndTokens])
📝 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 selectedChain = useMemo(() => {
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
return {
name: chain.axelarChainName,
iconUri: chain.chainIconURI,
}
} else {
const chain = supportedPeanutChains.find((chain) => chain.chainId === selectedChainID)
return {
name: chain?.name,
iconUri: chain?.icon.url,
}
}
}, [supportedSquidChainsAndTokens, selectedChainID])
const selectedToken = useMemo(() => {
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
const token = chain.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return {
symbol: token?.symbol,
iconUri: token?.logoURI,
}
} else {
const token = peanutTokenDetails
.find((tokenDetails) => tokenDetails.chainId === selectedChainID)
?.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return {
symbol: token?.symbol,
iconUri: token?.logoURI,
}
}
}, [selectedChainID, selectedTokenAddress, supportedSquidChainsAndTokens])
const selectedChain = useMemo(() => {
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
return {
name: chain?.axelarChainName ?? 'Unknown Chain',
iconUri: chain?.chainIconURI ?? '',
}
} else {
const chain = supportedPeanutChains.find((chain) => chain.chainId === selectedChainID)
return {
name: chain?.name ?? 'Unknown Chain',
iconUri: chain?.icon?.url ?? '',
}
}
}, [supportedSquidChainsAndTokens, selectedChainID])
const selectedToken = useMemo(() => {
const defaultToken = {
symbol: 'Unknown Token',
iconUri: '',
}
if (supportedSquidChainsAndTokens[selectedChainID]) {
const chain = supportedSquidChainsAndTokens[selectedChainID]
const token = chain.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return token ? {
symbol: token.symbol ?? defaultToken.symbol,
iconUri: token.logoURI ?? defaultToken.iconUri,
} : defaultToken
} else {
const token = peanutTokenDetails
.find((tokenDetails) => tokenDetails.chainId === selectedChainID)
?.tokens.find((token) => areTokenAddressesEqual(token.address, selectedTokenAddress))
return token ? {
symbol: token.symbol ?? defaultToken.symbol,
iconUri: token.logoURI ?? defaultToken.iconUri,
} : defaultToken
}
}, [selectedChainID, selectedTokenAddress, supportedSquidChainsAndTokens])

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 and nitpick comments (2)
src/components/Request/Pay/Views/Initial.view.tsx (2)

129-135: Consider adding error boundary for token support check

The token support check logic is well-implemented with proper memoization and null safety. However, consider adding error handling for malformed token addresses to prevent potential runtime errors.

 const tokenSupportsXChain = useMemo(() => {
+    try {
         return (
             supportedSquidChainsAndTokens[requestLinkData.chainId]?.tokens.some((token) =>
                 areTokenAddressesEqual(token.address, requestLinkData.tokenAddress)
             ) ?? false
         )
+    } catch (error) {
+        console.error('Error checking token support:', error)
+        return false
+    }
 }, [requestLinkData.tokenAddress, requestLinkData.chainId, supportedSquidChainsAndTokens])

377-387: Improve JSX indentation for better readability

The conditional messaging logic is clear and user-friendly, but the JSX indentation could be improved.

-                {tokenSupportsXChain ? (
-                    <label className="text-h9 font-light">
-                        You can fulfill this payment request with any token on any chain. Pick the token and chain that
-                        you want to fulfill this request with.
-                    </label>
-                ) : (
-                    <label className="text-h9 font-light">
-                        This token does not support cross-chain transfers. You can only fulfill this payment request
-                        with the selected token on the selected chain.
-                    </label>
-                )}
+                {tokenSupportsXChain ? (
+                    <label className="text-h9 font-light">
+                        You can fulfill this payment request with any token on any chain.
+                        Pick the token and chain that you want to fulfill this request with.
+                    </label>
+                ) : (
+                    <label className="text-h9 font-light">
+                        This token does not support cross-chain transfers.
+                        You can only fulfill this payment request with the selected token
+                        on the selected chain.
+                    </label>
+                )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1295578 and b6d437c.

📒 Files selected for processing (2)
  • src/components/Global/TokenSelector/Components/AdvancedButton.tsx (5 hunks)
  • src/components/Request/Pay/Views/Initial.view.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Global/TokenSelector/Components/AdvancedButton.tsx
🔇 Additional comments (3)
src/components/Request/Pay/Views/Initial.view.tsx (3)

83-83: LGTM: Context integration looks good

The addition of supportedSquidChainsAndTokens from context aligns with the PR's objective of integrating Squid-supported tokens.


283-284: LGTM: Network switching logic is well-implemented

The chain validation and switching logic is correctly implemented with proper error handling through the parent function's try-catch block.


389-389: LGTM: TokenSelector integration is well-implemented

The conditional rendering of TokenSelector with the new showOnlySquidSupported prop aligns perfectly with the PR's objective of utilizing Squid-supported tokens.

Copy link
Contributor

@Hugo0 Hugo0 left a comment

Choose a reason for hiding this comment

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

huge ux win

@Hugo0 Hugo0 merged commit 2fc06ec into develop Nov 22, 2024
3 checks passed
This was referenced Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants