Skip to content

Cashout Improvements#545

Merged
Hugo0 merged 7 commits intomainfrom
fix/cashout_fixes6
Nov 28, 2024
Merged

Cashout Improvements#545
Hugo0 merged 7 commits intomainfrom
fix/cashout_fixes6

Conversation

@Hugo0
Copy link
Copy Markdown
Contributor

@Hugo0 Hugo0 commented Nov 25, 2024

Bank Account Input and Validation Improvements

Core Changes

  • Standardized bank account input handling across all components
  • Improved US bank account validation and formatting
  • Added proper handling of existing account addresses for KYC'd users
  • Reduced minimum cashout amount to $1 in development environment
  • added test suite and pnpm commands

@vercel
Copy link
Copy Markdown

vercel bot commented Nov 25, 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 28, 2024 10:00am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 25, 2024

Caution

Review failed

The pull request is closed.

📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces multiple enhancements across various components related to bank account handling, input validation, and user interface improvements. Key updates include the addition of utility functions for sanitizing and formatting bank account information, modifications to error handling and recipient type assignment, and the introduction of a new component for U.S. bank account inputs. Additionally, testing capabilities have been established with new scripts and configuration for Jest, alongside updates to environment variables for configuration management.

Changes

File Change Summary
src/components/Cashout/Components/Initial.view.tsx Added functions for sanitizing and formatting bank account information; updated error handling for invalid accounts.
src/components/Claim/Link/Initial.view.tsx Modified recipient type logic based on address input; refined error handling conditions.
src/components/Global/GeneralRecipientInput/index.tsx Introduced sanitization for recipient input; updated validation regex for US bank accounts; added display formatting function.
src/components/Global/LinkAccountComponent/index.tsx Simplified step labels; enhanced state management for loading; added US account validation logic and improved error handling.
src/components/Global/USBankAccountInput/index.tsx New component for handling U.S. bank account input with validation and sanitization.
src/components/Global/ValidatedInput/index.tsx Updated to include sanitization and optional display formatting for input values.
src/components/LandingPage/BuildOnUs/index.tsx Updated the href attribute of an anchor element to a new URL.
src/components/Offramp/Confirm.view.tsx Added formatting for bank account display; refined total amount calculation and error handling.
src/utils/format.utils.ts Introduced sanitizeBankAccount and formatBankAccountDisplay utility functions for handling bank account data.
.env.example Updated environment variables with new keys and values; modified existing variable declarations.
.github/workflows/tests.yml New GitHub Actions workflow for automated testing processes.
README.md Added a section on testing framework and procedures.
jest.setup.ts New file for Jest global test setup, including console method overrides.
package.json Added testing scripts and dependencies for Jest; included Jest configuration.
src/app/api/bridge/external-account/create-external-account/route.ts Improved error handling and request data processing; added duplicate account handling logic.
src/components/Offramp/Offramp.consts.ts Updated cashout limit constant based on the environment.
src/components/Offramp/Success.view.tsx Adjusted layout and rendering logic for displaying amounts received.
src/components/Profile/index.tsx Restructured referral points display logic; improved readability.
src/interfaces/interfaces.ts Updated TypeScript interfaces to include new properties and restructure existing ones.
src/utils/__tests__/format.utils.test.ts New test file for unit testing sanitizeBankAccount and formatBankAccountDisplay functions.

Possibly related PRs

Suggested reviewers

  • jjramirezn

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
Copy Markdown
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: 21

🧹 Outside diff range and nitpick comments (13)
src/utils/format.utils.ts (1)

1-14: Consider implementing a comprehensive bank account handling strategy

Given that this code handles sensitive financial data, consider these architectural improvements:

  1. Create proper TypeScript interfaces for different bank account types
  2. Implement a centralized validation module
  3. Add proper error handling and logging (without exposing sensitive data)
  4. Consider using a dedicated financial data handling library

Example structure:

interface BankAccount {
  type: 'US' | 'IBAN';
  value: string;
  routingNumber?: string; // for US accounts
}

interface ValidationResult {
  isValid: boolean;
  errors: string[];
}
src/interfaces/index.ts (1)

1-38: Reconsider merging core type definitions in a temporary commit

The PR title suggests this is a temporary commit, but this file contains critical type definitions that will be used throughout the application. Consider:

  1. Splitting this into a separate, complete PR
  2. Removing the "temp commit" status
  3. Adding comprehensive documentation
  4. Getting team consensus on the type structure

These interfaces form a crucial part of the type system and should be well-thought-out before merging.

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

107-114: Consider memoizing the format function

Since formatDisplayValue is used during rendering and depends only on the recipient type, consider memoizing it with useCallback to prevent unnecessary re-renders.

-    const formatDisplayValue = (value: string) => {
+    const formatDisplayValue = useCallback((value: string) => {
         if (recipientType.current === 'iban') {
             return formatIBANDisplay(value)
         } else if (recipientType.current === 'us') {
             return formatUSAccountDisplay(value)
         }
         return value
-    }
+    }, [])

Line range hint 1-132: Address temporary status of changes

The PR title indicates this is a "temp commit, not fully done". Consider:

  1. Completing the US bank account validation
  2. Adding comprehensive test coverage
  3. Documenting the expected format for bank accounts
  4. Adding proper error messages for all validation cases

Please update the PR title and description once the implementation is complete.

Would you like help with any of these improvements?

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

107-107: Consider optimizing value formatting.

While the formatting logic is correct, it could benefit from memoization to prevent unnecessary re-renders.

Consider using useMemo:

+const displayValue = useMemo(
+    () => (formatDisplayValue ? formatDisplayValue(value) : value),
+    [formatDisplayValue, value]
+)

 <input
     type="text"
-    value={formatDisplayValue ? formatDisplayValue(value) : value}
+    value={displayValue}
     // ... other props
 />

Line range hint 1-158: Consider splitting responsibilities for better maintainability.

The ValidatedInput component is handling multiple concerns:

  1. Input validation
  2. Value sanitization
  3. Value formatting
  4. UI rendering

Consider splitting this into smaller, more focused components following the Single Responsibility Principle.

Suggested architecture:

  • Create a separate BankAccountInput component that uses ValidatedInput internally
  • Move bank-specific logic (sanitization, formatting) to the new component
  • Keep ValidatedInput as a generic validated input component
src/components/Cashout/Components/Initial.view.tsx (2)

231-236: Consider enhancing bank account formatting based on account type

