Skip to content

Conversation

@premiumjibles
Copy link
Contributor

@premiumjibles premiumjibles commented Dec 1, 2025

Summary by CodeRabbit

  • New Features

    • Improved chat streaming with pause detection for more accurate loading indicators.
    • Added a confirmation step to the token swap flow and enhanced swap header/stepper UI.
  • Bug Fixes

    • Fixed typo in chat welcome suggestions.
  • Improvements

    • Clearer swap loading states and value displays.
    • Slightly better prioritization of native tokens in asset matching.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
shapeshift-agentic Ready Ready Preview Comment Dec 1, 2025 6:30am

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

📝 Walkthrough

Walkthrough

Introduces streaming pause detection in chat, adds an approval confirmation step to the swap flow (EVM-specific), removes nonce support across EVM/swap transaction code, refactors swap UI with new helper components, and slightly adjusts asset scoring for native tokens.

Changes

Cohort / File(s) Summary
Chat streaming & pause detection
apps/agentic-chat/src/components/Chat.tsx, apps/agentic-chat/src/hooks/useStreamPauseDetector.ts
Added useStreamPauseDetector hook; Chat now derives lastMessageContent, isStreaming (from status) and isPaused, and uses isPaused to drive the loading indicator. useChatContext now exposes status instead of isLoading. Fixed welcome suggestion typo.
Swap execution flow & lifecycle
apps/agentic-chat/src/hooks/useSwapExecution.tsx, apps/agentic-chat/src/hooks/useSwapExecution.tsx
Introduced APPROVAL_CONFIRMATION step between APPROVAL and SWAP; persisted/hydrated state and finalization updated to include confirmation step; EVM-specific approval confirmation flow implemented.
Transaction & executor changes (nonce removal)
apps/agentic-chat/src/utils/chains/evm/transaction.ts, apps/agentic-chat/src/utils/chains/types.ts, apps/agentic-chat/src/utils/swapExecutor.ts
Removed nonce handling from EVM transaction construction and types (TransactionParams no longer has nonce); removed nonce from ExecuteTransactionOptions; eliminated related debug logs; error flows preserved.
Swap UI & components
apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx
Added confirmation step to stepper (5 steps); introduced UsdValue, SwapPair, BuyAmount helper components; added loading/error skeletons and updated header/details rendering to use new components.
Asset scoring
packages/utils/src/AssetService.ts
scoreMatch now detects native tokens via asset.assetId.includes('/slip44:') and gives a small tiebreaker bonus (1001 vs 1000) for exact symbol matches.
Misc: logging cleanup
apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
Removed two console.log debug statements; no control-flow changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas requiring extra attention:
    • useSwapExecution.tsx: persisted/hydrated state migration and APPROVAL_CONFIRMATION branching (EVM vs non-EVM paths).
    • EVM transaction surface (evm/transaction.ts, types.ts, swapExecutor.ts): ensure nonce removal doesn't break integrations or external callers expecting the field.
    • Chat.tsx and useStreamPauseDetector.ts: timer cleanup, dependency correctness, and that status replacement for isLoading is handled across consumers.
    • InitiateSwapUI.tsx: new components and skeleton logic for edge cases when data is absent or errored.
    • AssetService.scoreMatch: verify scoring change doesn't unintentionally affect matching elsewhere.

Possibly related PRs

Poem

🐇 I hop through streams and watch them flow,
Pauses caught where quiet grows,
Five-step swaps with careful cheer,
Native scores a tiny cheer,
A rabbit claps — the code runs clear!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
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 'fix: a bunch of bug fixes' is vague and generic, using non-descriptive language that doesn't convey meaningful information about the specific changes. Replace with a descriptive title that highlights the primary change, such as 'fix: implement pause detection and improve swap UI components' or focus on the most impactful change in this changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 bug-fix-bonanza

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

@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 (1)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)

326-336: Fix incorrect completed steps for non-EVM approval flows.

The final state unconditionally marks APPROVAL_CONFIRMATION as complete whenever needsApproval is true, but APPROVAL_CONFIRMATION is only actually performed for EVM chains. Non-EVM chains skip this step (lines 269-273), so it should not be marked as complete in the final state.

