Conversation
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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. Comment |
There was a problem hiding this comment.
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: IncludeUSE_RELAY_BUYin the polling effect dependencies.This
useEffectnow branches onUSE_RELAY_BUY, but the dependency array still omits it. Because the flag defaults tofalse, the interval is created with the “Express Intent” branch and never re-evaluates when Remote Config later flips the flag totrue, 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
refreshPreviewBuyDatais memoized withoutUSE_RELAY_BUYin its dependencies. Because the flag comes from Remote Config and flips after mount, this callback keeps the initialfalsebranch 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis 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.tsxsrc/apps/pulse/components/Buy/PreviewBuy.tsxsrc/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.tsxsrc/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.tssrc/apps/pulse/components/Buy/PreviewBuy.tsxsrc/apps/pulse/hooks/useRelayBuy.tssrc/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.tsxsrc/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.tsxsrc/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
Deploying x with
|
| 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 |
There was a problem hiding this comment.
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 initLine 404: when
USE_RELAY_BUYflips to true butisRelayInitializedis stillfalse,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, sobuyOfferstaysnull, 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 withuseRelayBuy = falseuntil its async init completes, even when the parent already opened Preview under the Relay flow. If the user clicks Confirm during that window,handleConfirmBuyfalls back toshortlistBid(), treats theBuyOfferinexpressIntentResponseas an Intent response, and we explode onintentResponse.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
minimumFetchIntervalMillisis 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 secondsAlternatively, 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:
- Parameterizing the existing tests to run for both modes
- Adding Relay Buy-specific tests that verify distinct behavior (e.g., gas estimation display, different transaction execution paths)
- 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
useEffectdoesn't check if the component is still mounted before callingsetUseRelayBuyandsetIsInitialized. 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis 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.tsxsrc/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.tsxsrc/apps/pulse/components/Buy/PreviewBuy.tsxsrc/apps/pulse/hooks/useRelayBuy.tssrc/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.tsxsrc/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.tsxsrc/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.comto.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: falsevalue.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: falseensures 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?: booleanflag 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
getUseRelayBuyhelper 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
useRelayBuyflag to the interface maintains backward compatibility while enabling Relay Buy support.
21-47: LGTM! Proper parameter threading and dependency management.The
useRelayBuyflag is correctly threaded through togetStepStatusand 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
useRelayBuyparameter in the expected hook call, using the appropriate default value offalse.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
txHashfromblockchainTxHash(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
completedTxHashfor Intent SDK Buy (ResourceLock flow)- Use
txHashfor 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
getDispensableAssetsto populatepayingTokens- Using
waitForwithtestIdinstead ofsetTimeoutand 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
useRelayBuyflag is correctly:
- Added with a sensible default (
false)- Threaded to
useTransactionStatushook- Used for conditional progress step rendering
- Passed to
TransactionInfofor hash display logicAlso 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
getStepStatusfunction correctly implements the differentiation between Intent SDK Buy and Relay Buy:
- Introduced
isIntentSdkBuy = isBuy && !useRelayBuyfor 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, andTransactionStatus.
There was a problem hiding this comment.
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_BUYandmaxStableCoinBalanceare 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 callgetUserOperationStatuswith a bid hash and surface a false failure. Likewise, fresh balance data can pointmaxStableCoinBalanceat a different chain, so Relay buys start polling the wrong network and never complete. Snapshot both the relay flag and the from-chain atshowTransactionStatustime 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
transactionContextinside 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 ExpressIntentResponseat line 201 bypasses type safety. SincesetExInRespalready acceptsExpressIntentResponse | BuyOffer | null(per the prop type at line 60), passbuyOfferdirectly 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
📒 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.tssrc/apps/pulse/components/Transaction/TransactionInfo.tsxsrc/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.tsxsrc/apps/pulse/components/App/HomeScreen.tsxsrc/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.tsxsrc/apps/pulse/components/App/HomeScreen.tsxsrc/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.tsxsrc/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.tsxsrc/apps/pulse/components/Transaction/TransactionDetails.tsxsrc/apps/pulse/components/Transaction/TransactionInfo.tsxsrc/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.tsxsrc/apps/pulse/hooks/useRelayBuy.tssrc/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
displayTxHashkeeps 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_BUYin 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
buyOfferis 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!
executeBuycorrectly 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
buildTransactionsfor gas estimation is the right approach, ensuring consistency between estimated and actual transactions.
| 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'; |
There was a problem hiding this comment.
Can we categorise for consistency
| PayingToken as PayingTokenType, | ||
| SelectedToken, | ||
| } from '../../types/tokens'; | ||
| import { Token } from '../../../../services/tokensData'; |
There was a problem hiding this comment.
| import { Token } from '../../../../services/tokensData'; | |
| // services | |
| import { Token } from '../../../../services/tokensData'; |
There was a problem hiding this comment.
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
📒 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
expressIntentResponseand 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_BUYflag. 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_BUYandfromChainId, 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.
| 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'); |
There was a problem hiding this comment.
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.
Description
How Has This Been Tested?
Screenshots (if appropriate):
Types of changes
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Tests