While the current formatting improves readability, consider differentiating the formatting based on account type (US vs IBAN) for better user experience.

 const formatBankAccountDisplay = (value: string) => {
+    // For US accounts (routing number + account number)
+    if (value.length === 17) { // 9 digits routing + 8 digits account
+        const routing = value.slice(0, 9)
+        const account = value.slice(9)
+        return `${routing.replace(/(.{3})/g, '$1 ').trim()} | ${account.replace(/(.{4})/g, '$1 ').trim()}`
+    }
+    // For IBAN
     return value
         .toUpperCase()
         .replace(/(.{4})/g, '$1 ')
         .trim()

368-376: Enhance error message clarity for bank account validation

While the error message is improved, it could be more specific about the expected format for US bank accounts.

-                                'Invalid bank account. For US bank accounts, enter your routing number and account number separately.',
+                                'Invalid bank account. For US bank accounts, enter your 9-digit routing number followed by your account number (typically 8-17 digits). For IBAN, enter your full IBAN number.',

Also applies to: 387-387

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

461-465: Consider enhancing recipient type handling

The current implementation sets the recipient type to 'address' when the address is empty, which might not handle all edge cases effectively.

Consider this more robust implementation:

-if (!update.recipient.address) {
-    setRecipientType('address')
-} else {
-    setRecipientType(update.type)
-}
+const newType = !update.recipient.address ? 'address' : 
+    update.type || determineRecipientType(update.recipient.address);
+setRecipientType(newType);

This would:

  1. Maintain the current logic
  2. Add a fallback to determine type from address format
  3. Make the code more maintainable
src/components/Offramp/Confirm.view.tsx (1)

Line range hint 1-24: Consider splitting into smaller, focused components

The OfframpConfirmView component has grown quite large and handles multiple responsibilities. This makes it harder to maintain and test.

Consider breaking it down into smaller, focused components:

  1. KYCFlow - Handle KYC verification steps
  2. BankAccountConfirmation - Display and handle bank account details
  3. TransactionConfirmation - Handle transaction submission and error states

This would:

  • Improve code organization and maintainability
  • Make testing easier
  • Allow for better reuse of common functionality
src/components/Global/LinkAccountComponent/index.tsx (3)

163-163: Remove unnecessary console.log statement

The console.log statement may have been used for debugging purposes and should be removed to clean up the code.

Apply this diff to remove the console.log:

-                console.log('Submitting form data:', formData)

167-167: Remove unnecessary console.log statement

The console.log at this line is likely a leftover from debugging. It's best to remove it to maintain clean production code.

Apply this diff to remove the console.log:

-                    console.log('Missing routing number')

202-202: Remove unnecessary console.log statement

The console.log statement here should be removed to avoid cluttering the console in production.

Apply this diff to remove the console.log:

-                    console.log('Form validation failed')
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2268ac3 and 7e886e5.

📒 Files selected for processing (10)
  • src/components/Cashout/Components/Initial.view.tsx (5 hunks)
  • src/components/Claim/Link/Initial.view.tsx (1 hunks)
  • src/components/Global/GeneralRecipientInput/index.tsx (3 hunks)
  • src/components/Global/LinkAccountComponent/index.tsx (10 hunks)
  • src/components/Global/USBankAccountInput/index.tsx (1 hunks)
  • src/components/Global/ValidatedInput/index.tsx (5 hunks)
  • src/components/LandingPage/BuildOnUs/index.tsx (1 hunks)
  • src/components/Offramp/Confirm.view.tsx (3 hunks)
  • src/interfaces/index.ts (1 hunks)
  • src/utils/format.utils.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/components/Cashout/Components/Initial.view.tsx (1)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.
🔇 Additional comments (13)
src/utils/format.utils.ts (2)

12-14: ⚠️ Potential issue

Enhance US bank account formatting and security

The current implementation is too basic for US bank account formatting needs.

Consider these security and formatting improvements:

+/**
+ * Formats a US bank account number for display
+ * @param value - The raw account number
+ * @returns Formatted and partially masked account number
+ */
 export const formatUSAccountDisplay = (value: string) => {
-    return value.toUpperCase()
+    if (!value) return '';
+    const cleaned = value.replace(/\D/g, '');
+    // Mask all but last 4 digits for security
+    return cleaned.slice(-4).padStart(cleaned.length, '*');
 }

Additionally:

  1. Consider separating routing number formatting into a different function
  2. Add validation for valid US bank account length and format
  3. Consider adding proper ACH formatting guidelines

Let's check for existing bank account validation patterns:


5-10: 🛠️ Refactor suggestion

Add IBAN validation and documentation

The IBAN formatting looks correct but lacks validation and proper documentation.

Consider these improvements:

+/**
+ * Formats an IBAN for display according to the standard format
+ * @param value - The raw IBAN string
+ * @returns Formatted IBAN with spaces every 4 characters
+ * @example
+ * formatIBANDisplay('DE89370400440532013000') // returns 'DE89 3704 0044 0532 0130 00'
+ */
 export const formatIBANDisplay = (value: string) => {
+    if (!value) return '';
+    // Remove existing spaces first to ensure consistent formatting
+    const cleaned = value.replace(/\s/g, '');
     return value
         .toUpperCase()
         .replace(/(.{4})/g, '$1 ')
         .trim()
 }

Consider adding IBAN structure validation using a library like ibantools to ensure valid IBANs.

Let's check if the project already uses any IBAN validation libraries:

src/interfaces/index.ts (1)

21-32: 🛠️ Refactor suggestion

Replace any[] types with proper interfaces and document fields

The interface uses any[] types which reduces type safety. Also, there seems to be redundancy in points tracking.

Consider the following improvements:

+interface Transaction {
+    id: string
+    amount: number
+    timestamp: Date
+    type: string
+    // Add other relevant fields
+}

+interface ReferralPointsTransaction extends Transaction {
+    referralId: string
+    // Add referral-specific fields
+}

 export interface IUserProfile {
+    /** Current point balance */
     points: number
-    transactions: any[]
+    transactions: Transaction[]
-    referralsPointsTxs: any[]
+    referralsPointsTxs: ReferralPointsTransaction[]
+    /** Total number of successful referral connections */
     totalReferralConnections: number
+    /** Number of users referred */
     referredUsers: number
+    /** Current streak count */
     streak: number
     accounts: Account[]
-    contacts: any[]
+    contacts: User[]
-    totalPoints: number // Possibly redundant with points?
+    /** Lifetime total points earned (including spent points) */
+    totalPoints: number
     user: User
 }

Also, please clarify the difference between points and totalPoints. Is one the current balance and the other the lifetime total?

src/components/LandingPage/BuildOnUs/index.tsx (1)

17-17: Verify the new Notion URL is accessible and correct

The integration form URL has been significantly changed. Please ensure:

  1. The new URL is accessible and points to the correct integration form
  2. No information or functionality is lost with the removal of the pvs parameter
src/components/Global/GeneralRecipientInput/index.tsx (1)

9-9: Verify the implementation of bank account formatting utilities

The code now uses utility functions for sanitization and formatting, which is a good practice. However, we should verify their implementation to ensure they handle all edge cases correctly.

Also applies to: 44-45

✅ Verification successful

Bank account formatting utilities are properly implemented

The utility functions are implemented correctly in src/utils/format.utils.ts:

  • sanitizeBankAccount: Removes spaces, hyphens, dots, and underscores, converting to lowercase
  • formatIBANDisplay: Formats IBAN with spaces every 4 characters and converts to uppercase
  • formatUSAccountDisplay: Converts US account numbers to uppercase

These implementations follow standard formatting practices for bank accounts and provide consistent handling of the input values.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation of the imported utility functions
ast-grep --pattern 'export function sanitizeBankAccount($_) {
  $$$
}'

ast-grep --pattern 'export function formatIBANDisplay($_) {
  $$$
}'

ast-grep --pattern 'export function formatUSAccountDisplay($_) {
  $$$
}'

Length of output: 219


Script:

#!/bin/bash
# Let's try a different approach using ripgrep to find these functions
rg "export (const|function) (sanitizeBankAccount|formatIBANDisplay|formatUSAccountDisplay)" -A 10

# Also search for the file that might contain these functions
fd "format.utils.ts"

Length of output: 866

src/components/Global/ValidatedInput/index.tsx (3)

4-4: LGTM! Clean type definition and import.

The new import and type definition are well-structured and properly typed.

Also applies to: 17-17


36-36: LGTM! Proper props destructuring.

The formatDisplayValue prop is correctly destructured alongside other props.


82-88: 🛠️ Refactor suggestion

Consider improving error handling and state management.

While the sanitization logic is good, there are a few concerns:

  1. Missing error handling for sanitizeBankAccount
  2. The isChanging flag might be misleading if sanitization removes all characters
  3. This appears to be temporary code based on the PR title

Let's verify the sanitizeBankAccount implementation:

Consider this safer implementation:

 const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
     const rawValue = e.target.value
-    const sanitizedValue = sanitizeBankAccount(rawValue)
+    let sanitizedValue
+    try {
+        sanitizedValue = sanitizeBankAccount(rawValue)
+    } catch (error) {
+        console.error('Error sanitizing bank account:', error)
+        sanitizedValue = rawValue
+    }
     onUpdate({
         value: sanitizedValue,
         isValid: false,
-        isChanging: !!sanitizedValue,
+        isChanging: sanitizedValue !== value,
     })
 }
src/components/Cashout/Components/Initial.view.tsx (3)

23-23: LGTM: Clean import of bank account sanitization utility

The addition of the sanitizeBankAccount utility aligns well with the component's bank account handling requirements.


224-225: LGTM: Proper sanitization of bank account identifiers

The sanitization of account identifiers before state updates ensures consistent handling of bank account numbers.


318-330: LGTM: Improved bank account display and interaction

The UI changes enhance the visual presentation and maintain consistent account handling with proper sanitization.

Also applies to: 339-340

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

461-465: Verify recipient type handling across the application

The changes to recipient type handling could affect other parts of the application.

Let's verify the usage of recipient types:

✅ Verification successful

Let me try different commands to gather the necessary information.


Recipient type handling is consistent across the application

The changes to recipient type handling in Initial.view.tsx are consistent with the application's type system and usage patterns. The search results show:

  • The RecipientType is properly defined in interfaces.ts as 'address' | 'ens' | 'iban' | 'us'
  • The setRecipientType usage in Initial.view.tsx correctly sets the type based on the recipient's address presence
  • The recipient type is consistently used across components for conditional rendering and flow control
  • The type handling aligns with the application's payment rail logic (SEPA for IBAN, ACH for US)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for recipient type usage
echo "Searching for recipient type references..."
rg "setRecipientType|recipientType.*=|type.*=.*('address'|'iban'|'us')" -t tsx -t ts

# Search for GeneralRecipientInput usage
echo "Searching for GeneralRecipientInput usage..."
ast-grep --pattern 'GeneralRecipientInput $$$'

# Search for related test files
echo "Searching for related test files..."
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts

Length of output: 507


Script:

#!/bin/bash
# Search for recipient type usage with correct file extensions
echo "Searching for recipient type references..."
rg "setRecipientType|recipientType.*=|type.*=.*('address'|'iban'|'us')" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -t tsx -t ts

# Search for recipient type enum or type definition
echo "Searching for recipient type definitions..."
rg "type.*Recipient.*=|enum.*Recipient|interface.*Recipient" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -t tsx -t ts

# Search specifically in the Claim directory
echo "Searching in Claim directory..."
rg "setRecipientType|recipientType" src/components/Claim/ --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -t tsx -t ts

Length of output: 9956

src/components/Offramp/Confirm.view.tsx (1)

657-662: LGTM! Improved bank account display formatting

The changes enhance the presentation of bank account information using proper flex layout and consistent formatting.

