Skip to content

Comments

PRO-3720-RelayBuy#454

Merged
vignesha22 merged 14 commits intostagingfrom
PRO-3720-Relay_Buy
Nov 18, 2025
Merged

PRO-3720-RelayBuy#454
vignesha22 merged 14 commits intostagingfrom
PRO-3720-Relay_Buy

Conversation

@vignesha22
Copy link
Contributor

@vignesha22 vignesha22 commented Nov 12, 2025

Description

  • Have Relay Buy by default to fetch from remote Config

How Has This Been Tested?

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

    • Feature-flagged "Relay Buy" flow: USDC-payable display, chain-aware buy path, pause/resume quote refresh, on-screen buy offer preview, and improved gas estimation with native-fee display.
  • Bug Fixes / Reliability

    • Enhanced error reporting and logging across buy/sell, polling and transaction flows; transaction UI and batch listing now respect Relay Buy chain context and exclude buy batches.
  • Tests

    • Added extensive Relay Buy integration and unit tests for preview, execution, status, and related flows; test setup updated for remote-config mocks.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Adds a Remote Config–gated Relay Buy path: new Firebase remote-config init and hook, Relay buy and gas-estimation hooks, Sentry logging, buy-flow gating and chain-context propagation through Buy → Preview → Transaction components, batch filtering updates, and expanded Relay Buy tests.

Changes