Apply this diff to fix:

       const finalState: SwapState = {
         currentStep: SwapStep.COMPLETE,
         completedSteps: new Set([
           SwapStep.QUOTE,
           SwapStep.NETWORK_SWITCH,
-          ...(needsApproval ? [SwapStep.APPROVAL, SwapStep.APPROVAL_CONFIRMATION] : []),
+          ...(needsApproval ? [SwapStep.APPROVAL] : []),
+          ...(needsApproval && chainNamespace === CHAIN_NAMESPACE.Evm ? [SwapStep.APPROVAL_CONFIRMATION] : []),
           SwapStep.SWAP,
         ]),
         ...(approvalTxHash && { approvalTxHash }),
         ...(swapTxHash && { swapTxHash }),
       }
🧹 Nitpick comments (2)
packages/utils/src/AssetService.ts (1)

73-76: Consider adding a comment to document the native token detection heuristic.

The /slip44: substring check is a reasonable heuristic for detecting native tokens based on the CAIP-19 format, where slip44 namespace indicates BIP-44 coin types (typically native chain assets). The small tiebreaker bonus (1001 vs 1000) is appropriate for subtle ranking preference.

Consider adding a brief comment to clarify the intent:

+    // Native tokens use slip44 namespace in CAIP-19 format (e.g., eip155:1/slip44:60 for ETH)
     const isNative = asset.assetId.includes('/slip44:')
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)

250-279: Consider simplifying the conditional structure.

The approval confirmation logic correctly handles EVM-only receipt waiting, but the else-if branches (lines 269-278) perform identical operations and can be consolidated.

Apply this diff to simplify:

       // Step 3: Approval Confirmation (EVM only)
       if (needsApproval && approvalTxHash && chainNamespace === CHAIN_NAMESPACE.Evm) {
         setState(draft => {
           draft.currentStep = SwapStep.APPROVAL_CONFIRMATION
         })
 
         const publicClient = getPublicClient(wagmiConfig, { chainId: Number(chainReference) })
         if (publicClient) {
           await publicClient.waitForTransactionReceipt({
             hash: approvalTxHash as `0x${string}`,
             confirmations: 1,
           })
         }
 
         setState(draft => {
           draft.completedSteps.add(draft.currentStep)
           draft.currentStep = (draft.currentStep + 1) as SwapStep
           draft.error = undefined
         })
-      } else if (needsApproval) {
-        // Non-EVM: skip confirmation step
-        setState(draft => {
-          draft.currentStep = (draft.currentStep + 1) as SwapStep
-        })
       } else {
-        // No approval needed: skip confirmation step
+        // Non-EVM or no approval: skip confirmation step
         setState(draft => {
           draft.currentStep = (draft.currentStep + 1) as SwapStep
         })
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 325dcc4 and 2eded98.

📒 Files selected for processing (5)
  • apps/agentic-chat/src/components/Chat.tsx (3 hunks)
  • apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (6 hunks)
  • apps/agentic-chat/src/hooks/useStreamPauseDetector.ts (1 hunks)
  • apps/agentic-chat/src/hooks/useSwapExecution.tsx (6 hunks)
  • packages/utils/src/AssetService.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/utils/src/AssetService.ts (1)
packages/types/src/asset.ts (1)
  • asset (3-26)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (2)
packages/caip/src/constants.ts (1)
  • CHAIN_NAMESPACE (46-54)
apps/agentic-chat/src/lib/wagmi-config.ts (1)
  • wagmiConfig (19-19)
apps/agentic-chat/src/components/Chat.tsx (3)
apps/agentic-chat/src/providers/ChatProvider.tsx (1)
  • useChatContext (33-39)
apps/agentic-chat/src/hooks/useStreamPauseDetector.ts (1)
  • useStreamPauseDetector (3-45)
apps/agentic-chat/src/components/LoadingIndicator.tsx (1)
  • LoadingIndicator (1-11)
⏰ 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). (1)
  • GitHub Check: main
🔇 Additional comments (11)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (3)

25-32: LGTM!

The SwapStep enum correctly adds APPROVAL_CONFIRMATION as step 3, maintaining sequential numbering and shifting subsequent steps accordingly.