Comment on lines +1 to +3
export const sanitizeBankAccount = (value: string) => {
return value.replace(/[\s\-\._]/g, '').toLowerCase()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and consider preserving case

The sanitization function needs additional safety checks and might need to preserve case for certain bank account formats.

Consider these improvements:

 export const sanitizeBankAccount = (value: string) => {
+    if (!value) return '';
+    // Some bank accounts are case-sensitive
-    return value.replace(/[\s\-\._]/g, '').toLowerCase()
+    return value.replace(/[\s\-\._]/g, '')
 }

Also, consider adding JSDoc comments to document the function's purpose and expected input format.

📝 Committable suggestion

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

Suggested change
export const sanitizeBankAccount = (value: string) => {
return value.replace(/[\s\-\._]/g, '').toLowerCase()
}
export const sanitizeBankAccount = (value: string) => {
if (!value) return '';
// Some bank accounts are case-sensitive
return value.replace(/[\s\-\._]/g, '')
}

Comment on lines +1 to +14
export const sanitizeBankAccount = (value: string) => {
return value.replace(/[\s\-\._]/g, '').toLowerCase()
}

export const formatIBANDisplay = (value: string) => {
return value
.toUpperCase()
.replace(/(.{4})/g, '$1 ')
.trim()
}

export const formatUSAccountDisplay = (value: string) => {
return value.toUpperCase()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Missing test coverage for bank account formatting utilities

The codebase lacks dedicated test coverage for the newly added bank account formatting utilities. No test files were found for:

  • sanitizeBankAccount
  • formatIBANDisplay
  • formatUSAccountDisplay

Recommend adding test cases to verify:

  • IBAN formatting with various input patterns
  • US account number formatting
  • Bank account sanitization with different special characters
🔗 Analysis chain

Verify test coverage for bank account formatting

Given that this PR is marked as "temp commit, not fully done", ensure proper test coverage is added before merging to production.

Let's check for existing tests:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for related test files
fd -e test.ts -e spec.ts format.utils

# Check for existing test patterns
rg -l "describe.*format.*bank|test.*iban|test.*account" --type ts

Length of output: 187


Script:

#!/bin/bash
# Let's check the content of the files that matched our pattern
# to understand the test coverage context better

echo "=== Content of files with potential test patterns ==="
rg "describe.*format.*bank|test.*iban|test.*account" -A 5 --type ts

# Let's also check if there are any test files we might have missed
echo -e "\n=== Looking for any test files containing 'sanitize' or 'format' ==="
fd -e test.ts -e spec.ts | xargs rg "sanitize|format" -l

# Check for any existing test directory structure
echo -e "\n=== Test directory structure ==="
fd -t d "test|spec|__tests__"

Length of output: 3740

Comment on lines +34 to +38
export interface IResponse {
success: boolean
data: any
message?: string
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make IResponse generic and follow naming conventions

The interface uses any type which reduces type safety, and the name doesn't follow TypeScript conventions.

-export interface IResponse {
+export interface Response<T = unknown> {
     success: boolean
-    data: any
+    data: T
     message?: string
 }

This allows for type-safe usage like:

const userResponse: Response<User> = ...
const profileResponse: Response<IUserProfile> = ...

Comment on lines +13 to +19
export interface Account {
account_id: string
account_identifier: string
account_type: string
bridge_account_id?: string
account_details?: string // JSON string containing address details
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace string JSON with typed interface for account_details

Using a string type for JSON data reduces type safety and makes it harder to work with the address details.

Consider creating a proper interface for address details:

+interface AddressDetails {
+    street?: string
+    city?: string
+    state?: string
+    postalCode?: string
+    country?: string
+}

 export interface Account {
     account_id: string
     account_identifier: string
     account_type: string
     bridge_account_id?: string
-    account_details?: string // JSON string containing address details
+    account_details?: AddressDetails
 }
📝 Committable suggestion

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

Suggested change
export interface Account {
account_id: string
account_identifier: string
account_type: string
bridge_account_id?: string
account_details?: string // JSON string containing address details
}
interface AddressDetails {
street?: string
city?: string
state?: string
postalCode?: string
country?: string
}
export interface Account {
account_id: string
account_identifier: string
account_type: string
bridge_account_id?: string
account_details?: AddressDetails
}

Comment on lines +3 to +11
export interface User {
userId: string
email?: string
username?: string
full_name?: string
bridge_customer_id?: string
kycStatus?: 'verified' | 'pending' | 'rejected' | undefined
profile_picture?: string
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type definitions in User interface

Several improvements can be made to enhance type safety and maintainability:

 export interface User {
+    /** Unique identifier for the user */
     userId: string
+    /** User's email address */
     email?: string
+    /** User's chosen username */
     username?: string
+    /** User's full name */
     full_name?: string
+    /** Bridge platform customer ID */
     bridge_customer_id?: string
-    kycStatus?: 'verified' | 'pending' | 'rejected' | undefined
+    kycStatus?: 'verified' | 'pending' | 'rejected'
+    /** URL to user's profile picture */
     profile_picture?: string
 }
  1. Removed redundant undefined from union type as it's already optional
  2. Added JSDoc comments for better documentation
  3. Consider standardizing naming convention (either camelCase or snake_case)
📝 Committable suggestion

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

Suggested change
export interface User {
userId: string
email?: string
username?: string
full_name?: string
bridge_customer_id?: string
kycStatus?: 'verified' | 'pending' | 'rejected' | undefined
profile_picture?: string
}
export interface User {
/** Unique identifier for the user */
userId: string
/** User's email address */
email?: string
/** User's chosen username */
username?: string
/** User's full name */
full_name?: string
/** Bridge platform customer ID */
bridge_customer_id?: string
kycStatus?: 'verified' | 'pending' | 'rejected'
/** URL to user's profile picture */
profile_picture?: string
}

Comment on lines +745 to +750
<div className="flex w-full flex-row items-center justify-between px-2 text-h8 text-gray-1">
<div className="flex w-max flex-row items-center gap-1">
<Icon name={'transfer'} className="h-4 fill-gray-1" />
<label className="font-bold">You will receive</label>
</div>
<div className="relative flex flex-1 items-center justify-end gap-1 text-sm font-normal">
<div className="flex items-center justify-end gap-1 text-sm font-normal">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor fee calculation logic for better maintainability

The current implementation has several maintainability concerns:

  1. Complex nested ternary expressions reduce readability
  2. Fee amounts are hardcoded in multiple places
  3. Account type checks are repeated

Consider extracting the fee calculation logic into a separate function:

+const getFeeAmount = (accountType: string): number => {
+  return accountType === 'iban' ? 1 : 0.5;
+};
+
+const calculateTotalWithFee = (amount: number, accountType: string): number => {
+  const fee = getFeeAmount(accountType);
+  return amount - fee;
+};

-{user?.accounts.find(
-  (account) =>
-    account.account_identifier.replaceAll(/\s/g, '').toLowerCase() ===
-    offrampForm.recipient.replaceAll(/\s/g, '').toLowerCase()
-)?.account_type === 'iban'
-  ? offrampType == OfframpType.CASHOUT
-    ? utils.formatTokenAmount(parseFloat(usdValue ?? tokenValue ?? '') - 1)
-    : tokenPrice &&
-      claimLinkData &&
-      utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount) - 1)
-  : /* ... rest of the ternary ... */}
+{(() => {
+  const account = user?.accounts.find(
+    (acc) => acc.account_identifier.replaceAll(/\s/g, '').toLowerCase() ===
+    offrampForm.recipient.replaceAll(/\s/g, '').toLowerCase()
+  );
+  const amount = offrampType === OfframpType.CASHOUT
+    ? parseFloat(usdValue ?? tokenValue ?? '')
+    : tokenPrice && claimLinkData
+      ? tokenPrice * parseFloat(claimLinkData.tokenAmount)
+      : 0;
+  return utils.formatTokenAmount(calculateTotalWithFee(amount, account?.account_type ?? ''));
+})()}

Committable suggestion skipped: line range outside the PR's diff.

<div className="flex flex-col gap-2">
<input
disabled
value={formatIBANDisplay(getAccountDetailsValue('accountNumber'))}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Display US account numbers without IBAN formatting

Using formatIBANDisplay on US account numbers may result in incorrect formatting. Display US account numbers as-is to ensure accuracy.

Apply this diff to correct the display:

-                                            value={formatIBANDisplay(getAccountDetailsValue('accountNumber'))}
+                                            value={getAccountDetailsValue('accountNumber')}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +295 to +297
const formatDisplayValue = (value: string) => {
return formatIBANDisplay(value)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure 'formatDisplayValue' handles account types correctly

Currently, formatDisplayValue uses formatIBANDisplay, which may not format US account numbers properly. Modify the function to conditionally format the account number based on the account type.

Apply this diff to update formatDisplayValue:

         const formatDisplayValue = (value: string) => {
-            return formatIBANDisplay(value)
+            const accountType = getAccountDetailsValue('type')
+            if (accountType === 'iban') {
+                return formatIBANDisplay(value)
+            }
+            return value // For US account numbers, no special formatting
         }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +310 to +324
setValueAs: (value: string) => value?.toLowerCase(), // Store as lowercase
})}
className={`custom-input ${ibanErrors.accountNumber ? 'border border-red' : ''}`}
placeholder={'Bank account'}
autoComplete="on"
name="bank-account"
value={formatDisplayValue(getIbanFormValue('accountNumber') || '')} // Display formatted
onChange={(e) => {
// Remove spaces and store lowercase
const rawValue = e.target.value.replace(/\s/g, '')
registerIban('accountNumber').onChange({
target: {
value: rawValue.toLowerCase(),
name: 'accountNumber',
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid converting account numbers to lowercase

Converting account numbers to lowercase may lead to incorrect data, especially for numeric US account numbers. Remove the .toLowerCase() conversion to preserve the original account number.

Apply this diff to fix the issue:

                                 {...registerIban('accountNumber', {
                                     required: 'This field is required',
-                                    setValueAs: (value: string) => value?.toLowerCase(), // Store as lowercase
+                                    setValueAs: (value: string) => value?.replace(/\s/g, ''), // Remove spaces
                                 })}

And update the onChange handler:

                                 onChange={(e) => {
                                     // Remove spaces
                                     const rawValue = e.target.value.replace(/\s/g, '')
                                     registerIban('accountNumber').onChange({
                                         target: {
-                                            value: rawValue.toLowerCase(),
+                                            value: rawValue,
                                             name: 'accountNumber',
                                         },
                                     })
                                 }}
📝 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
setValueAs: (value: string) => value?.toLowerCase(), // Store as lowercase
})}
className={`custom-input ${ibanErrors.accountNumber ? 'border border-red' : ''}`}
placeholder={'Bank account'}
autoComplete="on"
name="bank-account"
value={formatDisplayValue(getIbanFormValue('accountNumber') || '')} // Display formatted
onChange={(e) => {
// Remove spaces and store lowercase
const rawValue = e.target.value.replace(/\s/g, '')
registerIban('accountNumber').onChange({
target: {
value: rawValue.toLowerCase(),
name: 'accountNumber',
},
setValueAs: (value: string) => value?.replace(/\s/g, ''), // Remove spaces
})}
className={`custom-input ${ibanErrors.accountNumber ? 'border border-red' : ''}`}
placeholder={'Bank account'}
autoComplete="on"
name="bank-account"
value={formatDisplayValue(getIbanFormValue('accountNumber') || '')} // Display formatted
onChange={(e) => {
// Remove spaces
const rawValue = e.target.value.replace(/\s/g, '')
registerIban('accountNumber').onChange({
target: {
value: rawValue,
name: 'accountNumber',
},

Comment on lines +103 to +107
useEffect(() => {
if (getAccountDetailsValue('type') === 'us') {
setAccountDetailsValue('country', 'USA')
}
}, [getAccountDetailsValue('type')])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix dependency array in useEffect to properly watch 'type' changes

The dependency array in the useEffect hook uses getAccountDetailsValue('type'), which may not trigger the effect when the 'type' field changes. Instead, use the watch function from React Hook Form to monitor changes to the 'type' field.

Apply this diff to fix the issue:

+        const accountType = watch('type')

         useEffect(() => {
-            if (getAccountDetailsValue('type') === 'us') {
+            if (accountType === 'us') {
                 setAccountDetailsValue('country', 'USA')
             }
-        }, [getAccountDetailsValue('type')])
+        }, [accountType])
📝 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
useEffect(() => {
if (getAccountDetailsValue('type') === 'us') {
setAccountDetailsValue('country', 'USA')
}
}, [getAccountDetailsValue('type')])
const accountType = watch('type')
useEffect(() => {
if (accountType === 'us') {
setAccountDetailsValue('country', 'USA')
}
}, [accountType])

@notion-workspace
Copy link
Copy Markdown

cashout problem

@Hugo0 Hugo0 changed the title fix: temp commit, not fully done. Various cashout fixes and general improvements Cashout Improvements Nov 26, 2024
@Hugo0 Hugo0 marked this pull request as draft November 26, 2024 09:57
Copy link
Copy Markdown
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: 26

🧹 Outside diff range and nitpick comments (19)
.github/workflows/tests.yml (3)

19-24: Add dependency caching to speed up workflow execution

The workflow could benefit from caching pnpm dependencies to improve execution time.

 - uses: pnpm/action-setup@v2
   with:
       version: 8

+ - name: Get pnpm store directory
+   shell: bash
+   run: |
+     echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

+ - uses: actions/cache@v3
+   name: Setup pnpm cache
+   with:
+     path: ${{ env.STORE_PATH }}
+     key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+     restore-keys: |
+       ${{ runner.os }}-pnpm-store-

 - name: Install Dependencies
   run: pnpm install

29-33: Add coverage threshold checks

While coverage artifacts are being uploaded, there's no verification of coverage thresholds. Consider adding coverage checks to maintain code quality.

 - name: Run Tests
-  run: pnpm test
+  run: pnpm test -- --coverage --coverageThreshold='{"global":{"branches":"80","functions":"80","lines":"80","statements":"80"}}'

 - name: Upload Coverage
   uses: actions/upload-artifact@v4
   with:
       name: coverage
       path: coverage/
+  if: always()  # Upload coverage even if tests fail

9-11: Consider adding a test matrix for multiple Node.js versions

Testing against multiple Node.js versions would help ensure compatibility.

 jobs:
   test:
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [18.x, 20.x]
+      fail-fast: false
     steps:

Then update the Node.js setup step to use:

node-version: ${{ matrix.node-version }}
.env.example (1)

1-23: Add documentation for environment variables

Consider adding comments to describe:

  1. The purpose of each variable
  2. Required vs optional variables
  3. Format/structure requirements for values
  4. Development vs production considerations

Would you like me to help generate comprehensive documentation for these environment variables?

🧰 Tools
🪛 Gitleaks (8.21.2)

3-3: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

README.md (1)

35-66: Well-structured testing documentation with room for enhancement

The testing documentation is clear and well-organized. Consider enhancing it with:

  1. Information about test coverage requirements
  2. Guidelines for mocking and test data
  3. Best practices for testing async code
  4. Instructions for debugging tests

Would you like me to provide examples for these additional sections?

🧰 Tools
🪛 LanguageTool

[duplication] ~52-~52: Possible typo: you repeated a word
Context: ...ts__directories next to the code they test - Test files should be named.test.tsor...

(ENGLISH_WORD_REPEAT_RULE)

src/utils/__tests__/format.utils.test.ts (4)

4-22: Consider adding more edge cases to sanitizeBankAccount tests.

While the basic cases are covered, consider adding tests for:

  • Special characters beyond spaces/hyphens/dots (e.g., @, #, $)
  • Numbers-only input
  • Mixed alphanumeric input
  • Very long input strings
  • Non-string input types (numbers, objects)
 describe('sanitizeBankAccount', () => {
+    it('should handle special characters', () => {
+        expect(sanitizeBankAccount('BE68@539#007$547')).toBe('be68539007547')
+    })
+
+    it('should handle numbers-only input', () => {
+        expect(sanitizeBankAccount('12345')).toBe('12345')
+    })
+
+    it('should handle mixed alphanumeric input', () => {
+        expect(sanitizeBankAccount('abc123DEF')).toBe('abc123def')
+    })
+
+    it('should handle very long input', () => {
+        expect(sanitizeBankAccount('a'.repeat(100))).toBe('a'.repeat(100))
+    })
+
+    it('should handle non-string input', () => {
+        expect(sanitizeBankAccount(123 as any)).toBe('123')
+        expect(sanitizeBankAccount({} as any)).toBe('')
+    })

25-38: Add validation tests for IBAN format.

The current tests focus on formatting, but consider adding validation tests for:

  • Invalid IBAN format (e.g., wrong country code)
  • IBAN length constraints (too short/long)
  • Invalid characters in IBAN
 describe('IBAN format', () => {
+    it('should handle invalid country code', () => {
+        expect(formatBankAccountDisplay('XX68539007547034', 'iban')).toBe('XX68 5390 0754 7034')
+    })
+
+    it('should handle minimum/maximum IBAN length', () => {
+        // Minimum length IBAN (Norway - 15 chars)
+        expect(formatBankAccountDisplay('NO9386011117947', 'iban')).toBe('NO93 8601 1117 947')
+        // Maximum length IBAN (Malta - 31 chars)
+        expect(formatBankAccountDisplay('MT84MALT011000012345MTLCAST001S', 'iban'))
+            .toBe('MT84 MALT 0110 0001 2345 MTLC AST0 01S')
+    })
+
+    it('should handle invalid characters', () => {
+        expect(formatBankAccountDisplay('BE68$390@754#034', 'iban')).toBe('BE68 5390 0754 7034')
+    })

40-53: Add validation tests for US account format.

Consider adding tests for:

  • Invalid routing number length (should be exactly 9 digits)
  • Invalid account number length (typically 10-12 digits)
  • Special characters in account/routing numbers
 describe('US account format', () => {
+    it('should handle invalid routing number length', () => {
+        expect(formatBankAccountDisplay('12345678', 'us')).toBe('12345678')
+        expect(formatBankAccountDisplay('1234567890', 'us')).toBe('123456789-0')
+    })
+
+    it('should handle invalid account number length', () => {
+        const routing = '123456789'
+        expect(formatBankAccountDisplay(routing + '123', 'us')).toBe('123456789-123')
+        expect(formatBankAccountDisplay(routing + '1'.repeat(15), 'us'))
+            .toBe('123456789-' + '1'.repeat(15))
+    })
+
+    it('should handle special characters', () => {
+        expect(formatBankAccountDisplay('123@456#789$123456789', 'us'))
+            .toBe('123456789-123456789')
+    })

55-67: Enhance auto-detection test coverage.

The auto-detection tests should be more comprehensive. Consider adding:

  • Tests for ambiguous input that could match either format
  • Tests for invalid input that matches neither format
  • More edge cases beyond undefined
 describe('Auto-detection', () => {
+    it('should handle ambiguous input', () => {
+        // Could be either a short IBAN or US account
+        expect(formatBankAccountDisplay('123456789')).toBe('123456789')
+    })
+
+    it('should handle invalid format input', () => {
+        expect(formatBankAccountDisplay('invalid-input')).toBe('INVALID-INPUT')
+    })
+
+    it('should handle various edge cases', () => {
+        expect(formatBankAccountDisplay(null as any)).toBe('')
+        expect(formatBankAccountDisplay(123 as any)).toBe('123')
+        expect(formatBankAccountDisplay({} as any)).toBe('')
+    })
src/components/Profile/index.tsx (1)

544-583: Add defensive programming checks

The code assumes user.pointsPerReferral is always defined and iterable. Add null checks to prevent runtime errors.

Add null checks and provide fallback UI:

- user?.pointsPerReferral.map(
+ user?.pointsPerReferral?.length ? (
+   user.pointsPerReferral.map(
     (referral: { address: string; points: number; totalReferrals: number },
     index: number
   ) => (
     // ... existing code ...
   ))
+ ) : (
+   <div className="text-center text-h8">No referrals yet</div>
+ )
src/components/Global/LinkAccountComponent/index.tsx (3)

15-15: Remove unused import

The USBankAccountInput component is imported but never used in the code.

-import { USBankAccountInput } from '../USBankAccountInput'

163-167: Remove debug console.log statements

Multiple console.log statements are present throughout the code. These should be removed or replaced with proper logging for production code.

Consider using a logging utility that can be configured based on the environment:

-console.log('Starting form submission with user data:', {
-    user,
-    kycStatus: user?.user?.kycStatus,
-    accounts: user?.accounts,
-})
+logger.debug('Starting form submission with user data:', {
+    kycStatus: user?.user?.kycStatus,
+    accountsCount: user?.accounts?.length,
+})

Also applies to: 177-180, 189-199, 205-206, 210-211, 287-288


478-480: Add ZIP code format validation

The ZIP code field should validate the input format to ensure it matches US ZIP code patterns (5 digits or 5+4 format).

 {...registerAccountDetails('postalCode', {
     required: 'ZIP code is required',
+    pattern: {
+        value: /^\d{5}(-\d{4})?$/,
+        message: 'Please enter a valid ZIP code (e.g., 12345 or 12345-6789)'
+    }
 })}
src/components/Offramp/Confirm.view.tsx (3)

Line range hint 251-333: Enhance error messages for better user experience

The error handling for cross-chain scenarios is thorough, but some error messages could be more user-friendly and actionable.

Consider these improvements:

-                    errorMessage =
-                        'This token is not supported for cross-chain transfers. Please try a different token.'
+                    errorMessage =
+                        'This token cannot be converted to your preferred currency. Please try using a popular token like ETH or USDC.'

785-790: Extract fee calculation logic

The amount received calculation is tightly coupled with the display logic, making it hard to test and maintain.

Consider creating a dedicated fee calculation service:

  1. Create a new file src/services/fee.service.ts
  2. Extract fee calculation logic:
export interface FeeConfig {
  sepaFee: number;
  achFee: number;
}

export const calculateFeeAmount = (accountType: string, config: FeeConfig): number => {
  return accountType === 'iban' ? config.sepaFee : config.achFee;
};

export const calculateAmountReceived = (total: number, accountType: string, config: FeeConfig): number => {
  const fee = calculateFeeAmount(accountType, config);
  return total - fee;
};

873-882: Enhance error message clarity

The error display implementation is good, but the messages could be more specific and actionable.

Consider these improvements:

-                        'This token cannot be cashed out directly.{' '}
+                        'This token cannot be processed for bank withdrawal. For assistance with supported tokens,{' '}
src/app/api/bridge/external-account/create-external-account/route.ts (3)

14-15: Simplify request data handling by destructuring directly

You can destructure the request body directly without assigning it to requestData, reducing unnecessary variables.

Apply this diff to simplify the code:

- const requestData = await request.json()
- const { accountType, accountDetails, address, accountOwnerName } = requestData
+ const { accountType, accountDetails, address, accountOwnerName } = await request.json()

99-101: Eliminate redundant re-reading of response text

responseText has already been read and assigned at line 90. Reading it again can cause errors as the stream is exhausted after the first read.

Apply this diff to remove redundant code:

- const responseText = await response.text()
  console.log('Error response text:', responseText)

204-208: Handle JSON parsing errors gracefully

Parsing the response without a try-catch block may lead to unhandled exceptions if the response is not valid JSON.

Apply this diff to safely parse the response:

- const data = JSON.parse(responseText)
- console.log('Successfully created external account:', data)
+ let data
+ try {
+     data = JSON.parse(responseText)
+     console.log('Successfully created external account with ID:', data.id)
+ } catch (e) {
+     console.error('Error parsing response:', e)
+     return new NextResponse('Failed to parse Bridge API response', { status: 500 })
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7e886e5 and a71d420.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • .env.example (1 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • README.md (1 hunks)
  • jest.setup.ts (1 hunks)
  • package.json (2 hunks)
  • src/app/api/bridge/external-account/create-external-account/route.ts (4 hunks)
  • src/components/Global/GeneralRecipientInput/index.tsx (3 hunks)
  • src/components/Global/LinkAccountComponent/index.tsx (7 hunks)
  • src/components/Offramp/Confirm.view.tsx (6 hunks)
  • src/components/Offramp/Offramp.consts.ts (1 hunks)
  • src/components/Offramp/Success.view.tsx (1 hunks)
  • src/components/Profile/index.tsx (1 hunks)
  • src/interfaces/interfaces.ts (2 hunks)
  • src/utils/__tests__/format.utils.test.ts (1 hunks)
  • src/utils/format.utils.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • jest.setup.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Global/GeneralRecipientInput/index.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Offramp/Offramp.consts.ts (2)
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#420
File: src/components/Offramp/Offramp.consts.ts:27-28
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Offramp/Offramp.consts.ts`, the `MIN_CASHOUT_LIMIT` is set to $10 because smaller amounts are impractical due to approximately $1 fee per cashout.
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#420
File: src/components/Offramp/Offramp.consts.ts:27-28
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Offramp/Offramp.consts.ts`, the `MAX_CASHOUT_LIMIT` is set to $101,000 as a safety measure; it can be increased over time if needed, though it's unlikely to be exceeded.
🪛 Gitleaks (8.21.2)
.env.example

3-3: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 LanguageTool
README.md

[duplication] ~52-~52: Possible typo: you repeated a word
Context: ...ts__directories next to the code they test - Test files should be named.test.tsor...

(ENGLISH_WORD_REPEAT_RULE)

🔇 Additional comments (15)
src/utils/format.utils.ts (1)

1-30: Add comprehensive test coverage

As noted in previous reviews, test coverage is still missing for these utility functions.

Let's verify the current test coverage:

#!/bin/bash
# Check for existing test files
fd -e test.ts -e spec.ts format.utils

# Look for any test cases related to these functions
rg -l "describe.*sanitizeBankAccount|describe.*formatBankAccountDisplay" --type ts
.env.example (1)

1-23: Verify and revoke exposed credentials

Given that this is marked as a "temp commit", please verify if any of the hardcoded API keys and secrets are real credentials. If they are:

  1. Revoke them immediately
  2. Generate new credentials
  3. Update all necessary services
  4. Consider this a security incident

Special attention to:

  • Sentry DSN (appears to be a real project URL)
  • Infura API key
  • WalletConnect project ID
🧰 Tools
🪛 Gitleaks (8.21.2)

3-3: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

README.md (1)

32-66: ⚠️ Potential issue

Documentation changes should not be marked as temporary

The PR title suggests this is a "temp commit, not fully done" but documentation changes should be complete and accurate when merged to avoid confusion for other developers.

🧰 Tools
🪛 LanguageTool

[duplication] ~52-~52: Possible typo: you repeated a word
Context: ...ts__directories next to the code they test - Test files should be named.test.tsor...

(ENGLISH_WORD_REPEAT_RULE)

src/components/Offramp/Offramp.consts.ts (1)

27-27: Temporary debugging changes should not be committed

The PR title indicates this is a temporary commit. Modifying production constants for debugging purposes should be done locally rather than committed, especially when it contradicts established business logic.

Let's check if there are any other temporary changes in the codebase:

src/utils/__tests__/format.utils.test.ts (1)

1-4: Well-structured test organization!

The test file follows Jest best practices with clear describe blocks and proper imports.

package.json (3)

90-102: Verify Jest setup file and consider additional test patterns.

The Jest configuration looks good but requires verification of the setup file and could benefit from additional test patterns.

Let's verify the setup file exists and check for common test patterns:

Consider adding these additional test patterns to be more inclusive:

 "testMatch": [
     "**/__tests__/**/*.test.[jt]s?(x)",
-    "**/?(*.)+(spec|test).[jt]s?(x)"
+    "**/?(*.)+(spec|test).[jt]s?(x)",
+    "**/tests/**/*.[jt]s?(x)",
+    "**/__tests__/**/*.[jt]s?(x)"
 ]
✅ Verification successful

Jest setup and test patterns are properly configured

The verification shows:

  • jest.setup.ts exists with proper testing library imports and console mocking setup
  • Current test patterns successfully match existing test files (e.g., src/utils/__tests__/format.utils.test.ts)
  • The suggested additional patterns would be redundant since:
    • **/__tests__/**/*.test.[jt]s?(x) already covers the __tests__ directory
    • **/?(*.)+(spec|test).[jt]s?(x) covers both .test and .spec files
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if jest.setup.ts exists and contains necessary imports
fd -t f "jest.setup.ts" --exec cat {}

# Look for existing test files to ensure patterns match
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx

Length of output: 380


Line range hint 1-102: Consider completing the implementation before merging.

The PR title indicates this is a temporary commit. While the Jest setup looks complete and well-structured, consider:

  1. Completing any pending changes
  2. Adding some initial tests to validate the setup
  3. Updating the PR title once it's ready for review

Let's check for any existing test files:


83-89: Verify TypeScript version compatibility.

The TypeScript version 5.6.3 is quite recent. Ensure it's compatible with all dependencies, especially Next.js and Jest.

Let's check for potential version conflicts:

✅ Verification successful

TypeScript 5.6.3 is compatible with the project setup

The TypeScript version is compatible with:

  • Next.js 14.2.18 (which supports TypeScript 5.x)
  • The project's tsconfig.json configuration is standard and doesn't use any deprecated features
  • All @types packages are at compatible versions
  • Jest and ts-jest versions are aligned for TypeScript 5.x support
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check Next.js TypeScript requirements
rg -A 5 '"typescript":' "package.json"

# Check for TypeScript-related entries in package-lock.json
fd -t f "package-lock.json" --exec rg -A 5 '"typescript":'

Length of output: 312


Script:

#!/bin/bash
# Check Next.js version and its dependencies
rg '"next":' package.json

# Check for any TypeScript configuration files
fd tsconfig

# Look for any TypeScript-related errors or configurations in Next.js config
fd -t f "next.config" --exec cat {}

Length of output: 5109


Script:

#!/bin/bash
# Let's check the content of tsconfig.json to verify TypeScript configuration
cat tsconfig.json

# Check for any TypeScript-specific Next.js requirements in package.json
rg "@types/node|@types/react|@types/react-dom" package.json

Length of output: 1143

src/components/Offramp/Success.view.tsx (1)

60-60: Verify layout behavior with varying content lengths

The change from w-1/3 to w-max improves flexibility but should be tested with:

  • Long labels/translations
  • Different viewport sizes
  • RTL languages

Consider adding a maximum width constraint (max-w-[...]) to prevent potential layout issues with very long content.

src/interfaces/interfaces.ts (1)

8-8: LGTM: Optional message property addition

The optional message property is a good addition to the IResponse interface, following common API response patterns.

src/components/Global/LinkAccountComponent/index.tsx (3)

45-46: LGTM: Improved loading state management

The loading state implementation with specific states provides better user feedback during different stages of the account linking process.

Also applies to: 371-373


331-333: LGTM: Improved account number formatting

The formatDisplayValue function now correctly handles both IBAN and US account numbers using the formatBankAccountDisplay utility.


Line range hint 1-1: Address temporary commit status

The PR title indicates this is a temporary commit. Consider completing any remaining tasks and removing the temporary status before merging.

src/components/Offramp/Confirm.view.tsx (1)

685-690: LGTM! Clean implementation of bank account display

The bank account display implementation is well-structured and uses the new formatting utility for consistency.

src/app/api/bridge/external-account/create-external-account/route.ts (1)

143-155: Validate and handle the accounts data properly

Ensure that accounts is an array before using .find(), and verify that the comparison logic accurately identifies the existing account.

Run the following script to confirm accounts is an array:

Replace ${YOUR_API_BASE_URL} and test_customer_id with appropriate values.

✅ Verification successful

Array validation is properly implemented

The code already has sufficient validation and proper handling of the accounts data:

  1. In get-all-for-customerId/route.ts, accounts is initialized as an array with a fallback: const accounts = responseData.data || []
  2. Before using .find(), the code checks if allAccounts.ok is true
  3. The IBridgeAccount interface is well-defined with proper typing for all fields
  4. The comparison logic in .find() correctly matches the account types and details according to the interface definition
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that 'accounts' is an array.

curl -s -X POST "${YOUR_API_BASE_URL}/api/bridge/external-account/get-all-for-customerId" \
  -H "Content-Type: application/json" \
  -d '{"customerId": "test_customer_id"}' | jq 'type'

Length of output: 155


Script:

#!/bin/bash
# Let's analyze how accounts is obtained and validated in the codebase
rg -A 5 "const accounts\s*=" 

# Also check for any type definitions or interfaces related to accounts
ast-grep --pattern 'interface IBridgeAccount {
  $$$
}'

# Check how the accounts data is fetched and processed
rg "get-all-for-customerId" -A 10

Length of output: 6661

Comment on lines +15 to +17
- uses: actions/setup-node@v4
with:
node-version: '21.1.0'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using an LTS version of Node.js

Node.js 21.1.0 is a very recent version and might be unstable for production use. Consider using an LTS version like 20.x for better stability.

- node-version: '21.1.0'
+ node-version: '20.x'
📝 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
- uses: actions/setup-node@v4
with:
node-version: '21.1.0'
- uses: actions/setup-node@v4
with:
node-version: '20.x'

Comment on lines +26 to +27
- name: Run Tests
run: pnpm test
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add timeout and error handling for tests

The test step should have a timeout to prevent hanging builds and proper error handling.

 - name: Run Tests
-  run: pnpm test
+  run: pnpm test
+  timeout-minutes: 10
+  continue-on-error: false
📝 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
- name: Run Tests
run: pnpm test
- name: Run Tests
run: pnpm test
timeout-minutes: 10
continue-on-error: false

Comment on lines +3 to +7
on:
push:
branches: ['**']
pull_request:
branches: [main, develop]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the shell script results, I can now provide a final response as there's clear evidence of the workflow configurations in the repository.

Workflow trigger optimization needed for both CI workflows

The current configuration runs workflows on all branch pushes ('**'), which is resource-intensive. Additionally, the repository has two workflows with overlapping triggers:

  • tests.yml: Runs on all pushes and PRs to main/develop
  • prettier.yml: Runs on all pushes (with PR triggers commented out)

Suggested changes:

  • Limit push triggers to important branches (main, develop) and feature branch patterns
  • Enable pull_request triggers for the prettier workflow to catch formatting issues early
  • Consider combining both workflows to reduce CI overhead
🔗 Analysis chain

Review the workflow trigger configuration

The current configuration runs on all pushes to any branch, which might be resource-intensive. Consider limiting this to specific branches or patterns.

 on:
   push:
-    branches: ['**']
+    branches: [main, develop, 'feature/**']
   pull_request:
     branches: [main, develop]
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing workflow configurations
fd -e yml -e yaml . .github/workflows/ --exec grep -l "on:" {} \; | while read -r file; do
  echo "=== $file ==="
  cat "$file" | grep -A 5 "on:"
done

Length of output: 1327

Comment on lines +11 to +13
if (!type) {
type = /^[A-Z]{2}/i.test(sanitized) ? 'iban' : 'us'
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve IBAN detection logic

The current IBAN detection is oversimplified and could lead to false positives. Not all strings starting with two letters are valid IBANs.

Consider using a more robust IBAN validation:

-    if (!type) {
-        type = /^[A-Z]{2}/i.test(sanitized) ? 'iban' : 'us'
-    }
+    if (!type) {
+        // IBAN format: country code (2 letters) + check digits (2 numbers) + BBAN (up to 30 chars)
+        type = /^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/i.test(sanitized) ? 'iban' : 'us'
+    }
📝 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
if (!type) {
type = /^[A-Z]{2}/i.test(sanitized) ? 'iban' : 'us'
}
if (!type) {
// IBAN format: country code (2 letters) + check digits (2 numbers) + BBAN (up to 30 chars)
type = /^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/i.test(sanitized) ? 'iban' : 'us'
}

Comment on lines +1 to +4
export const sanitizeBankAccount = (value: string | undefined): string => {
if (!value) return ''
return value.replace(/[\s\-\._]/g, '').toLowerCase()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance type safety and add documentation

The function needs better documentation and type safety improvements.

Apply these changes:

+/**
+ * Sanitizes a bank account number by removing whitespace and special characters.
+ * @param value - The bank account number to sanitize
+ * @returns A cleaned string with all whitespace and special characters removed
+ */
-export const sanitizeBankAccount = (value: string | undefined): string => {
+export const sanitizeBankAccount = (value?: string | null): string => {
     if (!value) return ''
-    return value.replace(/[\s\-\._]/g, '').toLowerCase()
+    return value.replace(/[\s\-\._]/g, '')  // Preserve case as some bank accounts are case-sensitive
}
📝 Committable suggestion

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

Suggested change
export const sanitizeBankAccount = (value: string | undefined): string => {
if (!value) return ''
return value.replace(/[\s\-\._]/g, '').toLowerCase()
}
/**
* Sanitizes a bank account number by removing whitespace and special characters.
* @param value - The bank account number to sanitize
* @returns A cleaned string with all whitespace and special characters removed
*/
export const sanitizeBankAccount = (value?: string | null): string => {
if (!value) return ''
return value.replace(/[\s\-\._]/g, '') // Preserve case as some bank accounts are case-sensitive
}

Comment on lines +17 to +23
console.log('Creating external account with:', {
customerId,
accountType,
accountDetails,
address,
accountOwnerName,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid logging sensitive information

Logging sensitive data such as accountDetails, address, and accountOwnerName can lead to security risks. It's best to limit the logged information.

Apply this diff to sanitize the logs:

- console.log('Creating external account with:', {
-     customerId,
-     accountType,
-     accountDetails,
-     address,
-     accountOwnerName,
- })
+ console.log('Creating external account for customer:', customerId)
📝 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
console.log('Creating external account with:', {
customerId,
accountType,
accountDetails,
address,
accountOwnerName,
})
console.log('Creating external account for customer:', customerId)

Comment on lines +67 to +77
console.log('Sending request to Bridge API:', {
url: `https://api.bridge.xyz/v0/customers/${customerId}/external_accounts`,
method: 'POST',
headers: {
'Idempotency-Key': idempotencyKey,
'Api-Key': '[REDACTED]',
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent exposure of sensitive headers and body in logs

Logging headers like Idempotency-Key and the request body may expose sensitive information. It's safer to log minimal details.

Apply this diff to adjust the logging:

- console.log('Sending request to Bridge API:', {
-     url: `https://api.bridge.xyz/v0/customers/${customerId}/external_accounts`,
-     method: 'POST',
-     headers: {
-         'Idempotency-Key': idempotencyKey,
-         'Api-Key': '[REDACTED]',
-         Accept: 'application/json',
-         'Content-Type': 'application/json',
-     },
-     body: JSON.stringify(body),
- })
+ console.log('Sending request to Bridge API for customer:', customerId)
📝 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
console.log('Sending request to Bridge API:', {
url: `https://api.bridge.xyz/v0/customers/${customerId}/external_accounts`,
method: 'POST',
headers: {
'Idempotency-Key': idempotencyKey,
'Api-Key': '[REDACTED]',
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
console.log('Sending request to Bridge API for customer:', customerId)

const data = await response.json()

const data = JSON.parse(responseText)
console.log('Successfully created external account:', data)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Limit logging of sensitive information

Logging the entire data object may expose sensitive details. Log only the necessary identifiers or confirmation messages.

Apply this diff to adjust the logging:

- console.log('Successfully created external account:', data)
+ console.log('Successfully created external account with ID:', data.id)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +126 to +134
const allAccounts = await fetch(`/api/bridge/external-account/get-all-for-customerId`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
customerId,
}),
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use absolute URLs for server-side fetch calls

When making server-side fetch calls to your own API routes, relative URLs may not resolve correctly. Use absolute URLs instead.

Consider modifying the fetch call:

- const allAccounts = await fetch(`/api/bridge/external-account/get-all-for-customerId`, {
+ const allAccounts = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/bridge/external-account/get-all-for-customerId`, {

Ensure that NEXT_PUBLIC_BASE_URL is defined in your environment variables.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +162 to +174
console.log('Found matching existing account:', existingAccount)
return new NextResponse(
JSON.stringify({
success: true,
data: existingAccount,
}),
{
status: 200,
headers: {
'Content-Type': 'application/json',
},
}
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid returning full account details in responses

Returning sensitive account data can pose security risks. Return only the necessary information to the client.

Apply this diff to sanitize the response:

return new NextResponse(
    JSON.stringify({
        success: true,
        data: {
            id: existingAccount.id,
            accountType: existingAccount.account_details.type,
            last4: existingAccount.account_details.last_4,
            // Include only non-sensitive fields
        },
    }),
    {
        status: 200,
        headers: {
            'Content-Type': 'application/json',
        },
    }
)

Committable suggestion skipped: line range outside the PR's diff.

@Hugo0 Hugo0 assigned Hugo0 and jjramirezn and unassigned Hugo0 Nov 26, 2024
@Hugo0 Hugo0 marked this pull request as ready for review November 26, 2024 11:19
Copy link
Copy Markdown
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 (1)
.github/workflows/prettier.yml (1)

5-5: Consider refining the branch pattern.

The current pattern ['**'] will trigger the workflow for PRs against any branch. Consider limiting this to your main development branches (e.g., main, develop) to avoid unnecessary workflow runs.

-        branches: ['**']
+        branches: ['main', 'develop']
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a71d420 and b9ee37c.

📒 Files selected for processing (2)
  • .github/workflows/prettier.yml (1 hunks)
  • .github/workflows/tests.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/tests.yml
🔇 Additional comments (1)
.github/workflows/prettier.yml (1)

4-5: LGTM! Efficient workflow trigger configuration.

Changing the trigger from push to pull_request is a good practice as it:

  • Prevents duplicate workflow runs (PR creation + push)
  • Catches formatting issues during code review
  • Aligns with the introduction of the new test workflow

Comment on lines +50 to +52
} else if (/^[0-9]{1,17}$/.test(sanitizedInput)) {
type = 'us'
isValid = await utils.validateBankAccount(recipient)
if (!isValid) {
errorMessage.current =
'Invalid bank account. For US bank accounts, enter your bank routing number (9 digits) followed by your account number (example: 1112223330001234567 where routing number is: 111222333 and account number: 0001234567)'
}
isValid = true
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

provide it

Comment on lines +120 to +128
placeholder={placeholder}
validate={checkAddress}
onUpdate={onInputUpdate}
className={className}
autoComplete="on"
name="bank-account"
suggestions={getSuggestions(recipientType.current)}
infoText={infoText}
formatDisplayValue={formatDisplayValue}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Input needs to be reworked for: autocomplete="bank-account-number"

Comment on lines +65 to +72
<div className="flex items-center gap-1">
$
{user?.accounts.find(
(account) =>
account.account_identifier.replaceAll(/\s/g, '').toLowerCase() ===
offrampForm.recipient.replaceAll(/\s/g, '').toLowerCase()
)?.account_type === 'iban'
? offrampType == _consts.OfframpType.CASHOUT
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this needs to be standardized with new format utils

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (8)
src/utils/token.utils.ts (1)

27-32: Add input validation and reuse constants

The function looks good but could benefit from some improvements:

+const NATIVE_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000'
+
 export const getSquidTokenAddress = (tokenAddress: string): string => {
+    if (!tokenAddress.match(/^0x[0-9a-fA-F]{40}$/)) {
+        throw new Error('Invalid Ethereum address format');
+    }
-    return tokenAddress === '0x0000000000000000000000000000000000000000'
+    return tokenAddress === NATIVE_TOKEN_ADDRESS
         ? SQUID_ETH_ADDRESS
         : tokenAddress.toLowerCase()
 }
src/components/Request/Pay/Views/Initial.view.tsx (2)

Line range hint 141-167: Consider consolidating error state updates

The error handling sets multiple state variables independently. Consider creating a single function to handle all error-related state updates to ensure consistency and maintainability.

+ const handleEstimateTxError = () => {
+   setErrorState({ showError: true, errorMessage: ERR_NO_ROUTE })
+   setIsFeeEstimationError(true)
+   setSlippagePercentage(undefined)
+   setXChainUnsignedTxs(undefined)
+   setTxFee('0')
+ }

  try {
    // ... existing code ...
  } catch (error) {
-   setErrorState({ showError: true, errorMessage: ERR_NO_ROUTE })
-   setIsFeeEstimationError(true)
-   setSlippagePercentage(undefined)
-   setXChainUnsignedTxs(undefined)
-   setTxFee('0')
+   handleEstimateTxError()
  }

Line range hint 284-321: Enhance error handling specificity in transaction submission

The current error handling uses a generic error handler. Consider handling specific error cases (e.g., insufficient balance, network errors, signature rejections) to provide more targeted user feedback.

  try {
    // ... existing code ...
  } catch (error) {
+   if (error instanceof Error) {
+     if (error.message.includes('insufficient balance')) {
+       setErrorState({
+         showError: true,
+         errorMessage: 'Insufficient balance to complete the transaction',
+       })
+     } else if (error.message.includes('user rejected')) {
+       setErrorState({
+         showError: true,
+         errorMessage: 'Transaction was rejected in wallet',
+       })
+     } else {
        const errorString = ErrorHandler(error)
        setErrorState({
          showError: true,
          errorMessage: errorString,
        })
+     }
+   }
    console.error('Error while submitting request link fulfillment:', error)
  }
src/components/Claim/Link/Initial.view.tsx (3)

179-196: Consider improving error handling and logging.

While the routing logic is functional, there are a few areas for improvement:

  1. The debug log on line 183 should be removed or replaced with proper logging.
  2. The error handling could be more specific about why routing through USDC Optimism is necessary.
-console.log('Debug - Routing through USDC Optimism')
+logger.info('Fallback: Routing through USDC Optimism due to unsupported token/chain combination')

331-376: Enhance route fetching implementation.

The route fetching logic has been improved, but there are some potential enhancements:

  1. Consider extracting the hardcoded addresses to constants
  2. The error message could be more specific about why the route wasn't found
-                    route.toToken.toLowerCase() === claimLinkData.tokenAddress.toLowerCase() &&
+                    areTokenAddressesEqual(route.toToken, claimLinkData.tokenAddress) &&

-                errorMessage: 'No route found for the given token pair.',
+                errorMessage: `No route found from ${fromToken} to ${toToken || selectedTokenAddress}`,

455-459: Consider adding input validation.

The recipient type assignment logic could benefit from additional validation:

  1. Add type checking for the recipient address
  2. Consider handling edge cases where the address might be empty string vs undefined
-            if (!update.recipient.address) {
+            if (!update.recipient.address?.trim()) {
                 setRecipientType('address')
             } else {
                 setRecipientType(update.type)
             }
src/components/Offramp/Confirm.view.tsx (2)

Line range hint 252-334: Enhance user-facing error messages

While the error handling improvements are good, consider making the error messages more user-friendly and actionable:

-                    errorMessage: 'This token does not support cross-chain transfers. Please try a different token.',
+                    errorMessage: 'This token cannot be transferred across chains. Please use a supported token like USDC or ETH.',

-                    errorMessage = 'Unable to fetch token data'
+                    errorMessage = 'We could not verify this token. Please try again or use a different token.'

-                    errorMessage = 'Failed to setup cross-chain transfer'
+                    errorMessage = 'Unable to complete the transfer. Please try again or contact support if the issue persists.'

875-893: Improve error message accessibility

While the error handling UI improvements are good, consider enhancing accessibility:

-                    <div className="text-center">
+                    <div className="text-center" role="alert" aria-live="polite">
-                            <label className="text-h8 font-normal text-red">
+                            <div className="text-h8 font-normal text-red">
-                                <CrispButton className="text-blue-600 underline">
+                                <CrispButton className="text-blue-600 underline" aria-label="Contact support via chat">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b9ee37c and 8210232.

📒 Files selected for processing (5)
  • src/components/Claim/Link/Initial.view.tsx (6 hunks)
  • src/components/Offramp/Confirm.view.tsx (8 hunks)
  • src/components/Offramp/Offramp.consts.ts (1 hunks)
  • src/components/Request/Pay/Views/Initial.view.tsx (2 hunks)
  • src/utils/token.utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Offramp/Offramp.consts.ts
🧰 Additional context used
📓 Learnings (2)
src/components/Claim/Link/Initial.view.tsx (1)
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#545
File: src/components/Global/GeneralRecipientInput/index.tsx:118-126
Timestamp: 2024-11-26T12:06:11.603Z
Learning: For bank account input fields, use `autocomplete="bank-account-number"` when the recipient type is `'us'` or `'iban'`.
src/components/Request/Pay/Views/Initial.view.tsx (3)
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:67-74
Timestamp: 2024-11-12T09:39:20.720Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, the functions `getDefaultProvider` and `getTokenContractDetails` used in `fetchTokenSymbol` do not throw errors but can return `undefined`. Additional error handling is unnecessary since this is already handled in the code.
🔇 Additional comments (6)
src/utils/token.utils.ts (1)

24-25: Verify the standardized Squid ETH address

The SQUID_ETH_ADDRESS appears to be a special identifier. Let's verify this is the correct and standardized address used across the ecosystem.

✅ Verification successful

Let me gather more context about this address by searching for related documentation and checking if this is a standard ETH placeholder address used in other projects.


Based on the search results, I can now provide a final response as we have sufficient evidence about the usage of this address pattern in the codebase.

The SQUID_ETH_ADDRESS is correctly implemented

This is a standardized address pattern used to represent native ETH/native tokens in the Squid protocol. The evidence shows:

  • It's consistently used across the codebase for native token handling
  • The address is properly normalized in areTokenAddressesEqual function
  • It's listed in nativeCurrencyAddresses array along with the zero address
  • The pattern 0xeeee... is used in both uppercase and lowercase forms but refers to the same concept
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of this address in the codebase
rg -i "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"

# Search for any documentation or comments about this address
rg -i "squid.*eth.*address" -A 2 -B 2

Length of output: 3261


Script:

#!/bin/bash
# Search for comments or documentation explaining this address pattern
rg -i "0xeeee|native eth|eth address" -A 2 -B 2

# Search for any configuration or documentation files
fd -e md -e json . -x grep -l -i "0xeeee|native eth|eth address" {} \;

Length of output: 1755

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

26-26: LGTM! Good refactoring of cross-chain support check

Moving the cross-chain support check logic to a separate utility function improves code maintainability and reusability.

Also applies to: 131-134

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

39-40: LGTM! Clean import of utility functions.

The new utility imports improve code organization by moving token-related functions to a dedicated utils file.


148-151: LGTM! Improved code maintainability.

The refactoring of token support check into a utility function enhances code reusability and maintainability.


440-459: Verify recipient type assignment logic.

The recipient type handling has been updated to handle empty addresses correctly. However, we should verify that this change doesn't affect existing functionality.

✅ Verification successful

Let me gather more context about the GeneralRecipientInput component and its usage.


The recipient type handling is correctly implemented and safe.

The code inspection reveals that the recipient type logic in Initial.view.tsx aligns perfectly with the GeneralRecipientInput component's implementation. The component internally manages recipient types through a robust validation system, and the parent component's handling of empty addresses by setting type to 'address' is the correct fallback behavior. The update logic properly propagates the type from the GeneralRecipientInput's validation results in all other cases.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any other components that might be affected by the recipient type changes

# Search for other components using GeneralRecipientInput
ast-grep --pattern 'onUpdate={$_} // inside GeneralRecipientInput'

# Search for direct recipient type assignments
rg "setRecipientType\(" -A 2

Length of output: 567


Script:

#!/bin/bash
# Search for GeneralRecipientInput component definition and usage
ast-grep --pattern 'type GeneralRecipientUpdate = {
  $$$
}'

# Search for GeneralRecipientInput component implementations
ast-grep --pattern 'const GeneralRecipientInput = $_'

# Find all usages of GeneralRecipientInput
rg "GeneralRecipientInput" -A 3

Length of output: 12419

src/components/Offramp/Confirm.view.tsx (1)

20-21: LGTM: Improved bank account display consistency

The addition of formatBankAccountDisplay utility function centralizes the formatting logic, ensuring consistent display of bank account numbers across the application.

Also applies to: 692-692

@@ -0,0 +1,32 @@
import { areTokenAddressesEqual } from '@/utils/'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Import path should be updated to reference the correct file

The areTokenAddressesEqual function is defined in src/utils/general.utils.ts, but it's being imported from @/utils/. Update the import statement to:

import { areTokenAddressesEqual } from '@/utils/general.utils'
🔗 Analysis chain

Verify the imported utility function exists

Let's ensure the imported areTokenAddressesEqual function is properly defined in the utils directory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the definition of areTokenAddressesEqual function
ast-grep --pattern 'export function areTokenAddressesEqual($_) {
  $$$
}'

# Search for any file exporting this function
rg -l "export.*areTokenAddressesEqual"

Length of output: 143


Script:

#!/bin/bash
# Let's verify the actual implementation in general.utils.ts
rg "areTokenAddressesEqual" src/utils/general.utils.ts -A 5

Length of output: 937

Comment on lines +3 to +22
export const checkTokenSupportsXChain = (
tokenAddress: string,
chainId: string,
supportedSquidChainsAndTokens: Record<string, any>
): boolean => {
// For native tokens, check if the chain supports SQUID_ETH_ADDRESS
if (tokenAddress === '0x0000000000000000000000000000000000000000') {
return (
supportedSquidChainsAndTokens[chainId]?.tokens.some((token: any) =>
areTokenAddressesEqual(token.address, SQUID_ETH_ADDRESS)
) ?? false
)
}

return (
supportedSquidChainsAndTokens[chainId]?.tokens.some((token: any) =>
areTokenAddressesEqual(token.address, tokenAddress)
) ?? false
)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety and code maintainability

Several improvements could enhance the robustness of this function:

  1. Replace any types with proper interfaces:
-supportedSquidChainsAndTokens: Record<string, any>
+interface TokenInfo {
+  address: string;
+  // add other token properties
+}
+interface ChainTokens {
+  tokens: TokenInfo[];
+  // add other chain properties
+}
+supportedSquidChainsAndTokens: Record<string, ChainTokens>
  1. Extract magic strings to named constants:
+const NATIVE_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000'
-if (tokenAddress === '0x0000000000000000000000000000000000000000') {
+if (tokenAddress === NATIVE_TOKEN_ADDRESS) {
  1. Simplify the nested logic:
-return (
-    supportedSquidChainsAndTokens[chainId]?.tokens.some((token: any) =>
-        areTokenAddressesEqual(token.address, tokenAddress)
-    ) ?? false
-)
+const chainTokens = supportedSquidChainsAndTokens[chainId]?.tokens ?? [];
+return chainTokens.some((token: TokenInfo) =>
+    areTokenAddressesEqual(token.address, tokenAddress)
+);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +768 to +783
{offrampType === OfframpType.CASHOUT
? utils.formatTokenAmount(parseFloat(usdValue ?? '0'))
: tokenPrice && claimLinkData
? utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount))
: ''}{' '}
<MoreInfo
text={`This is the total amount before the ${
user?.accounts.find(
(account) =>
account.account_identifier.replaceAll(/\s/g, '').toLowerCase() ===
offrampForm.recipient.replaceAll(/\s/g, '').toLowerCase()
)?.account_type === 'iban'
? '$1 SEPA'
: '$0.50 ACH'
} fee is deducted.`}
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor fee calculation logic

The current implementation has several maintainability concerns:

  1. Complex nested ternary expressions
  2. Repeated account type checks
  3. Hardcoded fee values

Consider extracting the fee calculation logic into a separate utility function:

type AccountType = 'iban' | 'ach';
type OfframpFees = {
  [key in AccountType]: number;
};

const OFFRAMP_FEES: OfframpFees = {
  iban: 1,
  ach: 0.5
};

const calculateFinalAmount = (
  amount: number,
  accountType: AccountType,
  offrampType: OfframpType,
  tokenPrice?: number,
  claimLinkData?: any
): number => {
  const fee = OFFRAMP_FEES[accountType];
  const baseAmount = offrampType === OfframpType.CASHOUT
    ? amount
    : tokenPrice && claimLinkData
      ? tokenPrice * parseFloat(claimLinkData.tokenAmount)
      : 0;
  return baseAmount - fee;
};

Then use it in the component:

-                                    {user?.accounts.find(
-                                        (account) =>
-                                            account.account_identifier.replaceAll(/\s/g, '').toLowerCase() ===
-                                            offrampForm.recipient.replaceAll(/\s/g, '').toLowerCase()
-                                    )?.account_type === 'iban'
-                                        ? offrampType == OfframpType.CASHOUT
-                                            ? utils.formatTokenAmount(parseFloat(usdValue ?? tokenValue ?? '') - 1)
-                                            : tokenPrice &&
-                                              claimLinkData &&
-                                              utils.formatTokenAmount(
-                                                  tokenPrice * parseFloat(claimLinkData.tokenAmount) - 1
-                                              )
+                                    {utils.formatTokenAmount(
+                                        calculateFinalAmount(
+                                            parseFloat(usdValue ?? '0'),
+                                            account?.account_type as AccountType,
+                                            offrampType,
+                                            tokenPrice,
+                                            claimLinkData
+                                        )
+                                    )}

Also applies to: 787-792

@Hugo0
Copy link
Copy Markdown
Contributor Author

Hugo0 commented Nov 26, 2024

I think there's still a bunch of stuff to improve here. Don't have time to tackle all of these, but:

  • we should reuse confirm / success screens between link offramp and self-cashout. we need DRY here
  • Have a lot of separate consts in different files. Need to reuse & consolidate here too
  • too much state in link claim view
  • need to split up claiming components into 4 big buttons that lead to different flows (bank, address/ens, CEX etc)

Copy link
Copy Markdown
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 (3)
.github/workflows/prettier.yml (3)

4-4: LGTM! Consider adding paths-ignore for efficiency.

Good change to run on pull requests instead of pushes. This ensures code formatting is checked before merging.

Consider adding paths-ignore to skip unnecessary checks:

 pull_request:
     branches: ['**']
+    paths-ignore:
+      - '**/*.md'
+      - 'assets/**'
+      - '.gitignore'

24-27: Enhance file listing for better visibility.

While listing files is helpful for debugging, the current implementation could be improved.

Consider these improvements:

 - name: List files to be checked
   run: |
     echo "Files to be checked:"
-    find . -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" -o -name "*.md" -o -name "*.json" -o -name "*.css" -o -name "*.scss" -o -name "*.yaml" -o -name "*.yml" \) -not -path "./node_modules/*" -not -path "./.next/*"
+    pnpm exec prettier --config prettier.config.js --list-different "**/*.{js,jsx,ts,tsx,md,json,css,scss,yaml,yml}" || true

This change:

  1. Uses Prettier's built-in file listing
  2. Respects .prettierignore configurations
  3. Shows only files that would be processed

30-36: Improve error reporting with a summary.

The error messaging is good, but we can make it even more helpful.

Consider enhancing the error reporting:

 run: |
   echo "Running Prettier check..."
-  pnpm exec prettier --config prettier.config.js --check "**/*.{js,jsx,ts,tsx,md,json,css,scss,yaml,yml}" || (
+  pnpm exec prettier --config prettier.config.js --check "**/*.{js,jsx,ts,tsx,md,json,css,scss,yaml,yml}" > prettier-results.txt || (
+    echo "----------------------------------------"
+    echo "Summary of formatting issues:"
+    cat prettier-results.txt | grep "Code style issues" || true
+    echo "----------------------------------------"
     echo "❌ Prettier found style issues in the above files"
     echo "To fix these issues, run: pnpm format"
     exit 1
   )

This change:

  1. Captures Prettier output for better processing
  2. Adds a clear summary section
  3. Makes the output more readable in GitHub Actions logs
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8210232 and bb57823.

📒 Files selected for processing (2)
  • .github/workflows/prettier.yml (2 hunks)
  • prettier.config.js (0 hunks)
💤 Files with no reviewable changes (1)
  • prettier.config.js

semi: false,
singleQuote: true,
printWidth: 120,
importOrder: [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeeeeessss

Copy link
Copy Markdown
Contributor

@jjramirezn jjramirezn left a comment

Choose a reason for hiding this comment

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

Changes seem good, lots of complexity around cashout

One comment about the ValidatedInput please review it before merging.

const newValue = e.target.value
onUpdate({ value: newValue, isValid: false, isChanging: !!newValue })
const rawValue = e.target.value
const sanitizedValue = sanitizeBankAccount(rawValue)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this here, this component is used not only for bank accounts

.env.example Outdated
export NEXT_PUBLIC_WC_PROJECT_ID="6b12debabfec7d3971d2befb5a2e36fd"
export NEXT_PUBLIC_SENTRY_DSN="https://d2f7d5e16a3c2cecdd301c7f94e440d0@o4505827429187584.ingest.sentry.io/4505827431415808"
export NEXT_PUBLIC_RECAPTCHA_SITE_KEY="6Ldio2kpAAAAAAWJiNHbgv1yCzsROdxU3_Py85rZ"
export NEXT_PUBLIC_INFURA_API_KEY="051209b4dbce4a98bfe8b424c6b21417"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we publish these?

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