Skip to content

Comments

feat/PRO-3384/fee-capure-the-exchange#369

Merged
RanaBug merged 2 commits intostagingfrom
feat/PRO-3384/fee-capure-the-exchange
Jul 14, 2025
Merged

feat/PRO-3384/fee-capure-the-exchange#369
RanaBug merged 2 commits intostagingfrom
feat/PRO-3384/fee-capure-the-exchange

Conversation

@RanaBug
Copy link
Collaborator

@RanaBug RanaBug commented Jul 11, 2025

Description

  • Fee capture implementation: Stable Coins --> Stable Coins Fee / Gas Token ---> Gas Token Fee / ERC20 (non stable) --> Gas Token Fee

How Has This Been Tested?

  • Unit Tests ans Manual testing

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Summary by CodeRabbit

  • New Features

    • Swap offers now display estimated fee information, including amount, token symbol, and any relevant warnings.
    • Fee deduction and payment are integrated into the swap process, with detailed handling for native tokens, stablecoins, and other ERC20 tokens.
  • Bug Fixes

    • Improved error handling and user feedback when fee estimation or transaction preparation fails.
  • Tests

    • Added tests to verify correct fee display and error scenarios during swap preparation.
  • Chores

    • Introduced utilities for identifying stablecoins and processing fee transactions across multiple blockchains.
    • Expanded recognized native token addresses for enhanced token handling.

@RanaBug RanaBug requested a review from IAmKio July 11, 2025 16:29
@RanaBug RanaBug self-assigned this Jul 11, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 11, 2025

Walkthrough

Fee estimation and deduction are integrated into the swap flow, with 1% of the input token amount deducted and routed to a configured fee receiver. The UI now displays fee details and warnings, and error handling is improved. Supporting utilities for fee detection, stablecoin checks, and transaction decoding are added, alongside comprehensive tests.

Changes

File(s) Change Summary
src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx Integrates fee estimation and display into the swap UI; adds fee info state, async fetching, and error handling.
src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx Adds tests for fee display and error scenarios, mocks fee-related logic, and sets fee receiver env variable.
src/apps/the-exchange/hooks/useOffer.tsx Deducts 1% fee, adds fee payment step logic, validates balances, and exports new utility functions.
src/apps/the-exchange/utils/blockchain.ts Adds utilities for detecting fee transactions, extracting fee amounts/symbols, and mapping native symbols.
src/utils/blockchain.ts Adds stablecoin address mapping and isStableCoin function for chain-specific checks.
src/apps/the-exchange/utils/wrappedTokens.ts Adds a new native token address to the recognized native token addresses set.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ExchangeAction (UI)
    participant useOffer (Hook)
    participant LiFiSDK
    participant Blockchain

    User->>ExchangeAction (UI): Initiate swap
    ExchangeAction (UI)->>useOffer (Hook): fetchFeeInfo()
    useOffer (Hook)->>LiFiSDK: getStepTransactions (with 1% fee deduction)
    LiFiSDK->>useOffer (Hook): Returns step transactions (includes fee step)
    useOffer (Hook)->>ExchangeAction (UI): Return fee info
    ExchangeAction (UI)->>User: Display fee and warning (if any)
    User->>ExchangeAction (UI): Confirm swap
    ExchangeAction (UI)->>useOffer (Hook): getStepTransactions (on swap)
    useOffer (Hook)->>Blockchain: Execute fee payment & swap steps
    Blockchain-->>ExchangeAction (UI): Transaction result
Loading

Possibly related PRs

Suggested reviewers

  • IAmKio
  • vignesha22

Poem

In the warren where swaps are made,
A clever fee now joins the trade.
One percent hops to the fee receiver,
With warnings clear for every believer.
Tokens and balances checked with care—
Swapping smarter, everywhere!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-14T09_45_47_814Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai 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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jul 11, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: 07c48f3
Status: ✅  Deploy successful!
Preview URL: https://61962636.x-e62.pages.dev
Branch Preview URL: https://feat-pro-3384-fee-capure-the.x-e62.pages.dev

View logs

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

🧹 Nitpick comments (4)
src/utils/blockchain.ts (1)

312-347: Add documentation for STABLECOIN_ADDRESSES

Please add JSDoc comments to document the purpose and structure of this constant for better maintainability.

+/**
+ * Mapping of chain IDs to sets of stablecoin contract addresses.
+ * Used to identify stablecoin tokens for fee payment logic.
+ * Addresses are stored in lowercase for case-insensitive comparison.
+ */
 const STABLECOIN_ADDRESSES: Record<number, Set<string>> = {
src/apps/the-exchange/utils/blockchain.ts (1)

44-56: Extract ERC20 transfer method signature as a constant

The hard-coded method signature should be defined as a constant for better maintainability.

+// ERC20 transfer(address,uint256) method signature
+const ERC20_TRANSFER_SIGNATURE = '0xa9059cbb';
+
 // Helper: Detect if a tx is an ERC20 (stablecoin) fee step
 export const isERC20FeeTx = (
   tx: StepTransaction,
   swapToken: Token
 ): boolean => {
   return (
     typeof tx.to === 'string' &&
     typeof swapToken.contract === 'string' &&
     tx.to.toLowerCase() === swapToken.contract.toLowerCase() &&
     tx.value === BigInt(0) &&
     typeof tx.data === 'string' &&
-    tx.data.startsWith('0xa9059cbb')
+    tx.data.startsWith(ERC20_TRANSFER_SIGNATURE)
   );
 };
src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx (1)

183-195: Extract native balance fetching to reduce code duplication

The native balance extraction logic is duplicated between fetchFeeInfo and onClickToExchange.

+const getNativeBalanceForChain = (
+  walletPortfolio: PortfolioData | undefined,
+  chainId: number
+) => {
+  return getNativeBalanceFromPortfolio(
+    walletPortfolio
+      ? convertPortfolioAPIResponseToToken(walletPortfolio)
+      : undefined,
+    chainId
+  );
+};

 // In onClickToExchange:
-      const nativeBalance = getNativeBalanceFromPortfolio(
-        walletPortfolio
-          ? convertPortfolioAPIResponseToToken(walletPortfolio)
-          : undefined,
-        bestOffer.offer.fromChainId
-      );
+      const nativeBalance = getNativeBalanceForChain(
+        walletPortfolio,
+        bestOffer.offer.fromChainId
+      );
src/apps/the-exchange/hooks/useOffer.tsx (1)

53-58: Extract native token addresses as constants

The hardcoded native token addresses should be defined as constants for better maintainability.

+const NATIVE_TOKEN_ADDRESSES = [
+  '0x0000000000000000000000000000000000000000',
+  '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
+] as const;
+
 export const getNativeBalanceFromPortfolio = (
   walletPortfolio: Token[] | undefined,
   chainId: number
 ): string | undefined => {
   if (!walletPortfolio) return undefined;
   // Find the native token for the chain (by contract address)
   const nativeToken = walletPortfolio.find(
     (token) =>
       chainNameToChainIdTokensData(token.blockchain) === chainId &&
-      (token.contract === '0x0000000000000000000000000000000000000000' ||
-        token.contract === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee')
+      NATIVE_TOKEN_ADDRESSES.includes(token.contract.toLowerCase())
   );
   return nativeToken ? String(nativeToken.balance) : undefined;
 };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d4fc3ad and 204a694.

⛔ Files ignored due to path filters (1)
  • src/apps/the-exchange/components/ExchangeAction/test/__snapshots__/ExchangeAction.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx (8 hunks)
  • src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (3 hunks)
  • src/apps/the-exchange/hooks/useOffer.tsx (4 hunks)
  • src/apps/the-exchange/utils/blockchain.ts (2 hunks)
  • src/utils/blockchain.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
src/utils/blockchain.ts (1)
Learnt from: RanaBug
PR: pillarwallet/x#315
File: src/apps/the-exchange/utils/wrappedTokens.ts:6-20
Timestamp: 2025-05-23T14:44:33.911Z
Learning: XDAI (Gnosis Chain) is intentionally excluded from the WRAPPED_NATIVE_TOKEN_ADDRESSES mapping in the exchange app's wrappedTokens utility.
src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (1)
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx (1)
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
src/apps/the-exchange/hooks/useOffer.tsx (1)
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
🧬 Code Graph Analysis (2)
src/apps/the-exchange/utils/blockchain.ts (2)
src/apps/the-exchange/utils/types.tsx (1)
  • StepTransaction (33-42)
src/services/tokensData.ts (1)
  • Token (19-29)
src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx (7)
src/hooks/useTransactionDebugLogger.tsx (1)
  • useTransactionDebugLogger (1-15)
src/apps/the-exchange/hooks/useReducerHooks.tsx (1)
  • useAppSelector (6-6)
src/types/api.ts (1)
  • PortfolioData (731-740)
src/apps/the-exchange/hooks/useOffer.tsx (1)
  • getNativeBalanceFromPortfolio (47-60)
src/services/tokensData.ts (1)
  • convertPortfolioAPIResponseToToken (97-121)
src/apps/the-exchange/utils/blockchain.ts (4)
  • isNativeFeeTx (32-41)
  • isERC20FeeTx (44-56)
  • getFeeAmount (59-82)
  • getFeeSymbol (85-94)
src/apps/the-exchange/utils/converters.ts (1)
  • formatTokenAmount (6-11)
🪛 Biome (1.9.4)
src/apps/the-exchange/hooks/useOffer.tsx

[error] 264-264: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: build
🔇 Additional comments (2)
src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (1)

222-274: Well-structured tests for fee functionality

The new tests properly cover fee display and error scenarios with appropriate mocking and assertions.

src/apps/the-exchange/hooks/useOffer.tsx (1)

226-231: Verify fee receiver address is configured

The code should validate that the fee receiver address is properly configured before using it.

     const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
+    if (!feeReceiver || !isValidEthereumAddress(feeReceiver)) {
+      throw new Error('Invalid or missing fee receiver address configuration');
+    }
     const feeAmount = BigInt(route.fromAmount) / BigInt(100); // 1%

);

useEffect(() => {
const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you let me know what this value should be please 🙏

Copy link
Collaborator

@IAmKio IAmKio left a comment

Choose a reason for hiding this comment

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

Just a few minor questions but looks good so far!

amount: '0',
symbol: swapToken.symbol,
recipient: String(feeReceiver),
warning: 'Fee transaction not found. Please check your swap setup.',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can i just check - who sees this message? If it's the user - then we need to be a bit more helpful than "check your swap setup" 🙏

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh yes you are right, this is a message to the user, I've changed it to something less techy and more useful

setFeeInfo(null);
transactionDebugLog('Fee estimation error:', e);
setErrorMessage(
'Unable to prepare the swap. Please check your wallet and try again.'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
'Unable to prepare the swap. Please check your wallet and try again.'
'Unable to prepare the swap. Please check that you have enough funds in your wallet and try again'

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've changed this to something slightly different, because we already have a check before for enough funds

(token) =>
chainNameToChainIdTokensData(token.blockchain) === chainId &&
(token.contract === '0x0000000000000000000000000000000000000000' ||
token.contract === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just wondering does this take into account Polygon POL token which i think is a different address?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch! I've added it and made the logic slighlty different with a isNative helper instead

@github-actions github-actions bot temporarily deployed to Preview (feat/PRO-3384/fee-capure-the-exchange) July 14, 2025 09:44 Inactive
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: 2

♻️ Duplicate comments (3)
src/apps/the-exchange/hooks/useOffer.tsx (3)

130-130: Make fee percentage configurable.

The 1% fee is hardcoded. Consider making it configurable via environment variable for flexibility.

+const feePercentage = Number(import.meta.env.VITE_SWAP_FEE_PERCENTAGE || '1') / 100;
-const fromAmountFeeDeducted = Number(fromAmount) * 0.99;
+const fromAmountFeeDeducted = Number(fromAmount) * (1 - feePercentage);

279-279: Use optional chaining for better code safety.

-if (nativeFeeRoute && nativeFeeRoute.toAmount) {
+if (nativeFeeRoute?.toAmount) {

294-318: Extract error messages as constants and make buffer configurable.

+const ERROR_MESSAGES = {
+  INSUFFICIENT_NATIVE_BALANCE: 'Insufficient native token balance to pay the fee. Please ensure you have enough native token to cover the fee.',
+  FEE_ESTIMATION_FAILED: 'Failed to estimate native fee for ERC20. No route found.',
+  GENERIC_FEE_ERROR: 'Failed to estimate native fee for ERC20.'
+} as const;
+
+const FEE_BUFFER_PERCENTAGE = Number(import.meta.env.VITE_FEE_BUFFER_PERCENTAGE || '10');
🧹 Nitpick comments (2)
src/apps/the-exchange/hooks/useOffer.tsx (2)

66-109: Consider adding request caching and timeout handling.

The getNativeFeeForERC20 function makes external API calls for fee estimation. Consider implementing request caching and timeout handling to improve performance and reliability.

const getNativeFeeForERC20 = async ({
  tokenAddress,
  chainId,
  feeAmount,
  slippage = 0.03,
}: {
  tokenAddress: string;
  chainId: number;
  feeAmount: string;
  slippage?: number;
}) => {
  try {
+    // Add timeout to prevent hanging requests
+    const timeoutPromise = new Promise((_, reject) => 
+      setTimeout(() => reject(new Error('Request timeout')), 10000)
+    );
+
    const feeRouteRequest: RoutesRequest = {
      fromChainId: chainId,
      toChainId: chainId,
      fromTokenAddress: tokenAddress,
      toTokenAddress: zeroAddress,
      fromAmount: feeAmount,
      options: {
        slippage,
        bridges: {
          allow: ['relay'],
        },
        exchanges: { allow: ['openocean', 'kyberswap'] },
      },
    };

-    const result = await getRoutes(feeRouteRequest);
+    const result = await Promise.race([
+      getRoutes(feeRouteRequest),
+      timeoutPromise
+    ]);

224-320: Consider performance optimization for fee calculation.

The fee logic, especially for ERC20 tokens, involves multiple external API calls which could impact user experience. Consider implementing parallel processing or caching strategies.

For better performance, consider:

  1. Caching fee estimation results for similar token amounts
  2. Pre-calculating common fee scenarios
  3. Implementing fallback fee estimation methods
  4. Using batch requests where possible
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 204a694 and 07c48f3.

📒 Files selected for processing (6)
  • src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx (8 hunks)
  • src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (5 hunks)
  • src/apps/the-exchange/hooks/useOffer.tsx (4 hunks)
  • src/apps/the-exchange/utils/blockchain.ts (2 hunks)
  • src/apps/the-exchange/utils/wrappedTokens.ts (1 hunks)
  • src/utils/blockchain.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/utils/blockchain.ts
  • src/apps/the-exchange/utils/blockchain.ts
  • src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx
🧰 Additional context used
🧠 Learnings (3)
src/apps/the-exchange/utils/wrappedTokens.ts (2)
Learnt from: RanaBug
PR: pillarwallet/x#315
File: src/apps/the-exchange/utils/wrappedTokens.ts:6-20
Timestamp: 2025-05-23T14:44:33.911Z
Learning: XDAI (Gnosis Chain) is intentionally excluded from the WRAPPED_NATIVE_TOKEN_ADDRESSES mapping in the exchange app's wrappedTokens utility.
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (1)
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
src/apps/the-exchange/hooks/useOffer.tsx (1)
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.
🧬 Code Graph Analysis (2)
src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (2)
src/store.ts (1)
  • store (65-77)
src/apps/the-exchange/reducer/theExchangeSlice.ts (2)
  • setBestOffer (85-87)
  • setSwapToken (73-75)
src/apps/the-exchange/hooks/useOffer.tsx (5)
src/services/tokensData.ts (2)
  • Token (19-29)
  • chainNameToChainIdTokensData (234-255)
src/apps/the-exchange/utils/wrappedTokens.ts (2)
  • isNativeToken (30-31)
  • isWrappedToken (23-28)
src/hooks/useTransactionDebugLogger.tsx (1)
  • useTransactionDebugLogger (1-15)
src/utils/blockchain.ts (1)
  • isStableCoin (350-355)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNativeBalance (154-181)
🪛 Biome (1.9.4)
src/apps/the-exchange/hooks/useOffer.tsx

[error] 279-279: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (9)
src/apps/the-exchange/utils/wrappedTokens.ts (1)

4-4: Polygon MATIC Native Address Confirmed

The address 0x0000000000000000000000000000000000001010 is already used in src/services/tokensData.ts to identify MATIC and matches the common sentinel for Polygon’s native token (cf. 1inch). No further changes are needed.

• Added in src/apps/the-exchange/utils/wrappedTokens.ts to NATIVE_TOKEN_ADDRESSES
• Already referenced in src/services/tokensData.ts for MATIC detection

src/apps/the-exchange/components/ExchangeAction/test/ExchangeAction.test.tsx (6)

91-91: LGTM: Well-defined fee receiver constant.

The FEE_RECEIVER constant provides a clear, reusable address for testing fee functionality.


225-256: LGTM: Comprehensive native token fee test.

The test properly mocks the useOffer hook to simulate native token fee scenarios and verifies the UI displays the correct fee information.


258-317: LGTM: Thorough stablecoin fee test with proper ERC20 encoding.

The test correctly uses encodeFunctionData to simulate ERC20 transfer calls and validates the fee display for stablecoin scenarios with proper decimal handling.


319-378: LGTM: Wrapped token fee test with accurate contract address.

The test uses the correct WETH contract address and properly tests the wrapped token fee scenario.


380-431: LGTM: ERC20 native fallback test covers edge case.

The test properly validates the fallback behavior for non-stable ERC20 tokens where the fee is paid in native tokens.


433-451: LGTM: Error handling test ensures robust UI behavior.

The test properly validates error display when getStepTransactions fails, ensuring users receive appropriate feedback.

src/apps/the-exchange/hooks/useOffer.tsx (2)

43-45: LGTM: Utility function for wei conversion.

The toWei function provides a clean wrapper around parseUnits for wei conversion.


47-60: LGTM: Helper function for native balance extraction.

The getNativeBalanceFromPortfolio function efficiently extracts native token balance from wallet portfolio using the isNativeToken helper.

Comment on lines +226 to +227
const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
const feeAmount = BigInt(route.fromAmount) / BigInt(100); // 1%
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for fee receiver address.

The fee receiver address comes from environment variable without validation. Consider adding address validation to prevent potential issues.

const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
+if (!feeReceiver || !feeReceiver.match(/^0x[a-fA-F0-9]{40}$/)) {
+  throw new Error('Invalid or missing fee receiver address');
+}
const feeAmount = BigInt(route.fromAmount) / BigInt(100); // 1%
📝 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 feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
const feeAmount = BigInt(route.fromAmount) / BigInt(100); // 1%
const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
if (!feeReceiver || !feeReceiver.match(/^0x[a-fA-F0-9]{40}$/)) {
throw new Error('Invalid or missing fee receiver address');
}
const feeAmount = BigInt(route.fromAmount) / BigInt(100); // 1%
🤖 Prompt for AI Agents
In src/apps/the-exchange/hooks/useOffer.tsx around lines 226 to 227, the
feeReceiver address is assigned directly from an environment variable without
validation. Add validation logic to check if feeReceiver is a valid address
format before using it. If invalid, handle the error appropriately, such as
throwing an error or using a fallback, to prevent potential issues from an
incorrect or malformed address.

? cachedNativeBalance
: await getNativeBalance(fromAccount, fromTokenChainId);

const userNativeBalance = toWei(userNativeBalanceStr, 18);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential error in native balance conversion.

The toWei function might fail if userNativeBalanceStr contains an error string (as returned by getNativeBalance on failure).

const userNativeBalance = toWei(userNativeBalanceStr, 18);
+
+// Check if balance fetch failed
+if (userNativeBalanceStr.includes('Error')) {
+  throw new Error('Failed to fetch native token balance');
+}

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

🤖 Prompt for AI Agents
In src/apps/the-exchange/hooks/useOffer.tsx at line 291, the call to toWei with
userNativeBalanceStr may throw an error if userNativeBalanceStr contains an
error string from a failed getNativeBalance call. To fix this, add a check
before calling toWei to ensure userNativeBalanceStr is a valid numeric string.
If it is an error string or invalid, handle the error gracefully by setting a
default value or returning early to avoid calling toWei with invalid input.

Copy link
Collaborator

@IAmKio IAmKio left a comment

Choose a reason for hiding this comment

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

LGTM!

@RanaBug RanaBug merged commit 8a25e4e into staging Jul 14, 2025
6 checks passed
This was referenced Aug 7, 2025
@coderabbitai coderabbitai bot mentioned this pull request Aug 20, 2025
3 tasks
@coderabbitai coderabbitai bot mentioned this pull request Oct 7, 2025
3 tasks
@coderabbitai coderabbitai bot mentioned this pull request Oct 24, 2025
3 tasks
@coderabbitai coderabbitai bot mentioned this pull request Nov 18, 2025
3 tasks
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