82-84: LGTM!

The persisted state tracking correctly handles the new APPROVAL_CONFIRMATION step with symmetric serialization and deserialization.

Also applies to: 124-126


397-403: LGTM!

The steps array correctly includes the new APPROVAL_CONFIRMATION step with proper status tracking.

apps/agentic-chat/src/components/Chat.tsx (3)

17-17: LGTM!

The updated suggestion text is clearer and consistent with the other suggestions.


29-40: LGTM!

The memoization correctly extracts text content from the last assistant message, and the pause detection integration properly derives streaming state from the context status. The type assertion on line 35 is safe given the preceding filter.


103-103: LGTM!

The loading indicator now correctly shows only when the stream is paused rather than during the entire loading period, providing better UX feedback.

apps/agentic-chat/src/hooks/useStreamPauseDetector.ts (1)

3-45: LGTM!

The pause detection logic correctly manages the timer lifecycle with proper cleanup. The effect resets the pause state when content changes, sets a new timeout, and cleans up timers on unmount or dependency changes, preventing memory leaks and timer races.

apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (4)

35-44: LGTM!

The steps destructuring and validation correctly handles the new 5-step swap flow including the approval confirmation step.


46-52: LGTM!

The completed count calculation correctly includes all 5 steps and properly counts both COMPLETE and SKIPPED statuses.


65-97: LGTM!

The helper components (UsdValue, SwapPair, BuyAmount) provide clean loading states and placeholders, improving code readability and UX consistency.


157-169: LGTM!

The stepper correctly updates to 5 total steps and adds the "Confirming approval" step with proper connector positioning between approval and swap.

@premiumjibles premiumjibles merged commit 0c6f081 into main Dec 1, 2025
3 of 4 checks passed
@premiumjibles premiumjibles deleted the bug-fix-bonanza branch December 1, 2025 06:31
Copy link

@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 (2)
apps/agentic-chat/src/components/Chat.tsx (1)

29-37: Consider using a type guard instead of type assertion.

The cast on line 35 assumes part.type === 'text' parts have a text property. While the filter ensures this, a safer approach would use a type predicate.

-  const lastMessageContent = useMemo(() => {
-    const assistantMessages = messages.filter(m => m.role === 'assistant')
-    const lastAssistantMessage = assistantMessages[assistantMessages.length - 1]
-    if (!lastAssistantMessage) return undefined
-    return lastAssistantMessage.parts
-      .filter(part => part.type === 'text')
-      .map(part => (part as { type: 'text'; text: string }).text)
-      .join('')
-  }, [messages])
+  const lastMessageContent = useMemo(() => {
+    const assistantMessages = messages.filter(m => m.role === 'assistant')
+    const lastAssistantMessage = assistantMessages[assistantMessages.length - 1]
+    if (!lastAssistantMessage) return undefined
+    const isTextPart = (part: unknown): part is { type: 'text'; text: string } =>
+      typeof part === 'object' && part !== null && 'type' in part && part.type === 'text' && 'text' in part
+    return lastAssistantMessage.parts.filter(isTextPart).map(part => part.text).join('')
+  }, [messages])
apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (1)

68-97: Inline components recreated on every render—consider extracting or memoizing.

UsdValue, SwapPair, and BuyAmount are defined inside the render function, meaning they get new identities on each render. This can cause unnecessary DOM reconciliation and breaks React's component identity optimization.

Either extract these as separate components outside InitiateSwapUI or memoize them:

+const UsdValue = ({ swap, isLoading }: { swap: InitiateSwapOutput['swapData'] | undefined; isLoading: boolean }) => {
+  if (swap?.buyAmountUsd) return <Amount.Fiat value={swap.buyAmountUsd} />
+  if (isLoading) return <Skeleton className="h-5 w-16" />
+  return <>—</>
+}
+
+const SwapPair = ({ swap, isLoading }: { swap: InitiateSwapOutput['swapData'] | undefined; isLoading: boolean }) => {
+  if (swap) {
+    return (
+      <TxStepCard.SwapPair
+        fromSymbol={swap.sellAsset.symbol.toUpperCase()}
+        toSymbol={swap.buyAsset.symbol.toUpperCase()}
+      />
+    )
+  }
+  if (isLoading) return <Skeleton className="h-7 w-40" />
+  return <div className="text-lg font-semibold text-muted-foreground">—</div>
+}
+
+const BuyAmount = ({ swap, isLoading }: { swap: InitiateSwapOutput['swapData'] | undefined; isLoading: boolean }) => {
+  if (swap) {
+    return (
+      <TxStepCard.Amount>
+        <Amount.Crypto value={swap.buyAmountCryptoPrecision} symbol={swap.buyAsset.symbol.toUpperCase()} />
+      </TxStepCard.Amount>
+    )
+  }
+  if (isLoading) return <Skeleton className="h-7 w-32" />
+  return <div className="text-lg font-semibold text-muted-foreground">—</div>
+}
+
 export function InitiateSwapUI({ toolPart }: ToolUIComponentProps) {

Then use them with props: <UsdValue swap={swap} isLoading={isLoading} />

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2eded98 and 578b7ff.

📒 Files selected for processing (9)
  • apps/agentic-chat/src/components/Chat.tsx (3 hunks)
  • apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (6 hunks)
  • apps/agentic-chat/src/hooks/useStreamPauseDetector.ts (1 hunks)
  • apps/agentic-chat/src/hooks/useSwapExecution.tsx (6 hunks)
  • apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (0 hunks)
  • apps/agentic-chat/src/utils/chains/evm/transaction.ts (0 hunks)
  • apps/agentic-chat/src/utils/chains/types.ts (0 hunks)
  • apps/agentic-chat/src/utils/swapExecutor.ts (0 hunks)
  • packages/utils/src/AssetService.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • apps/agentic-chat/src/utils/chains/evm/transaction.ts
  • apps/agentic-chat/src/utils/chains/types.ts
  • apps/agentic-chat/src/utils/swapExecutor.ts
  • apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/agentic-chat/src/hooks/useStreamPauseDetector.ts
  • packages/utils/src/AssetService.ts
🧰 Additional context used
🧬 Code graph analysis (3)
apps/agentic-chat/src/components/Chat.tsx (3)
apps/agentic-chat/src/providers/ChatProvider.tsx (1)
  • useChatContext (33-39)
apps/agentic-chat/src/hooks/useStreamPauseDetector.ts (1)
  • useStreamPauseDetector (3-45)
apps/agentic-chat/src/components/LoadingIndicator.tsx (1)
  • LoadingIndicator (1-11)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
packages/caip/src/constants.ts (1)
  • CHAIN_NAMESPACE (46-54)
apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (3)
apps/agentic-chat/src/components/ui/Amount.tsx (1)
  • Amount (88-92)
apps/agentic-chat/src/components/ui/skeleton.tsx (1)
  • Skeleton (7-7)
apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
  • TxStepCard (149-162)
⏰ 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). (1)
  • GitHub Check: main
🔇 Additional comments (10)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (4)

24-31: LGTM! Enum values correctly reflect the new step sequence.

The addition of APPROVAL_CONFIRMATION at step 3 and reordering of subsequent steps is consistent with the new workflow.


81-86: Verify ordering of persisted phases for approval flow.

The approval_confirmed phase is pushed after approval_complete, but the approval_skipped check on line 84 uses SwapStep.APPROVAL which is correct. However, if approval was completed but confirmation failed, approval_complete would be present without approval_confirmed, which seems intentional for resumability.


307-315: LGTM! Final state correctly includes the new confirmation step.

The spread operator conditionally adds both SwapStep.APPROVAL and SwapStep.APPROVAL_CONFIRMATION when approval was needed, maintaining consistency with the updated workflow.


376-382: Steps array correctly exposes all 5 steps for UI consumption.

The ordering matches the enum definition and the execution flow.

apps/agentic-chat/src/components/Chat.tsx (3)

17-17: Good catch on the typo fix.

"Gib" → "Give" improves user-facing text quality.


39-40: LGTM! Streaming state derivation is clear and correct.

The isStreaming boolean correctly captures both submitted and streaming states, which aligns with the useStreamPauseDetector hook's expectation of an "active loading" boolean.


103-103: Verify intended UX: LoadingIndicator only shows during pauses.