Cohort / File(s) Summary
Firebase & Remote Config
src/main.tsx, src/services/firebase.ts, src/hooks/useRemoteConfig.ts
Initialize Firebase Remote Config at startup; add USE_RELAY_BUY default, initialization and flag helpers; expose useRemoteConfig() and getUseRelayBuy(); call initialize at app startup.
Relay Buy & Gas Hooks
src/apps/pulse/hooks/useRelayBuy.ts, src/apps/pulse/hooks/useGasEstimationBuy.ts
Add useRelayBuy (BuyOffer/BuyParams, getBestOffer, executeBuy, buildTransactions, allowance checks) and useGasEstimationBuy (batch-based gas estimation, pause/cleanup, native cost conversion).
Buy Flow Components
src/apps/pulse/components/Buy/Buy.tsx, src/apps/pulse/components/Buy/PreviewBuy.tsx
Wire USE_RELAY_BUY flag; manage buyOffer state; conditional fetch/submit routing (Relay vs Intent SDK); extend PreviewBuy props (fromChainId, onBuyOfferUpdate, setBuyFlowPaused, userPortfolio) and adapt UI/gas estimation/execution paths.
Home Screen Integration
src/apps/pulse/components/App/HomeScreen.tsx
Read remote-config; gate refresh/polling with isBuyFlowPaused; propagate useRelayBuy, fromChainId, onBuyOfferUpdate, setBuyFlowPaused, and portfolio into Buy/Preview/Transaction flows; add structured error logging on refresh/poll.
Transaction Stack & Types
src/apps/pulse/components/Transaction/TransactionStatus.tsx, src/apps/pulse/components/Transaction/TransactionDetails.tsx, src/apps/pulse/components/Transaction/TransactionInfo.tsx, src/apps/pulse/types/types.ts
Add optional useRelayBuy and fromChainId props; adjust tx-hash sourcing and step rendering so Relay Buy bypasses ResourceLock/Completed steps; propagate chain context for links/copy.
Step Status & Utilities
src/apps/pulse/utils/utils.tsx, src/apps/pulse/hooks/useTransactionStatus.ts
Add useRelayBuy to getStepStatus and useTransactionStatus; define isIntentSdkBuy = isBuy && !useRelayBuy and update step logic accordingly.
Batch Filtering / Providers
src/providers/GlobalTransactionsBatchProvider.tsx, src/components/BottomMenuModal/SendModal/SendModalBatchesTabView.tsx
Exclude pulse-buy batches from provider filtering and UI grouping alongside existing exclusions.
Sentry & Logging
src/apps/pulse/utils/sentry.ts, src/apps/pulse/index.tsx, src/apps/pulse/components/Sell/Sell.tsx
Add initSentryForPulse, logPulseError and fallback wallet helpers; initialize Sentry on Pulse mount; surface structured logging on sell and buy error paths.
Tests & Test Setup
src/test-utils/setupTests.ts, src/apps/pulse/components/App/tests/HomeScreen.test.tsx, src/apps/pulse/components/Buy/tests/*, src/apps/pulse/components/Transaction/tests/*, src/apps/pulse/hooks/tests/*, src/apps/pulse/utils/tests/utils.test.tsx, src/providers/__tests__/GlobalTransactionsBatchProvider.test.tsx
Add/adjust Relay Buy test suites and mocks; mock remote-config and batch hooks; replace flaky waits with waitFor/testId patterns; add Relay-specific scenario coverage.
Minor / UI plumbing
src/apps/pulse/components/Search/Search.tsx, src/apps/pulse/components/Buy/Buy.tsx (minor imports), src/apps/pulse/components/Sell/Sell.tsx
Preserve relayBuy query param on search navigation; add logPulseError import/use; small control-flow and prop propagation tweaks.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HomeScreen
    participant Buy
    participant PreviewBuy
    participant RelayHook as useRelayBuy
    participant TxStatus as TransactionStatus

    User->>HomeScreen: open Buy
    HomeScreen->>HomeScreen: read USE_RELAY_BUY via useRemoteConfig
    alt Relay enabled
        HomeScreen->>Buy: pass useRelayBuy=true, setBuyFlowPaused, onBuyOfferUpdate
        Buy->>RelayHook: getBestOffer(usd amount, token, fromChainId)
        RelayHook-->>Buy: BuyOffer
        User->>Buy: open Preview
        Buy->>PreviewBuy: pass BuyOffer, fromChainId, userPortfolio
        PreviewBuy->>RelayHook: executeBuy(...)
        RelayHook-->>PreviewBuy: enqueued txs / user-op hash
        PreviewBuy->>TxStatus: show status (useRelayBuy=true)
        TxStatus->>TxStatus: skip ResourceLock/Completed steps
    else Relay disabled
        HomeScreen->>Buy: pass useRelayBuy=false
        Buy->>PreviewBuy: pass ExpressIntentResponse
        PreviewBuy->>PreviewBuy: execute via Intent SDK (shortlistBid)
        PreviewBuy->>TxStatus: show status (useRelayBuy=false)
        TxStatus->>TxStatus: include ResourceLock/Completed steps
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus review areas:
    • src/apps/pulse/hooks/useRelayBuy.ts: transaction sequencing, approvals, fee transfer, allowance and error handling.
    • src/apps/pulse/hooks/useGasEstimationBuy.ts: batch lifecycle, concurrency/pause handling, unit conversions.
    • HomeScreen / Buy / PreviewBuy: flag propagation, fromChainId, polling branching and pause logic.
    • getStepStatus / transaction status components: Relay vs Intent SDK semantics and tx-hash chain selection.
    • Tests and test setup: remote-config mocks and deterministic waits.

Possibly related PRs

Suggested reviewers

  • RanaBug

Poem

🐇 I hopped through flags and offers bright,
Fetching quotes by day and night.
Two paths to buy — Intent or Relay,
I carted gas and tests away.
Hop, build, and ship — may all pass right!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete. It provides only a one-line change summary and leaves both 'How Has This Been Tested?' and 'Screenshots' sections blank, failing to meet the template requirements. Complete the description by detailing testing approach (unit tests, integration tests, manual testing steps) and confirming that the 'New feature' checkbox is checked, while removing unchecked boxes or explaining why they don't apply.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'PRO-3720-RelayBuy' is a ticket identifier and feature name but lacks specificity about the actual change. It doesn't clearly convey what was implemented or modified. Use a more descriptive title like 'Add Relay Buy feature flag via Firebase Remote Config' to clearly communicate the primary change to team members reviewing the history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch PRO-3720-Relay_Buy

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e13ce0 and de6f701.

📒 Files selected for processing (1)
  • src/apps/pulse/components/Search/Search.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/apps/pulse/components/Search/Search.tsx
⏰ 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

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/apps/pulse/components/App/HomeScreen.tsx (1)

663-744: Include USE_RELAY_BUY in the polling effect dependencies.

This useEffect now branches on USE_RELAY_BUY, but the dependency array still omits it. Because the flag defaults to false, the interval is created with the “Express Intent” branch and never re-evaluates when Remote Config later flips the flag to true, so the Relay Buy path never activates for the current session. Please add the flag to the dependency list so the effect tears down and rebuilds when the remote config value changes.

   }, [
     userOpHash,
     transactionData?.sellToken?.chainId,
     transactionData?.buyToken?.chainId,
     isPollingActive,
     isBackgroundPolling,
     hasSeenSuccess,
     transactionData?.isBuy,
-    intentSdk,
+    intentSdk,
+    USE_RELAY_BUY,
   ]);
src/apps/pulse/components/Buy/PreviewBuy.tsx (1)

465-587: Relay refresh path never activates

refreshPreviewBuyData is memoized without USE_RELAY_BUY in its dependencies. Because the flag comes from Remote Config and flips after mount, this callback keeps the initial false branch forever, so both manual and timed refreshes continue to hit the Intent SDK path and never fetch Relay offers. Add the flag to the dependency list so the memoized function swaps branches when the flag changes.

-  }, [
+  }, [
     buyToken,
     usdAmount,
     intentSdk,
     accountAddress,
     dispensableAssets,
     setExpressIntentResponse,
     clearError,
     isRelayInitialized,
     onBuyOfferUpdate,
     getBestOffer,
     fromChainId,
     isWaitingForSignature,
     isExecuting,
     estimateGasFees,
     cleanupBatch,
+    USE_RELAY_BUY,
   ]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ededb3 and bb5f81c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • package.json (1 hunks)
  • src/apps/pulse/components/App/HomeScreen.tsx (4 hunks)
  • src/apps/pulse/components/Buy/Buy.tsx (10 hunks)
  • src/apps/pulse/components/Buy/PreviewBuy.tsx (13 hunks)
  • src/apps/pulse/hooks/useGasEstimationBuy.ts (1 hunks)
  • src/apps/pulse/hooks/useRelayBuy.ts (1 hunks)
  • src/hooks/useRemoteConfig.ts (1 hunks)
  • src/main.tsx (1 hunks)
  • src/services/firebase.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 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.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
  • src/apps/pulse/components/App/HomeScreen.tsx
📚 Learning: 2025-09-09T12:40:15.629Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 391
File: src/apps/pulse/components/Sell/Sell.tsx:113-130
Timestamp: 2025-09-09T12:40:15.629Z
Learning: In the Pulse app Sell component, when a user changes/switches tokens, the input amount automatically resets to 0, which means liquidity validation state doesn't become stale when tokens change.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/pulse/hooks/useGasEstimationBuy.ts
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
  • src/apps/pulse/hooks/useRelayBuy.ts
  • src/apps/pulse/components/App/HomeScreen.tsx
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
  • src/apps/pulse/components/App/HomeScreen.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
  • src/apps/pulse/components/App/HomeScreen.tsx
🧬 Code graph analysis (7)
src/main.tsx (1)
src/services/firebase.ts (1)
  • initializeRemoteConfig (41-83)
src/apps/pulse/components/Buy/Buy.tsx (2)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (9-37)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-792)
src/apps/pulse/hooks/useGasEstimationBuy.ts (5)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-792)
src/services/tokensData.ts (1)
  • Token (19-29)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/blockchain.ts (1)
  • getNativeAssetForChainId (72-144)
src/hooks/useRemoteConfig.ts (2)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (47-792)
src/services/firebase.ts (2)
  • initializeRemoteConfig (41-83)
  • getUseRelayBuyFlag (86-122)
src/apps/pulse/components/Buy/PreviewBuy.tsx (6)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-792)
src/services/tokensData.ts (1)
  • Token (19-29)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (9-37)
src/apps/pulse/hooks/useGasEstimationBuy.ts (1)
  • useGasEstimationBuy (25-215)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/number.tsx (2)
  • formatExponentialSmallNumber (119-149)
  • limitDigitsNumber (42-69)
src/apps/pulse/hooks/useRelayBuy.ts (8)
src/apps/pulse/hooks/useRelaySdk.ts (1)
  • useRelaySdk (21-64)
src/hooks/useTransactionDebugLogger.tsx (1)
  • useTransactionDebugLogger (1-15)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/apps/pulse/constants/tokens.ts (1)
  • STABLE_CURRENCIES (13-15)
src/apps/the-exchange/utils/wrappedTokens.ts (1)
  • getWrappedTokenAddressIfNative (33-44)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/services/tokensData.ts (1)
  • Token (19-29)
src/apps/the-exchange/utils/blockchain.ts (1)
  • toWei (130-132)
src/apps/pulse/components/App/HomeScreen.tsx (1)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (9-37)
⏰ 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). (2)
  • GitHub Check: unit-tests
  • GitHub Check: lint

@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 12, 2025 09:09 Inactive
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 12, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: de6f701
Status: ✅  Deploy successful!
Preview URL: https://1c2542ac.x-e62.pages.dev
Branch Preview URL: https://pro-3720-relay-buy.x-e62.pages.dev

View logs

@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 12, 2025 15:11 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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/apps/pulse/components/Buy/Buy.tsx (1)

396-415: Ensure Relay quotes retry after SDK init

Line 404: when USE_RELAY_BUY flips to true but isRelayInitialized is still false, fetchBuyOffer() returns immediately. Because the effect’s dependency array does not include the readiness flag (or the callback), we never re-enter this effect once Relay finishes initialising, so buyOffer stays null, the banner shows “No available routes”, and the Buy button remains disabled until the user tweaks the amount. Please wire the SDK readiness into the dependencies (and guard in the body) so we automatically retry when the Relay client comes up.

-      if (USE_RELAY_BUY) {
-        fetchBuyOffer();
+      if (USE_RELAY_BUY) {
+        if (!isRelayInitialized) {
+          return;
+        }
+        fetchBuyOffer();
       } else {
         refreshBuyIntent();
       }
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [
     debouncedUsdAmount,
     token,
     accountAddress,
     intentSdk,
     areModulesInstalled,
     dispensableAssets,
     permittedChains,
     USE_RELAY_BUY,
+    isRelayInitialized,
   ]);
src/apps/pulse/components/Buy/PreviewBuy.tsx (1)

103-149: Guard Relay flow with the offer shape to avoid Intent branch crashes

useRemoteConfig() starts with useRelayBuy = false until its async init completes, even when the parent already opened Preview under the Relay flow. If the user clicks Confirm during that window, handleConfirmBuy falls back to shortlistBid(), treats the BuyOffer in expressIntentResponse as an Intent response, and we explode on intentResponse.bids[0]. Please derive the Relay branch from the payload (e.g., 'offer' in expressIntentResponse) in addition to the flag so we never dispatch the Intent path with a Relay offer.

-import { useRemoteConfig } from '../../../../hooks/useRemoteConfig';
+import { useRemoteConfig } from '../../../../hooks/useRemoteConfig';
@@
-interface PreviewBuyProps {
+const isRelayBuyOffer = (
+  response: ExpressIntentResponse | BuyOffer | null
+): response is BuyOffer => !!response && 'offer' in response;
+
+interface PreviewBuyProps {
@@
-  const { useRemoteConfig } = '../../../../hooks/useRemoteConfig';
-  const { useRelayBuy: USE_RELAY_BUY } = useRemoteConfig();
+  const { useRelayBuy: relayFlag } = useRemoteConfig();
+  const USE_RELAY_BUY =
+    relayFlag || isRelayBuyOffer(expressIntentResponse);
@@
-  const buyOffer = USE_RELAY_BUY ? (expressIntentResponse as BuyOffer) : null;
+  const buyOffer = USE_RELAY_BUY
+    ? (expressIntentResponse as BuyOffer)
+    : null;
@@
-    if (USE_RELAY_BUY) {
+    if (USE_RELAY_BUY) {
       await executeBuyDirectly();
🧹 Nitpick comments (5)
src/services/firebase.ts (1)

28-31: Consider increasing the fetch interval for production.

The minimumFetchIntervalMillis is set to 60 seconds with a comment indicating it's "reduced for testing". Firebase recommends a minimum of 12 hours (43200000ms) for production to avoid rate limiting and unnecessary network calls.

Apply this diff to use a production-appropriate interval:

-  minimumFetchIntervalMillis: 60000, // 1 minute (reduced for testing)
+  minimumFetchIntervalMillis: 3600000, // 1 hour
   fetchTimeoutMillis: 60000, // 60 seconds

Alternatively, use environment variables to configure different intervals for development vs. production.

src/apps/pulse/hooks/useGasEstimationBuy.ts (1)

66-76: Consider whether zero token price should be an error.

When tokenPrice <= 0, the code silently sets gas cost to '0' and returns early. This could mask pricing data issues.

Consider logging a warning or setting an error state to make debugging easier:

 const tokenPrice = parseFloat(buyToken.usdValue) || 0;
 if (tokenPrice <= 0) {
+  console.warn('Invalid token price for gas estimation:', buyToken.symbol);
+  setGasEstimationError('Invalid token price');
   setGasCostNative('0');
   setNativeTokenSymbol('');
   return;
 }
src/apps/pulse/components/Buy/tests/PreviewBuy.test.tsx (1)

322-482: Consider whether these duplicate tests add value.

The "Relay Buy integration" test suite appears to duplicate existing Intent SDK tests without exercising different code paths. The tests use the same mocks (mockExpressIntentResponse, mockShortlistBid) and verify identical behavior.

If Relay Buy and Intent SDK flows share the same UI behavior, consider:

  1. Parameterizing the existing tests to run for both modes
  2. Adding Relay Buy-specific tests that verify distinct behavior (e.g., gas estimation display, different transaction execution paths)
  3. Removing duplicate tests if they don't add coverage

For example:

describe.each([
  { mode: 'Intent SDK', useRelayBuy: false },
  { mode: 'Relay Buy', useRelayBuy: true }
])('$mode integration', ({ mode, useRelayBuy }) => {
  // shared test cases
});
src/hooks/useRemoteConfig.ts (1)

16-29: Consider adding cleanup to prevent state updates after unmount.

The async initialization in useEffect doesn't check if the component is still mounted before calling setUseRelayBuy and setIsInitialized. This could cause warnings in development if the component unmounts during initialization.

Apply this diff to add proper cleanup:

 useEffect(() => {
+  let mounted = true;
+
   const initialize = async () => {
     await initializeRemoteConfig();
 
     // Get the value after initialization completes
     const flagValue = getUseRelayBuyFlag();
 
     // Update state after we have the value
-    setUseRelayBuy(flagValue);
-    setIsInitialized(true);
+    if (mounted) {
+      setUseRelayBuy(flagValue);
+      setIsInitialized(true);
+    }
   };
 
   initialize();
+
+  return () => {
+    mounted = false;
+  };
 }, []);
src/apps/pulse/components/Buy/tests/Buy.test.tsx (1)

834-934: Consider removing duplicate test suite.

The "Relay Buy integration" test suite appears to duplicate scenarios already covered in the main test suite without testing Relay Buy-specific behavior differently. All tests render the same component with the same configuration and verify the same behavior.

Since Relay Buy-specific behavior is tested at the integration level (TransactionStatus, TransactionDetails, etc.) and the Buy component behavior doesn't change based on the flag, this suite adds maintenance burden without additional coverage.

Consider removing this test suite to reduce duplication:

-  describe('Relay Buy integration', () => {
-    it('renders correctly with Relay Buy enabled', () => {
-      // ... duplicate test content
-    });
-    // ... more duplicate tests
-  });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb5f81c and 11a19c1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • src/apps/pulse/components/App/HomeScreen.tsx (5 hunks)
  • src/apps/pulse/components/App/tests/HomeScreen.test.tsx (1 hunks)
  • src/apps/pulse/components/Buy/Buy.tsx (10 hunks)
  • src/apps/pulse/components/Buy/PreviewBuy.tsx (14 hunks)
  • src/apps/pulse/components/Buy/tests/Buy.test.tsx (2 hunks)
  • src/apps/pulse/components/Buy/tests/PreviewBuy.test.tsx (1 hunks)
  • src/apps/pulse/components/Transaction/TransactionDetails.tsx (4 hunks)
  • src/apps/pulse/components/Transaction/TransactionInfo.tsx (3 hunks)
  • src/apps/pulse/components/Transaction/TransactionStatus.tsx (2 hunks)
  • src/apps/pulse/components/Transaction/tests/TransactionDetails.test.tsx (1 hunks)
  • src/apps/pulse/components/Transaction/tests/TransactionInfo.relayBuy.test.tsx (1 hunks)
  • src/apps/pulse/hooks/tests/useTransactionStatus.relayBuy.test.ts (1 hunks)
  • src/apps/pulse/hooks/useGasEstimationBuy.ts (1 hunks)
  • src/apps/pulse/hooks/useRelayBuy.ts (1 hunks)
  • src/apps/pulse/hooks/useTransactionStatus.ts (3 hunks)
  • src/apps/pulse/types/types.ts (3 hunks)
  • src/apps/pulse/utils/tests/utils.test.tsx (1 hunks)
  • src/apps/pulse/utils/utils.tsx (4 hunks)
  • src/hooks/useRemoteConfig.ts (1 hunks)
  • src/providers/__tests__/GlobalTransactionsBatchProvider.test.tsx (1 hunks)
  • src/services/firebase.ts (2 hunks)
  • src/test-utils/setupTests.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/apps/pulse/components/Transaction/tests/TransactionInfo.relayBuy.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/apps/pulse/components/App/HomeScreen.tsx
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/providers/__tests__/GlobalTransactionsBatchProvider.test.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-05-28T14:30:02.702Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 320
File: src/components/BottomMenuModal/HistoryModal/TransactionInfo.tsx:177-182
Timestamp: 2025-05-28T14:30:02.702Z
Learning: In the transaction history tracking feature, chain ID validation is performed earlier in the flow before the values reach the TransactionInfo component, so additional validation at the display level is not needed.

Applied to files:

  • src/apps/pulse/components/Transaction/TransactionInfo.tsx
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/pulse/utils/utils.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
  • src/apps/pulse/hooks/useRelayBuy.ts
  • src/apps/pulse/hooks/useGasEstimationBuy.ts
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 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.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-09-09T12:40:15.629Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 391
File: src/apps/pulse/components/Sell/Sell.tsx:113-130
Timestamp: 2025-09-09T12:40:15.629Z
Learning: In the Pulse app Sell component, when a user changes/switches tokens, the input amount automatically resets to 0, which means liquidity validation state doesn't become stale when tokens change.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
🧬 Code graph analysis (14)
src/apps/pulse/hooks/useTransactionStatus.ts (1)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (47-786)
src/apps/pulse/components/Transaction/TransactionInfo.tsx (1)
src/apps/pulse/types/types.ts (1)
  • TransactionInfoProps (130-144)
src/apps/pulse/components/Transaction/TransactionDetails.tsx (1)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (47-786)
src/apps/pulse/components/Transaction/TransactionStatus.tsx (1)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (47-786)
src/apps/pulse/utils/utils.tsx (2)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (47-786)
src/apps/pulse/types/types.ts (1)
  • StepStatus (13-13)
src/apps/pulse/components/Buy/Buy.tsx (2)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (12-35)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-786)
src/apps/pulse/utils/tests/utils.test.tsx (1)
src/apps/pulse/utils/utils.tsx (1)
  • getStepStatus (117-182)
src/apps/pulse/components/Buy/PreviewBuy.tsx (6)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-786)
src/services/tokensData.ts (1)
  • Token (19-29)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (12-35)
src/apps/pulse/hooks/useGasEstimationBuy.ts (1)
  • useGasEstimationBuy (25-207)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/number.tsx (2)
  • formatExponentialSmallNumber (119-149)
  • limitDigitsNumber (42-69)
src/apps/pulse/hooks/tests/useTransactionStatus.relayBuy.test.ts (2)
src/apps/pulse/hooks/useTransactionStatus.ts (1)
  • useTransactionStatus (21-57)
src/apps/pulse/types/types.ts (1)
  • TransactionStatusState (1-5)
src/apps/pulse/components/App/tests/HomeScreen.test.tsx (2)
src/test-utils/testUtils.tsx (1)
  • TestWrapper (116-134)
src/apps/pulse/components/App/HomeScreen.tsx (1)
  • HomeScreen (89-1095)
src/apps/pulse/components/Buy/tests/PreviewBuy.test.tsx (2)
src/apps/pulse/components/Buy/PreviewBuy.tsx (1)
  • PreviewBuy (68-877)
src/apps/pulse/hooks/useIntentSdk.ts (1)
  • useIntentSdk (10-106)
src/apps/pulse/hooks/useRelayBuy.ts (8)
src/apps/pulse/hooks/useRelaySdk.ts (1)
  • useRelaySdk (21-64)
src/hooks/useTransactionDebugLogger.tsx (1)
  • useTransactionDebugLogger (1-15)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/apps/pulse/constants/tokens.ts (1)
  • STABLE_CURRENCIES (13-15)
src/apps/the-exchange/utils/wrappedTokens.ts (1)
  • getWrappedTokenAddressIfNative (33-44)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/services/tokensData.ts (1)
  • Token (19-29)
src/apps/the-exchange/utils/blockchain.ts (1)
  • toWei (130-132)
src/apps/pulse/hooks/useGasEstimationBuy.ts (5)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (47-786)
src/services/tokensData.ts (1)
  • Token (19-29)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/blockchain.ts (1)
  • getNativeAssetForChainId (72-144)
src/hooks/useRemoteConfig.ts (1)
src/services/firebase.ts (2)
  • initializeRemoteConfig (41-53)
  • getUseRelayBuyFlag (56-64)
⏰ 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 (23)
src/services/firebase.ts (3)

14-14: Verify the storage bucket URL change is intentional.

The storage bucket URL has been updated from .appspot.com to .firebasestorage.app. This appears to be a Firebase domain migration.

Please confirm this change is intentional and that the new storage bucket URL is correctly configured in your Firebase Console.


41-53: LGTM! Error handling approach is reasonable.

The idempotent initialization with fallback to default values on error is a solid approach. The function gracefully degrades when Remote Config is unavailable, ensuring the app continues to function with the default USE_RELAY_BUY: false value.

Consider adding telemetry or metrics to track Remote Config initialization failures in production, as silent failures could mask configuration issues.


56-64: LGTM! Consistent error handling pattern.

The function provides a clean synchronous API for reading the flag with appropriate fallback behavior.

src/apps/pulse/hooks/useGasEstimationBuy.ts (6)

16-32: LGTM! Well-defined interface.

The hook signature is clear with appropriate default values and type safety.


33-44: LGTM! Solid state management.

The combination of state and refs effectively prevents concurrent estimations and stale closure issues.


73-75: Floating-point arithmetic for token amounts.

The token amount calculation uses floating-point division, which may have precision issues for large amounts or tokens with many decimals.

Based on learnings: The team has accepted JavaScript's floating-point arithmetic limitations as a valid trade-off for this use case.


92-98: LGTM! Appropriate error handling for batch cleanup.

Silently catching cleanup errors is correct since the batch may not exist. Non-existence is an expected state.


184-198: LGTM! Correct dependency management.

The useEffect dependencies and ref pattern correctly trigger re-estimation when inputs change while avoiding stale closures.


200-207: LGTM! Complete public API.

The hook returns all necessary state and controls for gas estimation.

src/providers/__tests__/GlobalTransactionsBatchProvider.test.tsx (1)

11-12: LGTM! Appropriate unmocking for provider integration test.

Unmocking the hook for this specific test file allows testing the actual provider behavior rather than a mocked version, which is correct for a provider integration test.

src/test-utils/setupTests.ts (1)

93-136: LGTM! Comprehensive test mocks for Remote Config integration.

The mocks appropriately stub Firebase Remote Config, the firebase service module, useRemoteConfig hook, and useGlobalTransactionsBatch hook with sensible default return values. This enables consistent test behavior across the codebase.

The default USE_RELAY_BUY: false ensures tests run in the standard Intent SDK mode unless explicitly overridden.

src/apps/pulse/types/types.ts (1)

86-87: LGTM! Consistent type additions for Relay Buy support.

The useRelayBuy?: boolean flag is appropriately added to transaction-related interfaces, enabling components to adapt their behavior for Relay Buy flows.

Also applies to: 115-116, 143-143

src/hooks/useRemoteConfig.ts (1)

37-43: LGTM! Synchronous helper for post-initialization access.

The getUseRelayBuy helper provides convenient synchronous access to the flag after initialization completes.

src/apps/pulse/hooks/useTransactionStatus.ts (2)

9-16: LGTM! Clean interface extension.

Adding the optional useRelayBuy flag to the interface maintains backward compatibility while enabling Relay Buy support.


21-47: LGTM! Proper parameter threading and dependency management.

The useRelayBuy flag is correctly threaded through to getStepStatus and included in the memoization dependency array, ensuring step status updates when the flag changes.

src/apps/pulse/utils/tests/utils.test.tsx (1)

359-634: LGTM! Comprehensive Relay Buy test coverage.

The new test suite thoroughly validates the Relay Buy behavior across all transaction statuses and step types. The tests correctly verify that:

  • ResourceLock steps are inactive/pending for Relay Buy (not used in the UI flow)
  • Hash parameters (resourceLockTxHash, completedTxHash) are appropriately ignored
  • Clear differentiation between Intent SDK Buy and Relay Buy flows
src/apps/pulse/components/Transaction/tests/TransactionDetails.test.tsx (1)

592-604: LGTM! Test updated for new hook signature.

The test correctly includes the new useRelayBuy parameter in the expected hook call, using the appropriate default value of false.

src/apps/pulse/components/App/tests/HomeScreen.test.tsx (1)

269-309: LGTM! Appropriate integration tests for Remote Config.

The test suite appropriately verifies:

  • Basic rendering with the Relay Buy flag
  • Props propagation regardless of Remote Config state
  • Graceful handling of initialization state

These smoke tests complement the deeper Relay Buy behavior tests in component-specific test files.

src/apps/pulse/components/Transaction/TransactionStatus.tsx (1)

28-28: LGTM! Correct conditional logic for transaction hashes.

The implementation correctly distinguishes between Intent SDK Buy and Relay Buy flows:

  • Intent SDK Buy: uses completedTxHash (ResourceLock pattern)
  • Relay Buy: uses txHash from blockchainTxHash (simpler pattern like Sell)

The flag is properly threaded through to TransactionDetails.

Also applies to: 153-160

src/apps/pulse/components/Transaction/TransactionInfo.tsx (1)

34-39: LGTM! Hash display logic correctly implements Relay Buy vs Intent SDK Buy.

The changes appropriately:

  • Use completedTxHash for Intent SDK Buy (ResourceLock flow)
  • Use txHash for Relay Buy and Sell (simpler flow)
  • Tie copy/visibility behavior to the presence of a hash (!!displayTxHash)

Also applies to: 142-173

src/apps/pulse/components/Buy/tests/Buy.test.tsx (1)

746-776: LGTM! Improved test reliability.

The updates improve test determinism:

  • Proper mocking of getDispensableAssets to populate payingTokens
  • Using waitFor with testId instead of setTimeout and role-based lookup
  • Waiting for button to be enabled before interaction
src/apps/pulse/components/Transaction/TransactionDetails.tsx (1)

39-39: LGTM! Correct implementation of conditional progress steps.

The changes properly implement the different transaction flows:

  • Intent SDK Buy (isBuy && !useRelayBuy): Submitted → ResourceLock → Completed
  • Relay Buy/Sell: Submitted → Pending → Completed

The useRelayBuy flag is correctly:

  • Added with a sensible default (false)
  • Threaded to useTransactionStatus hook
  • Used for conditional progress step rendering
  • Passed to TransactionInfo for hash display logic

Also applies to: 55-62, 225-281, 303-303

src/apps/pulse/utils/utils.tsx (1)

117-182: LGTM! Clean implementation of Relay Buy distinction.

The getStepStatus function correctly implements the differentiation between Intent SDK Buy and Relay Buy:

  • Introduced isIntentSdkBuy = isBuy && !useRelayBuy for clarity
  • Relay Buy behaves like Sell (no ResourceLock step)
  • Intent SDK Buy maintains the ResourceLock → Completed flow
  • Comments updated to reflect the new terminology
  • Both new parameters are optional, maintaining backward compatibility

The logic is consistent with the related changes in TransactionDetails, TransactionInfo, and TransactionStatus.

@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 13, 2025 06:45 Inactive
@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 13, 2025 06:48 Inactive
@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 13, 2025 06:54 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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/apps/pulse/components/App/HomeScreen.tsx (1)

673-777: Don't switch polling mode mid-transaction.

USE_RELAY_BUY and maxStableCoinBalance are live values that can change after a buy has started (remote config resolves a few moments later, balances refresh, etc.). When that happens, this branch flips over to the Relay polling path even though the in-flight transaction was created with the legacy Intent flow, so we call getUserOperationStatus with a bid hash and surface a false failure. Likewise, fresh balance data can point maxStableCoinBalance at a different chain, so Relay buys start polling the wrong network and never complete. Snapshot both the relay flag and the from-chain at showTransactionStatus time and read those snapshots everywhere we drive status/polling/props instead of these live globals.

 const [transactionData, setTransactionData] = useState<{
   sellToken: SelectedToken | null;
   buyToken: SelectedToken | null;
   tokenAmount: string;
   sellOffer: SellOffer | null;
   payingTokens: PayingToken[];
   usdAmount: string;
   isBuy: boolean;
 } | null>(null);
+const [transactionContext, setTransactionContext] = useState<{
+  useRelayBuy: boolean;
+  fromChainId?: number;
+}>({ useRelayBuy: false });
@@
       setTransactionData({
         sellToken,
         buyToken,
         tokenAmount,
         sellOffer,
         payingTokens,
         usdAmount,
         isBuy,
       });
+      setTransactionContext({
+        useRelayBuy: USE_RELAY_BUY,
+        fromChainId: USE_RELAY_BUY ? maxStableCoinBalance?.chainId : undefined,
+      });
@@
-        if (isBuyTransaction && !USE_RELAY_BUY) {
+        if (isBuyTransaction && !transactionContext.useRelayBuy) {
@@
-          const fromChainId =
-            USE_RELAY_BUY && isBuy
-              ? maxStableCoinBalance?.chainId || 1
-              : chainId;
+          const fromChainId =
+            transactionContext.useRelayBuy && isBuyTransaction
+              ? transactionContext.fromChainId ?? 1
+              : chainId;
@@
-          <TransactionStatus
+          <TransactionStatus
             closeTransactionStatus={closeTransactionStatus}
             userOpHash={userOpHash}
@@
-            useRelayBuy={USE_RELAY_BUY}
+            useRelayBuy={transactionContext.useRelayBuy}
@@
-            fromChainId={maxStableCoinBalance?.chainId}
+            fromChainId={transactionContext.fromChainId}
           />

(You'll also need to read transactionContext inside the polling effect dependency list.)

src/apps/pulse/components/Buy/Buy.tsx (1)

197-211: Remove unnecessary and unsafe type cast.

The cast buyOffer as unknown as ExpressIntentResponse at line 201 bypasses type safety. Since setExInResp already accepts ExpressIntentResponse | BuyOffer | null (per the prop type at line 60), pass buyOffer directly without casting.

Apply this diff:

     if (USE_RELAY_BUY) {
       // For Relay Buy, we use the buyOffer instead of expressIntentResponse
-      // Convert buyOffer to a compatible format for PreviewBuy
       if (buyOffer) {
-        setExInResp(buyOffer as unknown as ExpressIntentResponse);
+        setExInResp(buyOffer);
         setPreviewBuy(true);
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11a19c1 and 4a29d33.

📒 Files selected for processing (8)
  • src/apps/pulse/components/App/HomeScreen.tsx (13 hunks)
  • src/apps/pulse/components/Buy/Buy.tsx (11 hunks)
  • src/apps/pulse/components/Buy/PreviewBuy.tsx (15 hunks)
  • src/apps/pulse/components/Transaction/TransactionDetails.tsx (5 hunks)
  • src/apps/pulse/components/Transaction/TransactionInfo.tsx (3 hunks)
  • src/apps/pulse/components/Transaction/TransactionStatus.tsx (3 hunks)
  • src/apps/pulse/hooks/useRelayBuy.ts (1 hunks)
  • src/apps/pulse/types/types.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-05-28T14:30:02.702Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 320
File: src/components/BottomMenuModal/HistoryModal/TransactionInfo.tsx:177-182
Timestamp: 2025-05-28T14:30:02.702Z
Learning: In the transaction history tracking feature, chain ID validation is performed earlier in the flow before the values reach the TransactionInfo component, so additional validation at the display level is not needed.

Applied to files:

  • src/apps/pulse/types/types.ts
  • src/apps/pulse/components/Transaction/TransactionInfo.tsx
  • src/apps/pulse/components/Transaction/TransactionStatus.tsx
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 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.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-09-09T12:40:15.629Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 391
File: src/apps/pulse/components/Sell/Sell.tsx:113-130
Timestamp: 2025-09-09T12:40:15.629Z
Learning: In the Pulse app Sell component, when a user changes/switches tokens, the input amount automatically resets to 0, which means liquidity validation state doesn't become stale when tokens change.

Applied to files:

  • src/apps/pulse/components/Buy/Buy.tsx
  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/components/Transaction/TransactionDetails.tsx
  • src/apps/pulse/components/Transaction/TransactionInfo.tsx
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/hooks/useRelayBuy.ts
  • src/apps/pulse/components/Buy/PreviewBuy.tsx
🧬 Code graph analysis (7)
src/apps/pulse/components/Buy/Buy.tsx (2)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (46-827)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (12-35)
src/apps/pulse/components/App/HomeScreen.tsx (3)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (12-35)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • BuyOffer (30-36)
src/services/userOpStatus.ts (1)
  • getUserOperationStatus (3-44)
src/apps/pulse/components/Transaction/TransactionDetails.tsx (1)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (46-827)
src/apps/pulse/components/Transaction/TransactionInfo.tsx (1)
src/apps/pulse/types/types.ts (1)
  • TransactionInfoProps (132-147)
src/apps/pulse/hooks/useRelayBuy.ts (8)
src/apps/pulse/hooks/useRelaySdk.ts (1)
  • useRelaySdk (21-64)
src/hooks/useTransactionDebugLogger.tsx (1)
  • useTransactionDebugLogger (1-15)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/apps/pulse/constants/tokens.ts (1)
  • STABLE_CURRENCIES (13-15)
src/apps/the-exchange/utils/wrappedTokens.ts (1)
  • getWrappedTokenAddressIfNative (33-44)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/services/tokensData.ts (1)
  • Token (19-29)
src/apps/the-exchange/utils/blockchain.ts (1)
  • toWei (130-132)
src/apps/pulse/components/Transaction/TransactionStatus.tsx (1)
src/apps/pulse/hooks/useRelayBuy.ts (1)
  • useRelayBuy (46-827)
src/apps/pulse/components/Buy/PreviewBuy.tsx (9)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (46-827)
src/services/tokensData.ts (1)
  • Token (19-29)
src/apps/pulse/hooks/useIntentSdk.ts (1)
  • useIntentSdk (10-106)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (12-35)
src/apps/pulse/hooks/useGasEstimationBuy.ts (1)
  • useGasEstimationBuy (25-207)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/number.tsx (2)
  • formatExponentialSmallNumber (119-149)
  • limitDigitsNumber (42-69)
src/apps/pulse/components/Buy/PayingToken.tsx (1)
  • PayingToken (18-96)
src/apps/pulse/types/tokens.ts (1)
  • PayingToken (12-21)
⏰ 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 (13)
src/apps/pulse/components/Transaction/TransactionInfo.tsx (1)

40-41: Hash gating looks solid.

Routing relay buys through displayTxHash keeps the explorer/copy controls hidden until we actually have a valid hash and picks the right chain for Relay vs legacy flows. Clean upgrade.

Also applies to: 165-175

src/apps/pulse/components/Buy/Buy.tsx (2)

32-35: LGTM!

The imports for Relay Buy integration are correct, bringing in the remote config hook and the new BuyOffer type.


93-102: LGTM!

Remote config flag and Relay Buy state are correctly initialized.

src/apps/pulse/components/Buy/PreviewBuy.tsx (4)

51-66: LGTM!

The props interface is correctly extended to support both Intent SDK and Relay Buy flows with proper typing.


184-195: LGTM!

The pause state bridge effects correctly include USE_RELAY_BUY in their dependency arrays, ensuring they re-run when the flag changes.


465-609: LGTM!

The refresh callback correctly handles both Relay Buy and Intent SDK flows, with proper validation, cleanup, and state management. Dependencies are complete.


756-779: LGTM!

The conditional rendering correctly displays USDC for Relay Buy or the full paying token list for Intent SDK. The data extraction from buyOffer is appropriate.

src/apps/pulse/hooks/useRelayBuy.ts (6)

30-44: LGTM!

The interfaces are clearly defined with appropriate types and documentation.


164-190: Note: Precision limitation accepted.

The rounding at lines 177-181 uses parseFloat().toFixed(6) which can introduce precision errors for very large or very small numbers. Based on learnings, the team has accepted JavaScript's decimal limitations as a valid trade-off for this use case.


344-373: LGTM!

The 1% fee calculation using integer math is correct: totalUsdcNeeded = usdcNeededForSwap * 100 / 99. The validation at lines 369-372 ensures the amounts add up correctly.


453-538: LGTM!

The approval logic correctly prefers Relay SDK approvals when available, and falls back to deriving the spender and checking allowances. The inherent race condition (allowance changing between check and execution) is unavoidable in async blockchain operations.


636-786: LGTM!

executeBuy correctly prepares the transaction batch without sending it, allowing the UI component to handle user confirmation and actual execution. Error handling provides specific, actionable messages.


796-814: LGTM!

Reusing buildTransactions for gas estimation is the right approach, ensuring consistency between estimated and actual transactions.

Comment on lines 29 to 38
import ArrowDown from '../../assets/arrow-down.svg';
import WalletIcon from '../../assets/wallet.svg';
import WarningIcon from '../../assets/warning.svg';
import { useRemoteConfig } from '../../../../hooks/useRemoteConfig';
import useIntentSdk from '../../hooks/useIntentSdk';
import useModularSdk from '../../hooks/useModularSdk';
import useRelayBuy, { BuyOffer } from '../../hooks/useRelayBuy';
import { PayingToken, SelectedToken } from '../../types/tokens';
import { MobulaChainNames, getChainId } from '../../utils/constants';
import { getDesiredAssetValue, getDispensableAssets } from '../../utils/intent';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we categorise for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

PayingToken as PayingTokenType,
SelectedToken,
} from '../../types/tokens';
import { Token } from '../../../../services/tokensData';
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
import { Token } from '../../../../services/tokensData';
// services
import { Token } from '../../../../services/tokensData';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 17, 2025 13:10 Inactive
@vignesha22 vignesha22 requested a review from IAmKio November 17, 2025 13:23
@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 17, 2025 19:39 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: 1

🧹 Nitpick comments (4)
src/components/BottomMenuModal/SendModal/SendModalBatchesTabView.tsx (1)

82-84: LGTM! Consistent filtering for pulse-buy batches.

The filtering logic correctly excludes pulse-buy batches from the grouping, mirroring the existing pulse-sell pattern. The updated comment accurately reflects the expanded exclusion criteria.

Consider extracting the batch filtering logic into a utility function to reduce duplication with lines 746-747:

// utils/batch.ts
export const shouldExcludeBatch = (batchName: string): boolean => {
  return (
    batchName.includes('pulse-sell') ||
    batchName.includes('pulse-buy') ||
    batchName === 'paymaster-batch'
  );
};

Then use it in both locations:

-      // Skip pulse-sell and pulse-buy batches - they are handled directly in the Pulse app
-      if (batchName.includes('pulse-sell')) return;
-      if (batchName.includes('pulse-buy')) return;
+      // Skip pulse-sell and pulse-buy batches - they are handled directly in the Pulse app
+      if (shouldExcludeBatch(batchName)) return;
src/apps/pulse/components/Buy/PreviewBuy.tsx (3)

285-324: Consider user feedback on early validation failure.

Lines 288-291 return silently with only a console log when required data is missing. While this scenario may be unlikely, consider setting an error message for the user to explain why the transaction cannot proceed.

Apply this diff to provide user feedback:

   const intentResponse = expressIntentResponse as ExpressIntentResponse;
   if (!buyToken || !intentResponse) {
     console.error('Missing required data to shortlist bid');
+    // Optionally set an error for the user
+    // e.g., setError('Unable to process transaction. Please try again.');
     return;
   }

326-331: Provide user feedback on validation failure.

Similar to the Intent SDK path, this early return only logs to console without setting an error message for the user. Consider providing user-facing feedback when required data is missing.

Apply this diff:

   if (!buyToken || !buyOffer || !kit || !fromChainId) {
     console.error('Missing required data to execute Relay Buy');
+    // Consider setting user-facing error here
     return;
   }

461-500: Consider surfacing refresh errors to the user.

When the Relay Buy offer refresh fails (lines 494-497), the error is only logged to console and the offer is set to null. While this is a background operation, users might benefit from knowing their quote couldn't be refreshed, especially if they're about to confirm a potentially stale offer. This is optional given the auto-refresh nature.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc525bc and 07772e3.

📒 Files selected for processing (3)
  • src/apps/pulse/components/Buy/PreviewBuy.tsx (14 hunks)
  • src/components/BottomMenuModal/SendModal/SendModalBatchesTabView.tsx (2 hunks)
  • src/providers/GlobalTransactionsBatchProvider.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-09-09T12:40:15.629Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 391
File: src/apps/pulse/components/Sell/Sell.tsx:113-130
Timestamp: 2025-09-09T12:40:15.629Z
Learning: In the Pulse app Sell component, when a user changes/switches tokens, the input amount automatically resets to 0, which means liquidity validation state doesn't become stale when tokens change.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 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.

Applied to files:

  • src/apps/pulse/components/Buy/PreviewBuy.tsx
🧬 Code graph analysis (1)
src/apps/pulse/components/Buy/PreviewBuy.tsx (6)
src/apps/pulse/hooks/useRelayBuy.ts (2)
  • BuyOffer (30-36)
  • useRelayBuy (46-804)
src/services/tokensData.ts (1)
  • Token (19-29)
src/hooks/useRemoteConfig.ts (1)
  • useRemoteConfig (34-63)
src/apps/pulse/hooks/useGasEstimationBuy.ts (1)
  • useGasEstimationBuy (25-207)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/utils/number.tsx (2)
  • formatExponentialSmallNumber (119-149)
  • limitDigitsNumber (42-69)
⏰ 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 (10)
src/components/BottomMenuModal/SendModal/SendModalBatchesTabView.tsx (1)

746-747: LGTM! Consistent filtering before rendering.

The filtering logic correctly excludes pulse-buy batches before rendering, consistent with the grouping logic at lines 82-84.

Note: This filtering logic duplicates the conditions at lines 82-84. See the refactoring suggestion in the comment for lines 82-84 to consolidate this logic into a shared utility function.

src/providers/GlobalTransactionsBatchProvider.tsx (1)

79-86: LGTM! Consistent batch count filtering.

The filtering logic correctly excludes pulse-buy batches from the batch count calculation, maintaining consistency with the UI filtering in SendModalBatchesTabView.tsx. The updated comments accurately reflect all three batch types being excluded.

src/apps/pulse/components/Buy/PreviewBuy.tsx (8)

1-69: LGTM! Well-structured imports and interface.

The imports are properly organized, and the PreviewBuyProps interface cleanly extends to support both the Relay Buy and Intent SDK flows. The union type for expressIntentResponse and the new optional props for Relay Buy functionality are appropriately typed.


71-137: LGTM! Clean hook setup and state management.

The component properly initializes hooks for both flows, with well-structured conditional logic based on the USE_RELAY_BUY flag. The unified error handling and gas estimation pause during execution are correctly implemented.


139-184: LGTM! Effects properly manage UI state and error clearing.

The copy state timeouts and error clearing logic are implemented correctly. The error clearing effect appropriately resets when inputs change, with proper guards to prevent resetting during active transaction states.


186-234: LGTM! Relay Buy effects properly handle pause and cleanup.

The effects for pausing HomeScreen refresh and cleaning up batches are well-implemented. All dependency arrays now correctly include USE_RELAY_BUY and fromChainId, addressing the concerns from previous reviews. The cleanup logic appropriately handles edge cases.


431-443: LGTM! Clean routing logic.

The unified handler appropriately routes to the correct execution path based on the feature flag. The early validation is consistent with the downstream functions.


571-618: LGTM! Auto-refresh logic is well-guarded.

The auto-refresh effect properly validates dependencies for both flows, prevents refreshing during sensitive states, and includes all necessary dependencies. The 15-second interval with cleanup is appropriate.


620-636: LGTM! User-friendly early return.

The guard clause provides clear feedback when required data is missing and offers a way to close the preview.


638-919: LGTM! Render logic correctly handles both flows.

The component properly calculates and displays values for both Relay Buy and Intent SDK paths. The conditional rendering of paying tokens (USDC for Relay Buy vs. all tokens for Intent SDK), gas estimation display, and transaction calculations are all correctly implemented with appropriate fallbacks and loading states.

Comment on lines +349 to +404
const result = await executeBuy(
buyToken,
usdAmount, // Pass USD amount, not token amount
fromChainId,
userPortfolio
);

if (result === true) {
// If executeBuy succeeded, execute the batch directly
const batchName = `pulse-buy-batch-${fromChainId}`;

const authorization = await getEIP7702AuthorizationIfNeeded(
kit,
fromChainId
);
const batchSend = await kit.sendBatches({
onlyBatchNames: [batchName],
authorization: authorization || undefined,
});

const sentBatch = batchSend.batches[batchName];

if (batchSend.isSentSuccessfully && !sentBatch?.errorMessage) {
// In PillarX we only batch transactions per chainId, this is why sendBatch should only
// have one chainGroup per batch
// chainGroups is an object keyed by chainId, not an array
const userOpHash = sentBatch?.chainGroups?.[fromChainId]?.userOpHash;

if (userOpHash) {
setIsTransactionSuccess(true);
setIsWaitingForSignature(false);
setIsExecuting(false);
if (setBuyFlowPaused) setBuyFlowPaused(false);

// Clean up the batch from kit after successful execution
cleanupBatch(fromChainId, 'success');

// Ensure we have a valid gas fee string
const gasFeeString =
gasCostNative && nativeTokenSymbol
? `≈ ${formatExponentialSmallNumber(limitDigitsNumber(parseFloat(gasCostNative)))} ${nativeTokenSymbol}`
: '≈ 0.00';

showTransactionStatus(userOpHash, gasFeeString);
return;
}

console.error('No userOpHash in batch send result');
throw new Error('No userOpHash returned after batch send');
}

console.error('Batch send failed:', sentBatch?.errorMessage);
throw new Error(sentBatch?.errorMessage || 'Batch send failed');
}

throw new Error('Failed to prepare buy transaction');
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 | 🟠 Major

Propagate specific error messages from executeBuy.

When executeBuy returns a string (error message), the code at line 404 throws a generic "Failed to prepare buy transaction" instead of the specific error. Per the useRelayBuy hook, executeBuy returns Promise<boolean | string> where a string indicates a specific error message that should be shown to the user.

Apply this diff to properly handle all result types:

       const result = await executeBuy(
         buyToken,
         usdAmount,
         fromChainId,
         userPortfolio
       );

       if (result === true) {
         // success path - execute batch
         const batchName = `pulse-buy-batch-${fromChainId}`;
         // ... rest of success logic
         return;
       }

-      throw new Error('Failed to prepare buy transaction');
+      // Handle error cases: string error message or false (generic failure)
+      if (typeof result === 'string') {
+        throw new Error(result); // Propagate specific error message
+      }
+      throw new Error('Failed to prepare buy transaction');
     } catch (err) {
🤖 Prompt for AI Agents
In src/apps/pulse/components/Buy/PreviewBuy.tsx around lines 349 to 404, the
code currently throws a generic "Failed to prepare buy transaction" when
executeBuy returns a non-true value; update the result handling so that if
executeBuy returns a string (an error message) you throw new Error(result) or
otherwise propagate that specific string to the user, keep the existing
treatment for result === true, and only fall back to a generic error for
unexpected non-boolean/non-string values.

@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 18, 2025 09:22 Inactive
@vignesha22 vignesha22 requested a review from RanaBug November 18, 2025 09:33
@github-actions github-actions bot temporarily deployed to Preview (PRO-3720-Relay_Buy) November 18, 2025 10:16 Inactive
@vignesha22 vignesha22 merged commit 28230ec into staging Nov 18, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants