Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Develop SYNC #672

Open
wants to merge 136 commits into
base: peanut-wallet
Choose a base branch
from
Open

Develop SYNC #672

wants to merge 136 commits into from

Conversation

Hugo0
Copy link
Contributor

@Hugo0 Hugo0 commented Feb 6, 2025

PR just to check the diff

Summary by CodeRabbit

  • New Features

    • Launched a dedicated payment page with updated routing and dynamic page details for a refined experience.
    • Introduced a TryNow component showcasing various cryptocurrency actions.
    • Added a FeeDescription component for detailed transaction fee information.
    • Implemented a new InfoRow component for structured display of information.
    • Added a WrongPasswordClaimLink component to enhance user feedback on password validation issues.
    • Introduced a new generateMetadata function for dynamic metadata generation across pages.
    • Added a new robots function to manage web crawler access to site paths.
    • Implemented a new generateSitemap function for improved SEO through sitemap generation.
    • Added a ValidatedInput component with enhanced layout and loading indicators.
    • Introduced a new MaintenanceBanner component to manage maintenance notifications.
  • Improvements

    • Enhanced fee displays and clear error notifications, including improved feedback for claim password issues.
    • Upgraded file upload validation for smoother document handling.
    • Consolidated footer links with updated Terms & Privacy and Jobs pages.
    • Streamlined metadata generation across various pages for improved SEO and social media sharing.
    • Improved handling of transaction fees and slippage calculations for better user experience.
    • Increased polling frequency for KYC status checks in the iframe wrapper.
    • Enhanced blockchain network configuration with dynamic Infura API URLs for specific chains.
    • Improved error handling and response clarity in external account creation and user account addition APIs.
  • Style

    • Adjusted color themes for a more cohesive and modern look.
    • Modified layout structures for better spacing and user experience.

kushagrasarathe and others added 30 commits December 17, 2024 00:41
[TASK-7742] update: merge t&c and privacy links in footer
- replaced `seo.config.ts` with new `metadata.ts` in `/app` dir
- removed privacy and terms metadata
- updated robots and sitemap config to remove terms, privacy and blog page
[TASK-7729] fix: remove allowedDomains from safe connector
[TASK-6560] + [TASK-6688]  fix: update formatAmount function
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/config/wagmi.config.tsx (3)

33-36: Consider network ordering impact.

The network array is well-typed, but since mainnet is set as the default network (line 48), consider moving it to the first position in the array for consistency.

-export const networks = [arbitrum, mainnet, optimism, polygon, gnosis, base, scroll, mantle, bsc] as [
+export const networks = [mainnet, arbitrum, optimism, polygon, gnosis, base, scroll, mantle, bsc] as [

46-62: Verify default network selection.

The configuration looks good, but please verify that mainnet as the default network aligns with your application's requirements, especially for development and testing environments.

Consider making the default network configurable through environment variables to support different environments (development, staging, production).


64-79: LGTM! Well-documented cookie handling.

The cookie-based state initialization is well-implemented and documented. However, consider adding type information for the cookies parameter in the JSDoc.

     /**
      * converts the provided cookies into an initial state for the application.
      *
      * @param {Config} wagmiConfig - The configuration object for the wagmi adapter.
-     * @param {Record<string, string>} cookies - An object representing the cookies.
+     * @param {string | null} cookies - The cookie string or null if no cookies are present.
      * @returns {InitialState} The initial state derived from the cookies.
      */
src/config/peanut.config.tsx (1)

4-16: Consider reorganizing imports for better maintainability.

Group imports by type (external libraries, components, styles, configs) for better organization:

'use client'
import * as config from '@/config'
import peanut from '@squirrel-labs/peanut-sdk'
+// External libraries
import { Analytics } from '@vercel/analytics/react'
import countries from 'i18n-iso-countries'
import enLocale from 'i18n-iso-countries/langs/en.json'
import { useEffect } from 'react'
import ReactGA from 'react-ga4'
import { ChakraProvider } from '@chakra-ui/react'

+// Styles
import * as styles from '@/styles/theme'
import 'react-tooltip/dist/react-tooltip.css'

+// Configs
import '../../sentry.client.config'
import '../../sentry.edge.config'
import '../../sentry.server.config'
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4c38e and 41019cb.

📒 Files selected for processing (2)
  • src/config/peanut.config.tsx (2 hunks)
  • src/config/wagmi.config.tsx (3 hunks)
🔇 Additional comments (5)
src/config/wagmi.config.tsx (2)

3-14: LGTM! Well-organized network imports.

The network imports are properly structured and all imported networks are utilized in the configuration.


38-43: LGTM! Proper WagmiAdapter configuration.

The adapter is correctly configured with all necessary properties and SSR support for Next.js.

src/config/peanut.config.tsx (3)

18-18: LGTM! Clean component structure.

The component follows React best practices with proper TypeScript type definitions.


19-24:

❓ Verification inconclusive

Verify analytics configuration and environment-specific settings.

  1. Ensure the Google Analytics environment variable is properly set
  2. Consider making verbose mode development-only
useEffect(() => {
    ReactGA.initialize(process.env.NEXT_PUBLIC_GA_KEY ?? '')
-   peanut.toggleVerbose(true)
+   if (process.env.NODE_ENV === 'development') {
+     peanut.toggleVerbose(true)
+   }
    countries.registerLocale(enLocale)
}, [])

🏁 Script executed:

#!/bin/bash
# Check if GA environment variable is defined in relevant config files
echo "Checking for GA environment variable..."
fd -t f "env" -x grep -l "NEXT_PUBLIC_GA_KEY"

Length of output: 132


Analytics and Environment Configuration Verification

  • Ensure that the GA environment variable (NEXT_PUBLIC_GA_KEY) is defined in your production configuration (e.g., in an appropriate .env file or deployment settings). Our initial file search did not return any files containing the key, so please double-check that it’s properly set in your production environment.
  • It's good that the peanut.toggleVerbose(true) call is now wrapped in a development-only condition (if (process.env.NODE_ENV === 'development')).
  • If LogRocket is no longer needed, consider removing the commented initialization line to clean up the code.
 useEffect(() => {
     ReactGA.initialize(process.env.NEXT_PUBLIC_GA_KEY ?? '')
-    peanut.toggleVerbose(true)
+    if (process.env.NODE_ENV === 'development') {
+      peanut.toggleVerbose(true)
+    }
     countries.registerLocale(enLocale)
 }, [])

29-32:

❓ Verification inconclusive

Verify the impact of setting cookies prop to null.

The change to set cookies={null} might affect functionality if the application relies on cookie-based features.


🏁 Script executed:

#!/bin/bash
# Search for cookie-related functionality in the codebase
echo "Checking for cookie usage..."
rg -l "cookies?" --type ts --type tsx

Length of output: 134


Action: Verify Cookies Prop Impact

The update sets cookies={null} in the config.ContextProvider on lines 29–32 of src/config/peanut.config.tsx. A previous attempt to search for cookie-related usage using ripgrep encountered file type errors, so we re-ran a command using glob patterns. As the results are still inconclusive, please manually verify that no parts of the application rely on a valid (non-null) cookies prop. Pay special attention to any functionality that might use cookies for session management or configuration.

  • Check any components or hooks that might expect a cookie value.
  • Confirm that the null assignment won’t disrupt cookie-dependent functionalities.

🏁 Script executed:

#!/bin/bash
echo "Re-checking cookie usage in TS and TSX files using glob patterns..."
rg -l 'cookies=' -g "*.tsx" -g "*.ts"

Length of output: 211


Verify impact of setting cookies prop to null

We've re-run the search and confirmed that the only occurrence of the cookies prop is in src/config/peanut.config.tsx. This suggests that there aren’t other direct references to cookie-based functionality in the broader codebase. However, please manually verify that the config.ContextProvider implementation safely handles a null value for cookies, especially if downstream components or session-related features might rely on a valid cookie object.

  • Confirm that any logic within ContextProvider and its consumers properly accounts for cookies={null}
  • Double-check session or cookie-dependent functionality, if applicable

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/components/Global/ValidatedInput/index.tsx (2)

113-118: Simplify the boolean coercion in className.

The double negation (!!infoText) is redundant as the value will already be coerced to a boolean in this context.

-                        !!infoText ? 'pl-8' : 'pl-4'
+                        infoText ? 'pl-8' : 'pl-4'
🧰 Tools
🪛 Biome (1.9.4)

[error] 117-117: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


140-144: Consider simplifying the opacity transition logic.

The current opacity transition logic could be simplified for better readability.

-                        className={`h-full ${
-                            isValidating
-                                ? 'opacity-100'
-                                : 'opacity-100 transition-opacity hover:opacity-100 md:opacity-0'
-                        }`}
+                        className="h-full transition-opacity md:opacity-0 md:hover:opacity-100"
+                        style={{ opacity: isValidating ? 1 : undefined }}
src/components/Claim/Link/Initial.view.tsx (3)

63-66: Consider using a custom hook for error state management.

The error state management could be abstracted into a custom hook to improve reusability and reduce boilerplate code across the component.

-const [errorState, setErrorState] = useState<{
-    showError: boolean
-    errorMessage: string
-}>({ showError: false, errorMessage: '' })
+const useErrorState = () => {
+  const [error, setError] = useState<{
+    showError: boolean
+    errorMessage: string
+  }>({ showError: false, errorMessage: '' })
+
+  const clearError = () => setError({ showError: false, errorMessage: '' })
+  const setErrorMessage = (message: string) => 
+    setError({ showError: true, errorMessage: message })
+
+  return { error, clearError, setErrorMessage }
+}

326-387: Add retry logic for route fetching.

The route fetching function could fail due to temporary network issues. Consider implementing a retry mechanism with exponential backoff.

 const fetchRoute = async (toToken?: string, toChain?: string) => {
+    const MAX_RETRIES = 3;
+    const INITIAL_DELAY = 1000;
+    let retryCount = 0;
+
+    const fetchWithRetry = async () => {
     try {
         const existingRoute = routes.find(
             (route) =>
                 route.fromChain === claimLinkData.chainId &&
                 route.fromToken.toLowerCase() === claimLinkData.tokenAddress.toLowerCase() &&
                 route.toChain === (toChain || selectedChainID) &&
                 areTokenAddressesEqual(route.toToken, toToken || selectedTokenAddress)
         )

         if (existingRoute) {
             setSelectedRoute(existingRoute)
             return existingRoute
         } else if (!isXChain && !toToken && !toChain) {
             setHasFetchedRoute(false)
             return undefined
         }

         // ... rest of the function ...

     } catch (error) {
+        if (retryCount < MAX_RETRIES) {
+            retryCount++;
+            await new Promise(resolve => 
+                setTimeout(resolve, INITIAL_DELAY * Math.pow(2, retryCount - 1))
+            );
+            return fetchWithRetry();
+        }
         console.error('Error fetching route:', error)
         if (!toToken && !toChain) {
             setSelectedRoute(undefined)
         }
         setErrorState({
             showError: true,
-            errorMessage: 'No route found for the given token pair.',
+            errorMessage: 'Unable to find a route. Please try again or select a different token pair.',
         })
         return undefined
     }
+    }
+    
+    return fetchWithRetry();
 }

434-443: Enhance accessibility for the attachment download link.

The download link could benefit from improved accessibility attributes.

 <a
     href={attachment.attachmentUrl}
     download
     target="_blank"
+    rel="noopener noreferrer"
+    aria-label="Download attachment"
     className="flex w-full cursor-pointer flex-row items-center justify-center gap-1 text-h9 font-normal text-gray-1 underline "
 >
     <Icon name={'download'} />
-    Download attachment
+    <span>Download attachment</span>
 </a>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 41019cb and 592d63f.

📒 Files selected for processing (3)
  • src/components/Claim/Link/Initial.view.tsx (8 hunks)
  • src/components/Claim/Link/Onchain/Confirm.view.tsx (5 hunks)
  • src/components/Global/ValidatedInput/index.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Claim/Link/Onchain/Confirm.view.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Global/ValidatedInput/index.tsx

[error] 117-117: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🔇 Additional comments (3)
src/components/Global/ValidatedInput/index.tsx (2)

1-20: LGTM! Good improvements to the type definitions.

The changes to make the label prop optional and the addition of necessary imports enhance the component's flexibility.


28-93: LGTM! Well-structured component implementation.

The component demonstrates good practices:

  • Efficient debouncing of validation
  • Proper cleanup in useEffect
  • Comprehensive error handling
  • Clean state management
src/components/Claim/Link/Initial.view.tsx (1)

39-673: Implementation looks solid!

The component has been well-structured with good error handling, cross-chain support, and user feedback. The changes improve the overall functionality while maintaining good code organization.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/components/Global/ValidatedInput/index.tsx (1)

113-118: Remove redundant double negation.

The double negation in !!infoText is unnecessary as the value will already be coerced to a boolean.

-                        !!infoText ? 'pl-8' : 'pl-4'
+                        infoText ? 'pl-8' : 'pl-4'
🧰 Tools
🪛 Biome (1.9.4)

[error] 117-117: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 592d63f and 267d25e.

📒 Files selected for processing (1)
  • src/components/Global/ValidatedInput/index.tsx (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Global/ValidatedInput/index.tsx

[error] 117-117: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

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

1-20: LGTM! Good improvements to imports and type definitions.

The changes enhance flexibility by making the label optional, and the new imports support the component's functionality.


42-92: LGTM! Robust validation and state management implementation.

The component implements proper debouncing, error handling, and state cleanup. The validation logic is well-structured with appropriate user feedback.


94-162: LGTM! Improved UI structure and feedback.

The changes enhance the component's UI through:

  • Better layout using flex containers
  • Improved loading state visibility
  • Enhanced close button positioning
🧰 Tools
🪛 Biome (1.9.4)

[error] 117-117: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/config/routesUnderMaintenance.ts (2)

3-6: Add JSDoc documentation for the maintenanceTime property.

Please document the expected date format and any validation requirements for the maintenance window.

 interface MaintenanceConfig {
     routes: string[]
+    /**
+     * Optional maintenance window configuration.
+     * @property {string} start - Start time in ISO 8601 format (e.g., '2025-02-20T11:10:00Z')
+     * @property {string} end - End time in ISO 8601 format (e.g., '2025-02-20T17:30:00Z')
+     */
     maintenanceTime?: {
         start: string
         end: string
     }
 }

24-27: Consider adding runtime validation for maintenance times.

The maintenance window configuration should validate that:

  1. Dates are in valid ISO 8601 format
  2. Start time is before end time
  3. Dates are not too far in the future (to prevent stale configurations)

Consider adding a validation function:

function validateMaintenanceWindow(start: string, end: string): void {
  const startDate = new Date(start);
  const endDate = new Date(end);
  
  if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
    throw new Error('Invalid date format. Use ISO 8601 format (e.g., 2025-02-20T11:10:00Z)');
  }
  
  if (startDate >= endDate) {
    throw new Error('Maintenance start time must be before end time');
  }
  
  const maxFutureDate = new Date();
  maxFutureDate.setMonth(maxFutureDate.getMonth() + 3); // Example: max 3 months ahead
  
  if (startDate > maxFutureDate) {
    throw new Error('Maintenance window cannot be scheduled too far in the future');
  }
}
src/components/Global/Banner/MaintenanceBanner.tsx (2)

11-17: Extract time window check into a reusable utility function.

The maintenance window check logic could be reused elsewhere. Consider extracting it into a utility function.

+// In a new file: src/utils/maintenance.ts
+export function isWithinMaintenanceWindow(config: MaintenanceConfig): boolean {
+  const now = Date.now();
+  const startTime = config.maintenanceTime ? new Date(config.maintenanceTime.start).getTime() : null;
+  const endTime = config.maintenanceTime ? new Date(config.maintenanceTime.end).getTime() : null;
+  
+  return Boolean(config.maintenanceTime && startTime && endTime && now >= startTime && now <= endTime);
+}

 // In MaintenanceBanner.tsx
-// Check if we're within the maintenance window
-const now = Date.now() // Current time in milliseconds
-const startTime = config.maintenanceTime ? new Date(config.maintenanceTime.start).getTime() : null
-const endTime = config.maintenanceTime ? new Date(config.maintenanceTime.end).getTime() : null
-
-const isWithinMaintenanceWindow =
-    config.maintenanceTime && startTime && endTime && now >= startTime && now <= endTime
+const isWithinMaintenanceWindow = isWithinMaintenanceWindow(config)

23-26: Remove unused function and enhance maintenance message.

The formatDate function is defined but never used. Also, consider making the maintenance message more informative by including the time window.

-    const formatDate = (timestamp: number) => new Date(timestamp).toUTCString()
-    const maintenanceMessage = config.maintenanceTime
-        ? `Scheduled maintenance. Some features might be unavailable.`
-        : 'Under maintenance'
+    const maintenanceMessage = config.maintenanceTime
+        ? `Scheduled maintenance from ${new Date(config.maintenanceTime.start).toUTCString()} to ${new Date(config.maintenanceTime.end).toUTCString()}. Some features might be unavailable.`
+        : 'Under maintenance'
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 267d25e and bd46702.

📒 Files selected for processing (2)
  • src/components/Global/Banner/MaintenanceBanner.tsx (1 hunks)
  • src/config/routesUnderMaintenance.ts (2 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (10)
src/app/api/peanut/user/add-account/route.ts (2)

35-45: Consider handling other common error status codes.

While the addition of specific handling for 409 is good, consider extending this pattern to other common error status codes (e.g., 401 for unauthorized, 403 for forbidden) for more comprehensive error handling.

if (!response.ok) {
    if (response.status === 409) {
        return new NextResponse(JSON.stringify({ error: 'User already exists' }), {
            status: 409,
            headers: {
                'Content-Type': 'application/json',
            },
        })
    }
+   if (response.status === 401) {
+       return new NextResponse(JSON.stringify({ error: 'Unauthorized access' }), {
+           status: 401,
+           headers: {
+               'Content-Type': 'application/json',
+           },
+       })
+   }
+   if (response.status === 403) {
+       return new NextResponse(JSON.stringify({ error: 'Forbidden access' }), {
+           status: 403,
+           headers: {
+               'Content-Type': 'application/json',
+           },
+       })
+   }
    throw new Error(`Failed to create user: ${response.status}`)
}

44-45: Consider extracting error response text.

The error message currently only includes the status code. Consider adding the response text for more detailed error information.

- throw new Error(`Failed to create user: ${response.status}`)
+ const errorText = await response.text();
+ throw new Error(`Failed to create user: ${response.status}. ${errorText}`);
src/app/api/bridge/external-account/create-external-account/route.ts (4)

8-24: Robust JSON parsing with clear error response.
Great job returning a structured JSON response for malformed requests. This ensures clients receive informative error details. As a minor improvement, consider logging the parse error details to assist troubleshooting.


47-57: Handling missing API key.
Returning a 500 error with a clear message is appropriate, as this is a server configuration issue. Consider redacting or limiting sensitive environment details in error messages in production.


95-105: Account type validation.
This explicit check for 'iban' or 'us' is clear. For extensibility, consider enumerating possible account types in a central config or constant to reduce duplication across the codebase.


142-229: Comprehensive handling of duplicate accounts.
Well done detecting duplicate inputs, fetching existing accounts, and matching them by last 4 digits. Consider concurrency scenarios (e.g., race conditions if multiple requests come in quickly), though this is likely robust enough.

src/utils/cashout.utils.ts (2)

4-4: Combined imports.
Consolidating generateKeysFromString and getSquidRouteRaw on a single line is neat. Keep consistent import grouping to maintain readability.


245-245: Informative comment for account matching.
Explicitly mentioning the matching strategy helps future contributors maintain or extend the code.

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

188-203: Check console logs for PII.
Consider sanitizing or removing logs in production to prevent the accidental leak of sensitive user information like bridge_customer_id.


586-604: Consider a reusable KYC error component.
You replicate the reKYC link logic; extracting a small sub-component would reduce duplication.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between bd46702 and 30ad3a0.

📒 Files selected for processing (7)
  • src/app/api/bridge/external-account/create-external-account/route.ts (3 hunks)
  • src/app/api/peanut/user/add-account/route.ts (2 hunks)
  • src/components/Global/KYCComponent/index.tsx (2 hunks)
  • src/components/Global/LinkAccountComponent/index.tsx (13 hunks)
  • src/constants/cashout.consts.ts (2 hunks)
  • src/interfaces/interfaces.ts (1 hunks)
  • src/utils/cashout.utils.ts (5 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/constants/cashout.consts.ts
  • src/components/Global/KYCComponent/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Global/LinkAccountComponent/index.tsx

[error] 334-334: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🔇 Additional comments (26)
src/app/api/peanut/user/add-account/route.ts (2)

3-4: Import reorganization looks good.

The imports for NextRequest and NextResponse have been moved after the constants and cookies imports. While this is just a reorganization that doesn't affect functionality, it's a clean change.


36-43: Good addition of specific error handling for conflict scenario.

This change properly handles the 409 Conflict status code with a structured JSON response rather than throwing a generic error. This is a significant improvement as it:

  1. Returns the appropriate HTTP status code (409)
  2. Provides a clear, descriptive error message
  3. Uses proper JSON content type

This makes the API more robust and follows RESTful best practices.

src/interfaces/interfaces.ts (1)

9-15: Good addition for comprehensive error messaging.
The optional details property in the IResponse interface meaningfully encapsulates finer-grained information. This structured approach can help in providing clearer or more actionable feedback to the client.

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

1-1: Confirm usage of newly imported interface.
You’re importing IBridgeAccount from @/interfaces; verify it’s utilized somewhere below to avoid potential unused import flags in certain build configurations.


26-26: Destructure request body fields carefully.
Destructuring is fine, but ensure you handle undefined fields if future validations rely on these properties.


30-31: Fetch query param usage looks good.
Retrieving customerId via url.searchParams.get() is straightforward. Confirm for future extension if additional query parameters need stricter validation or fallback defaults.


33-43: Useful 400 error for missing customerId.
Explicitly informing the client about the missing customerId is helpful for debugging. This is consistent with your structured error responses.


112-112: Idempotency handling.
Including an Idempotency-Key is a good practice to prevent duplicate requests. Confirm the external service’s behavior aligns with your usage (e.g., does it store or purge keys over time?).


118-118: JSON parsing of the Bridge response.
Storing the parsed response in a variable ensures subsequent error checks can reference it. This approach is solid for debugging or logging.


121-140: Structured handling for invalid parameters.
Conditionally constructing a clear error message for missing parameters helps front-end developers and end users correct invalid requests faster. Good use of data.message fallback when fields are absent.


255-276: Centralized catch block.
Logging the error details and returning a 500 with structured JSON is consistent with the rest of the function’s approach to uniform error responses. Ensure sensitive error stack traces are not exposed in production.

src/utils/cashout.utils.ts (6)

2-2: Reorganized imports for clarity.
Importing interfaces earlier can help maintain a structured import style. No issues identified.


134-135: New KYCStatus type.
Explicitly enumerating valid KYC states makes the code more self-documenting. This approach is flexible enough to accommodate future states.


143-143: Seamless KYC status integration.
Refactoring kyc_status to use KYCStatus ensures consistency and avoids magic strings throughout the code.


224-225: Storing parsed response in local variable.
Clear step to separate fetch operations from error handling. This approach is easier to debug than inline parsing.


228-229: Duplicate account detection logic.
You’re checking responseData.code === 'duplicate_external_account' to handle pre-existing accounts gracefully. This parallels the logic on the server side, providing consistent UX.


278-278: Returning response data as final payload.
Exposing the raw response data can help in debugging dynamic scenarios. Confirm no sensitive fields are leaked.

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

2-2: All new imports look good.
No concerns identified here.

Also applies to: 5-7, 9-9, 12-14


49-49: New reKYCUrl state variable is well-introduced.
This addition cleanly accommodates the re-verification link.


113-113: Verify the updated KYC status check.
Confirm that user.user.kycStatus === 'approved' is indeed the correct final status, rather than 'verified' or other possible status values.


411-428: Providing an immediate reKYC link is great for UX.
This conditional error message helps users complete KYC quickly.


522-522: Layout change identified, no further feedback.


613-626: User-friendly success screen.
Displaying a direct link to the profile after successful linking improves clarity.


636-636: No issues identified.


653-653: No issues identified.


663-663: Conditional rendering of <KYCComponent /> seems correct.
This aligns nicely with requiring kycStatus === 'approved' to skip KYC.

Comment on lines +304 to +351
const createExternalAccountRes: IResponse = await utils.createExternalAccount(
customerId,
formData.type as 'iban' | 'us',
accountDetails,
// Only include address for US accounts
formData.type === 'us' ? address : undefined,
address,
accountOwnerName
)

console.log('Create external account response:', response)
// handle verification requirement first
if (!createExternalAccountRes.data.success) {
// check for verification URL

if (!response.success) {
setErrorState({
showError: true,
errorMessage: response.message || 'Failed to create external account',
})
return
}
if (createExternalAccountRes.data.details?.code === 'endorsement_requirements_not_met') {
const verificationUrl = createExternalAccountRes.data.details.requirements.kyc_with_proof_of_address

const data: IBridgeAccount = response.data
setErrorState({
showError: true,
errorMessage:
createExternalAccountRes.data?.message ||
'Please complete the verification process to continue.',
})

console.log('Creating account in database')
await utils.createAccount(
user?.user?.userId ?? '',
customerId,
data.id,
formData.type,
getIbanFormValue('accountNumber')?.replaceAll(/\s/g, '') ?? '',
address
)
if (verificationUrl) {
setReKYCUrl(verificationUrl)
}
return
}

console.log('Fetching updated user data')
await fetchUser()
const bridgeAccountId = createExternalAccountRes.data.id

onCompleted ? onCompleted() : setCompletedLinking(true)
} catch (error) {
console.error('Error in handleSubmitLinkIban:', error)
let errorMessage = 'Failed to link bank account'
if (!!bridgeAccountId) {
// add account to database
await utils.createAccount(
user.user.userId,
customerId,
bridgeAccountId,
formData.type,
accountDetails.accountNumber,
createExternalAccountRes.data
)

if (error instanceof Error) {
// Clean up error message by removing redundant "Error:" prefixes
errorMessage = error.message.replace(/^Error:\s+/g, '')
}
// re-fetch user to get recently added accounts
await fetchUser()

onCompleted ? onCompleted() : setCompletedLinking(true)
}
}
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify the double negation & consider splitting large function.
At line 334, avoid !!bridgeAccountId in favor of if (bridgeAccountId). The entire handleSubmitLinkIban method is quite large – consider extracting subfunctions for legibility and maintainability.

- if (!!bridgeAccountId) {
+ if (bridgeAccountId) {
📝 Committable suggestion

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

Suggested change
const createExternalAccountRes: IResponse = await utils.createExternalAccount(
customerId,
formData.type as 'iban' | 'us',
accountDetails,
// Only include address for US accounts
formData.type === 'us' ? address : undefined,
address,
accountOwnerName
)
console.log('Create external account response:', response)
// handle verification requirement first
if (!createExternalAccountRes.data.success) {
// check for verification URL
if (!response.success) {
setErrorState({
showError: true,
errorMessage: response.message || 'Failed to create external account',
})
return
}
if (createExternalAccountRes.data.details?.code === 'endorsement_requirements_not_met') {
const verificationUrl = createExternalAccountRes.data.details.requirements.kyc_with_proof_of_address
const data: IBridgeAccount = response.data
setErrorState({
showError: true,
errorMessage:
createExternalAccountRes.data?.message ||
'Please complete the verification process to continue.',
})
console.log('Creating account in database')
await utils.createAccount(
user?.user?.userId ?? '',
customerId,
data.id,
formData.type,
getIbanFormValue('accountNumber')?.replaceAll(/\s/g, '') ?? '',
address
)
if (verificationUrl) {
setReKYCUrl(verificationUrl)
}
return
}
console.log('Fetching updated user data')
await fetchUser()
const bridgeAccountId = createExternalAccountRes.data.id
onCompleted ? onCompleted() : setCompletedLinking(true)
} catch (error) {
console.error('Error in handleSubmitLinkIban:', error)
let errorMessage = 'Failed to link bank account'
if (!!bridgeAccountId) {
// add account to database
await utils.createAccount(
user.user.userId,
customerId,
bridgeAccountId,
formData.type,
accountDetails.accountNumber,
createExternalAccountRes.data
)
if (error instanceof Error) {
// Clean up error message by removing redundant "Error:" prefixes
errorMessage = error.message.replace(/^Error:\s+/g, '')
}
// re-fetch user to get recently added accounts
await fetchUser()
onCompleted ? onCompleted() : setCompletedLinking(true)
}
}
} catch (error) {
const createExternalAccountRes: IResponse = await utils.createExternalAccount(
customerId,
formData.type as 'iban' | 'us',
accountDetails,
address,
accountOwnerName
)
// handle verification requirement first
if (!createExternalAccountRes.data.success) {
// check for verification URL
if (createExternalAccountRes.data.details?.code === 'endorsement_requirements_not_met') {
const verificationUrl = createExternalAccountRes.data.details.requirements.kyc_with_proof_of_address
setErrorState({
showError: true,
errorMessage:
createExternalAccountRes.data?.message ||
'Please complete the verification process to continue.',
})
if (verificationUrl) {
setReKYCUrl(verificationUrl)
}
return
}
const bridgeAccountId = createExternalAccountRes.data.id
if (bridgeAccountId) {
// add account to database
await utils.createAccount(
user.user.userId,
customerId,
bridgeAccountId,
formData.type,
accountDetails.accountNumber,
createExternalAccountRes.data
)
// re-fetch user to get recently added accounts
await fetchUser()
onCompleted ? onCompleted() : setCompletedLinking(true)
}
}
} catch (error) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 334-334: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

jjramirezn and others added 2 commits February 26, 2025 07:44
[TASK-9242] fix: zero balance shown on token selector when balance is being fetched
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.

5 participants