Previously, a loading indicator likely showed continuously during streaming. Now it only appears after pauseThresholdMs (500ms default) of no content updates. Confirm this is the intended behavior—users won't see immediate feedback when streaming starts.

apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (3)

35-44: LGTM! Step destructuring and guard check correctly updated.

The addition of confirmationStep and its inclusion in the null check ensures the UI handles the new 5-step workflow properly.


46-52: LGTM! Completed count calculation includes all steps.

The filter correctly counts both COMPLETE and SKIPPED statuses for progress tracking.


157-172: LGTM! Stepper correctly reflects the 5-step workflow.

The totalCount={5} and new "Confirming approval" step integrate properly with the updated useSwapExecution hook.

Comment on lines +236 to +260
// Step 3: Approval Confirmation (EVM only)
if (needsApproval && approvalTxHash && chainNamespace === CHAIN_NAMESPACE.Evm) {
setState(draft => {
draft.currentStep = SwapStep.APPROVAL_CONFIRMATION
})

const publicClient = getPublicClient(wagmiConfig, { chainId: Number(chainReference) })
if (publicClient) {
await publicClient.waitForTransactionReceipt({
hash: approvalTxHash as `0x${string}`,
confirmations: 1,
})
}

setState(draft => {
draft.completedSteps.add(draft.currentStep)
draft.currentStep = (draft.currentStep + 1) as SwapStep
draft.error = undefined
})
} else {
// Non-EVM or no approval: skip confirmation step
setState(draft => {
draft.currentStep = (draft.currentStep + 1) as SwapStep
})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle publicClient being undefined more explicitly.

When getPublicClient returns undefined, the code silently skips waiting for the receipt and proceeds. This could lead to the swap transaction being submitted before the approval is actually confirmed on-chain, potentially causing the swap to fail.

Consider throwing an error or retrying when the public client is unavailable:

       const publicClient = getPublicClient(wagmiConfig, { chainId: Number(chainReference) })
-      if (publicClient) {
-        await publicClient.waitForTransactionReceipt({
-          hash: approvalTxHash as `0x${string}`,
-          confirmations: 1,
-        })
+      if (!publicClient) {
+        throw new Error(`Unable to get public client for chain ${chainReference}`)
       }
+      await publicClient.waitForTransactionReceipt({
+        hash: approvalTxHash as `0x${string}`,
+        confirmations: 1,
+      })
📝 Committable suggestion

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

Suggested change
// Step 3: Approval Confirmation (EVM only)
if (needsApproval && approvalTxHash && chainNamespace === CHAIN_NAMESPACE.Evm) {
setState(draft => {
draft.currentStep = SwapStep.APPROVAL_CONFIRMATION
})
const publicClient = getPublicClient(wagmiConfig, { chainId: Number(chainReference) })
if (publicClient) {
await publicClient.waitForTransactionReceipt({
hash: approvalTxHash as `0x${string}`,
confirmations: 1,
})
}
setState(draft => {
draft.completedSteps.add(draft.currentStep)
draft.currentStep = (draft.currentStep + 1) as SwapStep
draft.error = undefined
})
} else {
// Non-EVM or no approval: skip confirmation step
setState(draft => {
draft.currentStep = (draft.currentStep + 1) as SwapStep
})
}
// Step 3: Approval Confirmation (EVM only)
if (needsApproval && approvalTxHash && chainNamespace === CHAIN_NAMESPACE.Evm) {
setState(draft => {
draft.currentStep = SwapStep.APPROVAL_CONFIRMATION
})
const publicClient = getPublicClient(wagmiConfig, { chainId: Number(chainReference) })
if (!publicClient) {
throw new Error(`Unable to get public client for chain ${chainReference}`)
}
await publicClient.waitForTransactionReceipt({
hash: approvalTxHash as `0x${string}`,
confirmations: 1,
})
setState(draft => {
draft.completedSteps.add(draft.currentStep)
draft.currentStep = (draft.currentStep + 1) as SwapStep
draft.error = undefined
})
} else {
// Non-EVM or no approval: skip confirmation step
setState(draft => {
draft.currentStep = (draft.currentStep + 1) as SwapStep
})
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants