Skip to content

Conversation

jjramirezn
Copy link
Contributor

This PR does the following

  • Adds a max button to the claim and cashout flows where the user can select their balance for that token as input
  • Adds check of value+gas before trying transaction both in claim and cashout for native tokens
  • In claim suggest a new value for a native token that didn't pass previous check (in cashout is needed a bigger refactor for being able to do that, still with the previous check users should not be able to try to send more than they have)

Copy link

vercel bot commented Oct 29, 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 Oct 29, 2024 4:20pm

Copy link

Copy link
Contributor

coderabbitai bot commented Oct 29, 2024

Caution

Review failed

The pull request is closed.

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces significant enhancements across several components related to cashout and link creation functionalities. Key updates include the addition of a balanceByToken feature to manage token-specific balances, improved error handling, and the introduction of a maxValue property for better user interface interactions. The TokenAmountInput component has been modified to accommodate the new maxValue functionality, while various hooks and utility functions have been optimized for performance and clarity. Overall, these changes aim to refine user experience and functionality in managing token transactions.

Changes

File Path Change Summary
src/components/Cashout/Components/Initial.view.tsx Added selectedTokenAddress to context, updated useBalance to include balanceByToken, introduced maxValue, and refined error handling in handleOnNext.
src/components/Create/Link/Input.view.tsx Updated imports, added balanceByToken, computed maxValue using useMemo, and improved error handling for gasless deposits.
src/components/Create/useCreateLink.tsx Refactored functions to use useCallback, optimized balance checks, and enhanced error handling.
src/components/Global/TokenAmountInput/index.tsx Added optional maxValue property, implemented handleMaxClick for setting max value, and modified rendering logic for the "Max" button.
src/hooks/useBalance.tsx Added balanceByToken method, updated return values to include it, and streamlined balance fetching logic.
src/utils/general.utils.ts Introduced floorFixed function for formatting numbers to fixed decimals, with minor adjustments for clarity.

Possibly related PRs


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

🧹 Outside diff range and nitpick comments (8)
src/components/Global/TokenAmountInput/index.tsx (1)

96-103: Enhance max button accessibility

While the max button implementation is good, consider these accessibility improvements:

  • Add aria-label to better describe the action
  • Add tooltip to explain what max value represents
 {maxValue && maxValue !== tokenValue && (
     <button
         onClick={handleMaxClick}
         className="absolute right-1 ml-1 border border-n-1 px-3 py-2 text-h7 uppercase hover:bg-n-3/10"
+        aria-label="Set maximum token amount"
+        title={`Set maximum amount: ${maxValue}`}
     >
         Max
     </button>
 )}
src/hooks/useBalance.tsx (2)

167-175: Add TypeScript types and documentation.

The new balanceByToken function is well-implemented with proper memoization and null checks. Consider these improvements:

+/**
+ * Finds a balance entry for a specific token on a specific chain.
+ * @param chainId - The chain ID to look for
+ * @param tokenAddress - The token address to look for
+ * @returns The balance entry if found, undefined otherwise
+ */
 const balanceByToken = useCallback(
-    (chainId: string, tokenAddress: string) => {
+    (chainId: interfaces.ChainId, tokenAddress: interfaces.TokenAddress) => {

177-177: Consider adding return type annotation.

The return value correctly includes the new balanceByToken function. Consider adding an explicit return type for better type safety:

-return { balances, fetchBalances, valuePerChain, refetchBalances, hasFetchedBalances, balanceByToken }
+return {
+  balances,
+  fetchBalances,
+  valuePerChain,
+  refetchBalances,
+  hasFetchedBalances,
+  balanceByToken
+} as const satisfies interfaces.UseBalanceReturn
src/components/Cashout/Components/Initial.view.tsx (2)

194-198: Consider token-specific decimal precision

The maxValue calculation is implemented correctly, but consider the following improvements:

  1. The fixed precision of 6 decimal places might not be suitable for all tokens, as some tokens have different decimal places (e.g., USDC has 6, while most ERC20 tokens have 18).
  2. Consider adding validation for negative balances, although this should be rare.

Consider updating the implementation to handle token-specific decimals:

 const maxValue = useMemo(() => {
     const balance = balanceByToken(selectedChainID, selectedTokenAddress)
     if (!balance) return ''
-    return floorFixed(balance.amount, 6)
+    const tokenDecimals = balance.decimals ?? 6 // Fallback to 6 if decimals not provided
+    return balance.amount > 0 ? floorFixed(balance.amount, tokenDecimals) : '0'
 }, [selectedChainID, selectedTokenAddress, balanceByToken])

Line range hint 102-193: Consider improving error handling and component organization

The handleOnNext function has grown quite complex and handles multiple responsibilities:

  1. Bank account validation
  2. User fetching and type determination
  3. Link preparation
  4. KYC status management

Consider breaking this down into smaller, more focused functions or custom hooks for better maintainability and testing.

Consider:

  1. Extracting user management logic into a custom hook (e.g., useUserManagement)
  2. Creating a separate service for bank account operations
  3. Implementing proper error boundaries for better error handling

Example structure:

// hooks/useUserManagement.ts
const useUserManagement = () => {
  // Extract user fetching and type determination logic here
}

// services/bankAccountService.ts
const validateAndPrepareBankAccount = async (bankAccountNumber: string) => {
  // Extract bank account validation logic here
}

// hooks/useLinkPreparation.ts
const useLinkPreparation = () => {
  // Extract link preparation logic here
}
src/utils/general.utils.ts (1)

238-240: Add input validation and JSDoc documentation.

The function implementation is correct for handling token amounts. However, consider these improvements:

  1. Add input validation:
 export function floorFixed(value: number, decimals: number) {
+    if (isNaN(value) || !isFinite(value)) return '0'
+    if (decimals < 0) decimals = 0
     return (Math.floor(value * 10 ** decimals) / 10 ** decimals).toFixed(decimals)
 }
  1. Add JSDoc documentation:
/**
 * Formats a number to a fixed number of decimal places using floor rounding.
 * @param value - The number to format
 * @param decimals - Number of decimal places (non-negative)
 * @returns The formatted number as a string with exact decimal places
 * @example
 * floorFixed(1.23456, 2) // returns "1.23"
 * floorFixed(1.23456, 0) // returns "1"
 */
src/components/Create/Link/Input.view.tsx (1)

171-173: Improve the clarity of the error message

The error message could be rephrased for better readability.

Apply this diff to update the error message:

     setErrorState({
         showError: true,
         errorMessage:
-            'You do not have enough balance to cover the transaction fees, try again with suggested amount',
+            'You do not have enough balance to cover the transaction fees. Please try again with the suggested amount.',
     })
src/components/Create/useCreateLink.tsx (1)

Line range hint 193-237: Improve error handling in the estimateGasFee function.

The function currently throws an error if it fails to fetch the token price. However, it would be better to handle this error gracefully and provide a fallback value or a default gas fee estimate.

Here's a suggested fix:

-if (!nativeTokenPrice || typeof nativeTokenPrice.price !== 'number' || isNaN(nativeTokenPrice.price)) {
-    throw new Error('Failed to fetch token price')
-}
-const transactionCostUSD = Number(transactionCostNative) * nativeTokenPrice.price
+let transactionCostUSD = 0
+if (nativeTokenPrice && typeof nativeTokenPrice.price === 'number' && !isNaN(nativeTokenPrice.price)) {
+    transactionCostUSD = Number(transactionCostNative) * nativeTokenPrice.price
+} else {
+    console.warn('Failed to fetch token price. Using default transaction cost.')
+    transactionCostUSD = Number(transactionCostNative) * 0.0001 // Default to a small value
+}
🧰 Tools
🪛 Biome

[error] 203-203: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4c626b4 and 23dc13b.

📒 Files selected for processing (6)
  • src/components/Cashout/Components/Initial.view.tsx (4 hunks)
  • src/components/Create/Link/Input.view.tsx (8 hunks)
  • src/components/Create/useCreateLink.tsx (13 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (4 hunks)
  • src/hooks/useBalance.tsx (3 hunks)
  • src/utils/general.utils.ts (1 hunks)
🧰 Additional context used
🪛 Biome
src/components/Create/useCreateLink.tsx

[error] 653-653: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

🔇 Additional comments (13)
src/components/Global/TokenAmountInput/index.tsx (3)

11-11: LGTM: Appropriate prop type for maxValue

The optional string type for maxValue is appropriate for handling token amounts without floating-point precision issues.


14-14: LGTM: Clean prop integration

The maxValue prop is correctly integrated into the component signature.


14-14: Verify gas fee handling for native tokens

Per PR objectives, the system should check combined value of transaction and gas fees for native tokens. Ensure that the maxValue being passed to this component already accounts for gas fees where appropriate.

✅ Verification successful

Let me search for more context about how maxValue is calculated and used.


Based on the search results, I can now generate a final response as I have found evidence of gas fee handling in the codebase.

Gas fees are properly handled for native tokens

The codebase already accounts for gas fees when using native tokens. Specifically in src/components/Create/Link/Input.view.tsx, before setting the maxValue:

  • Gas fees are estimated using estimateGasFee
  • For native tokens, the code checks if users have enough balance to cover both the transaction amount and gas fees
  • The check is done by adding the gas amount to the token value: tokenValue: String(Number(tokenValue) + maxGasAmount)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for maxValue calculations in parent components
rg -A 5 "maxValue.*TokenAmountInput" 

Length of output: 751


Script:

#!/bin/bash
# Search for maxValue calculations and gas fee handling
rg -A 10 "maxValue.*=|const.*maxValue" 

# Search for gas fee related calculations
rg -A 5 "gasFee|gasLimit|estimateGas"

# Search for TokenAmountInput usage to see where maxValue is passed
ast-grep --pattern 'TokenAmountInput maxValue={$_}'

Length of output: 13728

src/hooks/useBalance.tsx (3)

3-4: LGTM! Clean import additions.

The new imports support the added functionality while promoting code reuse through utility functions.


126-127: LGTM! Improved code clarity.

Good refactor to use the dedicated utility function while maintaining the same sorting behavior.


167-177: Verify integration with consuming components.

Since this hook provides critical functionality for the max button feature, let's verify its integration:

✅ Verification successful

No issues found with balanceByToken integration

The verification shows that balanceByToken is properly integrated in both components that use it:

  1. In Create/Link/Input.view.tsx, it's used within a useMemo hook to calculate the max value for the input, with proper null checks and formatting:
const maxValue = useMemo(() => {
    const balance = balanceByToken(selectedChainID, selectedTokenAddress)
    if (!balance) return ''
    return floorFixed(balance.amount, 6)
}, [selectedChainID, selectedTokenAddress, balanceByToken])
  1. Similarly in Cashout/Components/Initial.view.tsx, it follows the same pattern with proper null handling:
const maxValue = useMemo(() => {
    const balance = balanceByToken(selectedChainID, selectedTokenAddress)
    if (!balance) return ''
    return floorFixed(balance.amount, 6)
}, [selectedChainID, selectedTokenAddress, balanceByToken])

Both implementations properly handle undefined returns and have appropriate dependency arrays in their hooks. The integration is consistent with the hook's design and includes proper error handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the usage of balanceByToken in components and error handling

# Test 1: Find components using balanceByToken
echo "Components using balanceByToken:"
rg "balanceByToken\s*=" src/

# Test 2: Check error handling in those components
echo "\nError handling in components:"
rg "balanceByToken.*\s*(try|catch|if.*undefined|if.*null)" src/

Length of output: 309


Script:

#!/bin/bash
# Let's try a broader search to find usages of the hook and its returned values

# Find files importing or using useBalance
echo "Files importing useBalance:"
rg "useBalance" src/

# Find direct usage of the hook
echo -e "\nDirect hook usage:"
rg "const.*=.*useBalance\(" src/

# Find any usage of balanceByToken method
echo -e "\nUsage of balanceByToken method:"
rg "balanceByToken\(" src/

# Look for max button related components
echo -e "\nMax button related components:"
rg -i "max.*button|maxbutton" src/

Length of output: 4895


Script:

#!/bin/bash
# Let's examine the specific components that use balanceByToken to verify error handling

echo "Content of Create/Link/Input.view.tsx:"
cat src/components/Create/Link/Input.view.tsx

echo -e "\nContent of Cashout/Components/Initial.view.tsx:"
cat src/components/Cashout/Components/Initial.view.tsx

Length of output: 33484

src/components/Cashout/Components/Initial.view.tsx (3)

35-37: LGTM: Context extraction is properly updated

The addition of selectedTokenAddress to the context extraction is well-integrated and necessary for the max button functionality.


39-39: LGTM: Balance hook usage is properly updated

The addition of balanceByToken is consistent with the requirements and is properly utilized for max value calculations.


230-230: LGTM: TokenAmountInput properly configured with maxValue

The maxValue prop is correctly passed to TokenAmountInput, enabling the max button functionality as intended.

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

238-240: LGTM! The implementation is correct for token amount formatting.

The function correctly handles the formatting of token amounts by:

  1. Using Math.floor to round down, which is appropriate for financial calculations
  2. Avoiding floating-point precision issues by using integer math
  3. Ensuring exact decimal places in the output string
src/components/Create/Link/Input.view.tsx (1)

293-293: Verify TokenAmountInput handles maxValue correctly

By adding maxValue to TokenAmountInput, ensure that the component properly handles cases where maxValue might be '0' or another edge case.

Consider adding validations within TokenAmountInput to handle these scenarios and prevent potential errors.

src/components/Create/useCreateLink.tsx (2)

71-111: LGTM!

The generateLinkDetails function looks good. It correctly retrieves the token details, generates the base URL, and returns the link details object.


653-653: ⚠️ Potential issue

Remove the unnecessary catch clause.

The catch clause that only rethrows the original error is useless and can be confusing.

-try {
-    await checkUserHasEnoughBalance({ tokenValue })
-    const linkDetails = generateLinkDetails({ tokenValue, walletType, envInfo: environmentInfo })
-    const password = await generatePassword()
-    await switchNetwork(selectedChainID)
-
-    const _isGaslessDepositPossible = isGaslessDepositPossible({
-        chainId: selectedChainID,
-        tokenAddress: selectedTokenAddress,
-    })
-
-    if (_isGaslessDepositPossible) {
-        return prepareGaslessDeposit(linkDetails, password)
-    } else {
-        return prepareStandardDeposit(linkDetails, password, tokenValue)
-    }
-} catch (error) {
-    throw error
-}
+await checkUserHasEnoughBalance({ tokenValue })
+const linkDetails = generateLinkDetails({ tokenValue, walletType, envInfo: environmentInfo })
+const password = await generatePassword()
+await switchNetwork(selectedChainID)
+
+const _isGaslessDepositPossible = isGaslessDepositPossible({
+    chainId: selectedChainID,
+    tokenAddress: selectedTokenAddress,
+})
+
+if (_isGaslessDepositPossible) {
+    return prepareGaslessDeposit(linkDetails, password)
+} else {
+    return prepareStandardDeposit(linkDetails, password, tokenValue)
+}
🧰 Tools
🪛 Biome

[error] 653-653: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

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

🧹 Outside diff range and nitpick comments (5)
src/hooks/useBalance.tsx (1)

126-127: Consider adding a comment to explain the sorting priority.

The sorting logic prioritizes native tokens (zero address) over other tokens when values are equal. While the implementation is correct, a brief comment would help explain this business logic.

 if (valueA === valueB) {
+    // Prioritize native tokens (zero address) when values are equal
     if (isAddressZero(a.address)) return -1
     if (isAddressZero(b.address)) return 1
src/components/Create/Link/Input.view.tsx (1)

159-176: Improve gas fee handling readability and user experience.

While the number conversions are fine as per previous feedback, consider these improvements:

  1. Document the 1.3 multiplier (30% buffer) used in the calculation
  2. Make the error message more user-friendly

Apply this diff:

 if (undefined !== _feeOptions && isNativeCurrency(selectedTokenAddress)) {
+    // Calculate maximum gas cost with current gas prices
     const maxGasAmount = Number(
         formatEther(_feeOptions.gasLimit.mul(_feeOptions.maxFeePerGas || _feeOptions.gasPrice))
     )
     try {
         await checkUserHasEnoughBalance({
             tokenValue: String(Number(tokenValue) + maxGasAmount),
         })
     } catch (error) {
+        // Reduce token value by gas amount plus 30% buffer for price fluctuations
         _setTokenValue((Number(tokenValue) - maxGasAmount * 1.3).toFixed(6))
         setErrorState({
             showError: true,
             errorMessage:
-                'You do not have enough balance to cover the transaction fees, try again with suggested amount',
+                'Insufficient balance for transaction fees. A new amount has been suggested that includes gas fees.',
         })
         return
     }
 }
src/components/Create/useCreateLink.tsx (2)

Line range hint 188-232: Reduce code duplication in gas fee estimation.

The error handling block duplicates most of the code from the try block. Consider extracting the common logic into a separate function.

+const calculateGasFee = async (
+    chainId: string,
+    preparedTx: any,
+    gasLimit?: BigNumber
+) => {
+    const feeOptions = await peanut.setFeeOptions({
+        chainId: chainId,
+        unsignedTx: preparedTx,
+        gasLimit,
+    })
+
+    let transactionCostWei = feeOptions.gasLimit.mul(
+        feeOptions.maxFeePerGas || feeOptions.gasPrice
+    )
+    let transactionCostNative = formatEther(transactionCostWei)
+    const nativeTokenPrice = await fetchTokenPrice(
+        '0x0000000000000000000000000000000000000000',
+        chainId
+    )
+    if (!nativeTokenPrice || typeof nativeTokenPrice.price !== 'number' || isNaN(nativeTokenPrice.price)) {
+        throw new Error('Failed to fetch token price')
+    }
+    const transactionCostUSD = Number(transactionCostNative) * nativeTokenPrice.price
+
+    return {
+        feeOptions,
+        transactionCostUSD,
+    }
+}
+
 const estimateGasFee = useCallback(
     async ({ chainId, preparedTx }: { chainId: string; preparedTx: any }) => {
         try {
-            const feeOptions = await peanut.setFeeOptions({
-                chainId: chainId,
-                unsignedTx: preparedTx,
-            })
-
-            let transactionCostWei = feeOptions.gasLimit.mul(feeOptions.maxFeePerGas || feeOptions.gasPrice)
-            let transactionCostNative = formatEther(transactionCostWei)
-            const nativeTokenPrice = await fetchTokenPrice('0x0000000000000000000000000000000000000000', chainId)
-            if (!nativeTokenPrice || typeof nativeTokenPrice.price !== 'number' || isNaN(nativeTokenPrice.price)) {
-                throw new Error('Failed to fetch token price')
-            }
-            const transactionCostUSD = Number(transactionCostNative) * nativeTokenPrice.price
-
-            return {
-                feeOptions,
-                transactionCostUSD,
-            }
+            return await calculateGasFee(chainId, preparedTx)
         } catch (error) {
             try {
-                const feeOptions = await peanut.setFeeOptions({
-                    chainId: chainId,
-                    unsignedTx: preparedTx,
-                    gasLimit: BigNumber.from(100000),
-                })
-
-                let transactionCostWei = feeOptions.gasLimit.mul(feeOptions.maxFeePerGas || feeOptions.gasPrice)
-                let transactionCostNative = formatEther(transactionCostWei)
-                const nativeTokenPrice = await fetchTokenPrice('0x0000000000000000000000000000000000000000', chainId)
-                if (!nativeTokenPrice) {
-                    throw new Error('Failed to fetch token price')
-                }
-                const transactionCostUSD = Number(transactionCostNative) * nativeTokenPrice.price
-
-                return {
-                    feeOptions,
-                    transactionCostUSD,
-                }
+                return await calculateGasFee(chainId, preparedTx, BigNumber.from(100000))
             } catch (error) {
                 console.error('Failed to estimate gas fee:', error)
                 throw error
             }
         }
     },
     []
 )
🧰 Tools
🪛 Biome

[error] 198-198: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


Line range hint 419-430: Review native token value precision handling.

For native token transfers, the value is being fixed to 7 decimal places which might cause precision loss. Consider using the full precision of the input value.

-tokenValue = Number(tokenValue).toFixed(7)
-const amount = parseEther(tokenValue)
+const amount = parseEther(tokenValue)
src/utils/general.utils.ts (1)

238-240: The implementation looks correct but could be more robust.

The function correctly floors numbers to specified decimal places, which is crucial for token amount calculations. However, consider adding input validation and edge case handling.

Consider this enhanced implementation:

 export function floorFixed(value: number, decimals: number) {
+    if (!Number.isFinite(value)) return '0';
+    if (!Number.isInteger(decimals) || decimals < 0) {
+        throw new Error('Decimals must be a non-negative integer');
+    }
     return (Math.floor(value * 10 ** decimals) / 10 ** decimals).toFixed(decimals);
 }

This version:

  • Handles NaN and Infinity
  • Validates that decimals is a non-negative integer
  • Maintains the same precision-handling logic
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 23dc13b and c62d6d1.

📒 Files selected for processing (6)
  • src/components/Cashout/Components/Initial.view.tsx (4 hunks)
  • src/components/Create/Link/Input.view.tsx (8 hunks)
  • src/components/Create/useCreateLink.tsx (12 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (4 hunks)
  • src/hooks/useBalance.tsx (3 hunks)
  • src/utils/general.utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Global/TokenAmountInput/index.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Create/Link/Input.view.tsx (2)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:159-176
Timestamp: 2024-10-29T12:23:32.778Z
Learning: Number conversions are acceptable as they are in the code and do not require changes.
🪛 Biome
src/components/Create/useCreateLink.tsx

[error] 648-648: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

🔇 Additional comments (10)
src/hooks/useBalance.tsx (3)

3-4: LGTM! Import statements are properly organized.

The new imports for useCallback and utility functions are correctly added and align with their usage in the code.


177-177: LGTM! Return value properly includes the new balanceByToken method.

The addition of balanceByToken to the returned object aligns with the PR objectives and maintains good code organization.


167-177: Verify the usage patterns of balanceByToken in consuming components.

Let's ensure the new balanceByToken method is properly integrated with the max button functionality mentioned in the PR objectives.

✅ Verification successful

balanceByToken is correctly integrated with max button functionality

The verification shows proper integration:

  • Used in both Create/Link and Cashout components to calculate maxValue
  • Correctly used with TokenAmountInput component's maxValue prop
  • Implementation properly considers chain ID and token address
  • Used in conjunction with useCallback and useMemo for performance
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components using balanceByToken
echo "Searching for balanceByToken usage patterns..."
rg "balanceByToken" --type tsx -B 2 -A 2

# Search for max button implementations that should use this function
echo "Searching for max button implementations..."
rg "max.*button|maxValue|max.*amount" --type tsx -B 2 -A 2

Length of output: 357


Script:

#!/bin/bash
# Search for balanceByToken usage without file type restriction
echo "Searching for balanceByToken usage patterns..."
rg "balanceByToken" -B 2 -A 2

# Search for max button implementations
echo "Searching for max button implementations..."
rg "max.*button|maxValue|max.*amount" -B 2 -A 2

Length of output: 8421

src/components/Create/Link/Input.view.tsx (2)

244-248: LGTM! Well-implemented balance handling.

The maxValue calculation is correctly memoized and follows the established pattern of returning an empty string for undefined balances.


275-277: LGTM! Clean UI implementation.

The recipient display logic properly handles both ENS names and addresses, and the maxValue integration with TokenAmountInput is clean.

Also applies to: 293-293

src/components/Cashout/Components/Initial.view.tsx (3)

35-39: LGTM: Context and hook changes are well-integrated

The addition of selectedTokenAddress and balanceByToken properly supports the new max button functionality while maintaining clean integration with existing code.


Line range hint 230-236: LGTM: Clean integration of maxValue with TokenAmountInput

The maxValue prop is properly integrated with the TokenAmountInput component while maintaining existing wallet connection and submission functionality.


230-230: Verify TokenAmountInput implementation

Let's verify that the TokenAmountInput component properly implements the max button functionality.

✅ Verification successful

Let me check the actual implementation of the max button functionality in the TokenAmountInput component.


TokenAmountInput implementation is correctly handling max value functionality

Based on the code inspection and the learning from jjramirezn, the TokenAmountInput component correctly implements the max button functionality:

  • It properly accepts the maxValue prop
  • The handleMaxClick function sets the input denomination to 'TOKEN' (which is intentional as per the learning)
  • The max button is only shown when maxValue exists and differs from the current tokenValue
  • When clicked, it correctly sets the token value to the maximum amount
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check TokenAmountInput implementation for max button functionality

# Search for TokenAmountInput component definition
echo "Searching for TokenAmountInput component implementation..."
rg -l "TokenAmountInput" src/components/Global

# Check maxValue prop handling
echo "Checking maxValue prop handling..."
ast-grep --pattern 'interface $_ {
  $$$
  maxValue?: $_
  $$$
}'

Length of output: 931


Script:

#!/bin/bash
# Check the implementation of TokenAmountInput component
echo "Checking TokenAmountInput implementation..."
rg -A 20 "const TokenAmountInput.*=.*{" src/components/Global/TokenAmountInput/index.tsx

# Look for max button handling
echo "Checking max button handling..."
rg -A 10 "maxValue" src/components/Global/TokenAmountInput/index.tsx

Length of output: 1969

src/components/Create/useCreateLink.tsx (2)

705-705: LGTM!

The token price handling aligns with the PR objectives and is implemented correctly.


89-97: Review token amount precision handling.

The token amount is being fixed to 6 decimal places which might not be appropriate for all tokens. Consider using the token's decimal places from tokenDetails.tokenDecimals to determine the appropriate precision.

const maxValue = useMemo(() => {
const balance = balanceByToken(selectedChainID, selectedTokenAddress)
if (!balance) return ''
return floorFixed(balance.amount, 6)
Copy link
Contributor

Choose a reason for hiding this comment

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

Agree with coderabbit here, why 6 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added comments

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

🧹 Outside diff range and nitpick comments (3)
src/hooks/useBalance.tsx (1)

126-127: Consider documenting the sorting priority.

The sorting logic correctly prioritizes native tokens, which aligns with the PR's objective of improving token handling.

Add a comment explaining the sorting priority:

 if (valueA === valueB) {
+    // Prioritize native tokens (zero address) in the list
     if (isAddressZero(a.address)) return -1
     if (isAddressZero(b.address)) return 1
src/components/Create/Link/Input.view.tsx (1)

277-277: Consider improving readability of recipient display logic

While the logic is correct, the nested ternary could be made more readable.

Consider this more readable alternative:

-? `Send to ${recipient.name?.endsWith('.eth') ? recipient.name : shortenAddressLong(recipient.address ?? '')}`
+? `Send to ${getDisplayName(recipient)}`

// Add this helper function:
const getDisplayName = (recipient: { name?: string, address?: string }) => {
  if (recipient.name?.endsWith('.eth')) return recipient.name;
  return shortenAddressLong(recipient.address ?? '');
};
src/components/Create/useCreateLink.tsx (1)

Line range hint 419-430: Consider precision handling for native token amounts.

While the ERC20 token parsing is correct, the native token amount is fixed to 7 decimal places which might lead to precision loss for some values.

Consider removing the fixed decimal places or increasing the precision:

-tokenValue = Number(tokenValue).toFixed(7)
+tokenValue = Number(tokenValue).toString()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c62d6d1 and 976d3a0.

📒 Files selected for processing (5)
  • src/components/Cashout/Components/Initial.view.tsx (4 hunks)
  • src/components/Create/Link/Input.view.tsx (8 hunks)
  • src/components/Create/useCreateLink.tsx (13 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (4 hunks)
  • src/hooks/useBalance.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Global/TokenAmountInput/index.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Create/Link/Input.view.tsx (1)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
🔇 Additional comments (15)
src/hooks/useBalance.tsx (3)

2-4: LGTM! Clean import organization.

The imports are well-organized, using direct imports instead of namespace imports, which is a TypeScript best practice.


11-13: LGTM! Consistent type usage.

The state declarations correctly use the directly imported types, maintaining consistency with the import changes.


177-177: Verify the impact of the new return value.

The addition of balanceByToken to the return object is correct, but we should verify its usage in consuming components.

Let's verify the usage of this hook:

✅ Verification successful

Let me search for the actual usage of balanceByToken to verify its impact.


The addition of balanceByToken is properly integrated and used

The new balanceByToken function is being correctly used in three components:

  • src/components/Create/useCreateLink.tsx: For checking token balance during link creation
  • src/components/Create/Link/Input.view.tsx: For displaying token balance in the input view
  • src/components/Cashout/Components/Initial.view.tsx: For showing balance in the cashout flow

All usages follow a consistent pattern of retrieving and displaying token balances for specific chain and token combinations, replacing what would otherwise require manual filtering of the balances array.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for useBalance hook usage
rg "useBalance\(\)" -A 5

Length of output: 6410


Script:

#!/bin/bash
# Search for balanceByToken usage
rg "balanceByToken" -A 3

Length of output: 3728

src/components/Create/Link/Input.view.tsx (6)

7-7: LGTM: Import optimizations

The imports have been refined to include only the necessary functions, improving code maintainability.

Also applies to: 11-11, 13-13, 21-21


60-60: LGTM: Balance management enhancement

The addition of balanceByToken enables precise token balance retrieval, supporting the max value feature.


107-111: LGTM: Improved gasless deposit check

The gasless deposit check has been refactored for better readability and reusability.


184-187: LGTM: Points estimation logic

The points estimation logic correctly handles both gasless and regular transactions.

🧰 Tools
🪛 Biome

[error] 186-187: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


245-250: LGTM: Efficient max value calculation

The max value calculation is properly memoized and follows the established pattern of returning an empty string for undefined balances, as confirmed by previous learnings.


295-295: LGTM: TokenAmountInput enhancement

The maxValue prop has been correctly added to the TokenAmountInput component.

src/components/Cashout/Components/Initial.view.tsx (4)

35-37: LGTM: Context extraction is properly updated

The addition of selectedTokenAddress to the context extraction is consistent with the requirements for implementing the max button functionality.


39-39: LGTM: Balance hook usage is properly updated

The addition of balanceByToken from the useBalance hook provides the necessary functionality to fetch token-specific balances.


194-199: LGTM: maxValue calculation is properly implemented

The implementation correctly calculates the maximum value based on the selected token's balance and is properly memoized for performance optimization.


231-231: LGTM: maxValue prop is properly passed to TokenAmountInput

The maxValue prop is correctly passed to the TokenAmountInput component, enabling the max button functionality as required.

src/components/Create/useCreateLink.tsx (2)

33-64: LGTM! Balance checking implementation is robust.

The balance checking logic has been well-implemented with:

  • Clear validation of chain and token address
  • Proper balance retrieval using balanceByToken
  • Appropriate minimum amount validation

628-642: LGTM! Comprehensive native token balance validation.

The implementation correctly:

  • Checks if the token is native currency
  • Calculates the maximum gas amount
  • Validates the total amount (token value + gas fees)

@jjramirezn jjramirezn merged commit d63740d into develop Oct 29, 2024
2 checks passed
@jjramirezn jjramirezn deleted the fix/limit-with-gas-native-token branch October 29, 2024 16:20
@coderabbitai coderabbitai bot mentioned this pull request Oct 31, 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.

3 participants