Skip to content

feat(swap-widget): toggle sell input between crypto and fiat amount - #12436

Merged
kaladinlight merged 12 commits into
developfrom
feat/swap-widget-fiat-input-toggle
Jun 17, 2026
Merged

feat(swap-widget): toggle sell input between crypto and fiat amount#12436
kaladinlight merged 12 commits into
developfrom
feat/swap-widget-fiat-input-toggle

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jun 17, 2026

Copy link
Copy Markdown
Member

Description

Lets users flip the swap widget's sell input between entering a crypto amount and entering a fiat (USD) amount. Tapping the USD subtext under the sell field toggles the mode: in fiat mode the user types a dollar amount (shown with a $ prefix) and the equivalent crypto is used for quoting/execution, with the crypto equivalent shown as the subtext.

Design notes:

  • sellAmount/sellAmountBaseUnit stay always-crypto — the single source of truth read by quoting, approval, execution, guards and redirect is untouched. Fiat is a thin input/display layer (isSellAmountFiat, sellAmountFiat) on top.
  • State changes split cleanly across two machine events: SET_SELL_AMOUNT carries the full sell amount (crypto amount, base unit, and the fiat string), while SET_SELL_FIAT_MODE only flips the mode and seeds the fiat display. Mode and amount are orthogonal, so e.g. clearing the amount on swap-direction is a single unconditional dispatch.
  • Conversions live in the handler/hook layer (where the USD price is available), via pure helpers fiatToCrypto/cryptoToFiatInput.
  • useSellFiatSync reconciles the crypto amount when the USD price arrives asynchronously or the sell asset changes while in fiat mode (the machine has no access to price).
  • When an asset has no USD price, the toggle is hidden and the fiat subtext is omitted entirely (rather than a misleading $0.00); switching to a price-less asset while in fiat mode falls back to crypto entry.

Issue (if applicable)

closes #12417
closes SS-5696
https://linear.app/shapeshift-dao/issue/SS-5696/unable-to-type-in-a-dollar-amount-to-swap

Risk

High Risk PRs Require 2 approvals

Low risk. Changes are isolated to the swap-widget sell input (UI + sell-amount state/handlers). No new or modified on-chain transactions — quoting/approval/execution still consume the same sellAmountBaseUnit crypto value as before; fiat is converted to that crypto value before any quote is requested.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

None directly. Only the sell amount the user enters changes representation; the downstream crypto base-unit value and all swapper/quote/approval paths are unchanged.

Testing

Engineering

Unit tests (pnpm --filter @shapeshiftoss/swap-widget test) cover the pure converters (fiatToCrypto, cryptoToFiatInput), the sync decision (computeSellFiatSyncAction), and the machine fiat-mode state transitions (SET_SELL_AMOUNT carrying fiat, SET_SELL_FIAT_MODE flipping mode, crypto cleared on asset change in fiat mode).

Manual (demo app under packages/swap-widget/src/demo):

  1. Default sell input is crypto; subtext shows the $ USD value with a ⇅ icon.
  2. Tap the subtext → input switches to fiat ($ prefix); subtext reads ≈ <crypto> <SYMBOL>.
  3. Type a fiat amount → quote uses the correct crypto amount (cross-check the ≈ <crypto> subtext).
  4. Tap again → returns to crypto mode with the equivalent crypto amount.
  5. In fiat mode, switch the sell asset to another priced asset → $ amount persists, crypto recomputes.
  6. Switch to an asset with no USD price → reverts to crypto-only, toggle hidden, no $0.00.
  7. Clear the input → action button reads "Enter an amount".

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

User-facing: in the swap widget, on the Sell field, tap the USD value line beneath the amount to switch between entering a token amount and a dollar amount. Verify entering a dollar amount produces a correct quote and the displayed token equivalent is sensible.

Screenshots (if applicable)

Summary by CodeRabbit

  • New Features
    • Added fiat-based entry for the sell amount with real-time conversion to the corresponding crypto value.
    • Added a fiat/crypto toggle for the sell input, including an automatic sync of fiat values when a USD price is available.
    • Updated the sell-side USD display to support fiat estimates in fiat mode, with a cleaner placeholder when pricing is unavailable.
  • Style
    • Improved USD value/toggle and fiat prefix presentation in the UI.
  • Tests
    • Expanded coverage for fiat conversion and fiat-mode swap machine behavior.

kaladinlight and others added 6 commits June 16, 2026 16:40
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sync price

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Also add the new fiat context fields to existing machine test fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…m comments

Omit the sell fiat subtext entirely when the asset has no USD price (a $0.00
would mislead), pull the $ prefix snug against the amount, and drop redundant
comments in favor of self-documenting code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kaladinlight
kaladinlight requested a review from a team as a code owner June 17, 2026 17:38
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@kaladinlight, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 4 minutes and 42 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f28c12d3-9539-4be9-a612-39dd497eeea5

📥 Commits

Reviewing files that changed from the base of the PR and between e81391d and 3bd9181.

📒 Files selected for processing (2)
  • packages/swap-widget/src/hooks/useSwapDisplayValues.ts
  • packages/swap-widget/src/machines/__tests__/types.test.ts
📝 Walkthrough

Walkthrough

The PR adds fiat-mode sell input to the swap widget. It introduces isSellAmountFiat and sellAmountFiat state machine context fields, a SET_SELL_FIAT_MODE event, three fiat/crypto conversion utilities, a useSellFiatSync hook for async USD price synchronization, updated swap handlers with fiat/crypto branching, conditional fiat UI in InputStep with a toggle button, and supporting CSS styling.

Changes

Fiat Sell Input Mode

Layer / File(s) Summary
SwapMachine type contracts for fiat state and events
packages/swap-widget/src/machines/swapMachine.ts
SwapMachineContext adds isSellAmountFiat: boolean and sellAmountFiat: string fields. SwapMachineEvent union extends SET_SELL_AMOUNT with fiatValue: string payload and adds SET_SELL_FIAT_MODE event variant carrying isFiat: boolean. Context initialization defaults isSellAmountFiat to false and sellAmountFiat to empty string. assignSellAsset conditionally clears crypto amounts when fiat mode is enabled, otherwise recalculates sellAmountBaseUnit using the new asset precision. assignSellAmount persists fiatValue into sellAmountFiat. assignSellFiatMode toggles isSellAmountFiat from SET_SELL_FIAT_MODE. The input state wires SET_SELL_FIAT_MODE to the action.
Fiat/crypto conversion utilities and tests
packages/swap-widget/src/utils/fiatConversion.ts, packages/swap-widget/src/utils/__tests__/fiatConversion.test.ts
fiatToCrypto converts fiat strings and USD price to crypto display amount and base-unit string, validating finiteness and positivity. cryptoToFiat converts crypto base-unit amounts to 2-decimal fiat strings with missing/zero handling. computeSellFiatSyncAction conditionally emits fiat synchronization events: returns null when not in fiat mode or when sellAmountFiat is empty; emits SET_SELL_FIAT_MODE with isFiat: false when USD price is missing; emits SET_SELL_AMOUNT only if sellAmountBaseUnit is currently missing. Tests cover decimal precision for 18-decimal and 6-decimal assets, empty/invalid fiat inputs, missing/zero prices, sub-cent conversions, and idempotent sync behavior.
SwapMachine state transitions and fiat mode tests
packages/swap-widget/src/machines/__tests__/swapMachine.test.ts
Updates all SET_SELL_AMOUNT event payloads across 24 state-transition tests to include fiatValue field for schema alignment. Adds comprehensive fiat sell mode test suite verifying createInitialContext() defaults to crypto mode, SET_SELL_FIAT_MODE flips mode and populates sellAmountFiat while preserving crypto amounts, and SET_SELL_ASSET in fiat mode clears stale crypto fields while retaining the fiat value.
Guard and type test context updates
packages/swap-widget/src/machines/__tests__/guards.test.ts, packages/swap-widget/src/machines/__tests__/types.test.ts
Updates test context factories to initialize isSellAmountFiat: false and sellAmountFiat: '' for guard predicate tests. Adds fiat state fields to SwapMachineContext test fixture and includes fiatValue: '' in SET_SELL_AMOUNT event test fixture.
useSwapHandlers fiat/crypto branching and mode toggle
packages/swap-widget/src/hooks/useSwapHandlers.ts
Imports fiat/crypto conversion utilities. Extends handleSwapTokens to include fiatValue in sell-amount reset event. Updates handleSellAmountChange to accept optional sellAssetUsdPrice and branch on isSellAmountFiat: converts fiat input to crypto amounts while storing fiatValue in fiat mode, or parses crypto input to base units and clears fiatValue in crypto mode. Adds handleToggleSellFiat to switch modes: disables fiat when enabled, or when enabling computes fiatValue from current sellAmountBaseUnit before dispatching. Both handlers are exposed in returned object.
useSellFiatSync async price sync hook
packages/swap-widget/src/hooks/useSellFiatSync.ts
Introduces new hook that reads swap machine context via selectors for fiat mode, fiat input string, current base-unit amount, and asset precision. Calls computeSellFiatSyncAction with the async USD price and input state, then conditionally sends the resulting action to the machine actor inside a useEffect. Enables synchronization when USD price arrives or sell inputs change.
InputStep fiat-aware input and toggle UI
packages/swap-widget/src/components/InputStep.tsx
Extends InputStepProps with onToggleSellFiat callback and optional sellAssetUsdPrice in onSellAmountChange. Updates sell amount input to conditionally display $ prefix in fiat mode, select displayed value from either sellAmountFiat or sellAmount based on mode, sanitize numeric input, and call onSellAmountChange(sanitized, sellAssetUsdPrice). Replaces static sell USD display with fiat-toggle button when sellAssetUsdPrice is available (switches label between "≈" estimate in fiat mode and computed sellUsdValue otherwise), or empty placeholder when absent.
SwapWidget fiat sync hook and handler integration
packages/swap-widget/src/components/SwapWidget.tsx
Imports and calls useSellFiatSync hook with displayValues.sellAssetUsdPrice. Extracts handleToggleSellFiat from useSwapHandlers destructuring and wires it into InputStep via onToggleSellFiat prop.
USD toggle and fiat prefix styling
packages/swap-widget/src/components/SwapWidget.css
Adds .ssw-usd-value-toggle class for button-like toggle layout with flex centering, padding, cursor pointer, and hover background color. Adds .ssw-fiat-prefix class for large $ prefix typography with increased font size, weight, and color plus negative right margin for visual alignment with the numeric input.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant InputStep
  participant useSwapHandlers
  participant SwapMachine
  participant useSellFiatSync

  User->>InputStep: clicks fiat toggle button
  InputStep->>useSwapHandlers: onToggleSellFiat(sellAssetUsdPrice)
  useSwapHandlers->>useSwapHandlers: cryptoToFiat(...) when enabling fiat
  useSwapHandlers->>SwapMachine: send SET_SELL_FIAT_MODE { isFiat }
  SwapMachine->>SwapMachine: assignSellFiatMode updates context

  Note over useSellFiatSync: USD price arrives async
  useSellFiatSync->>useSellFiatSync: computeSellFiatSyncAction(...)
  useSellFiatSync->>SwapMachine: send SET_SELL_AMOUNT if base-unit changed

  User->>InputStep: types fiat amount "$50"
  InputStep->>useSwapHandlers: onSellAmountChange("50", sellAssetUsdPrice)
  useSwapHandlers->>useSwapHandlers: fiatToCrypto("50", price, precision)
  useSwapHandlers->>SwapMachine: send SET_SELL_AMOUNT with crypto amounts + fiatValue
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A dollar sign hops into the field,
The swap now speaks in fiat revealed.
Toggle once and watch it flip—
Crypto or USD at your fingertip!
The machine learns both tongues with care,
A bunny-built bridge beyond compare. 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding a toggle feature to switch the sell input between crypto and fiat amount modes.
Linked Issues check ✅ Passed The PR fully addresses the linked issue #12417 by implementing the ability to type in dollar amounts for swaps. Users can now toggle between crypto and fiat input modes via the USD subtext.
Out of Scope Changes check ✅ Passed All changes are scoped to the swap-widget sell input UI and state handlers. No modifications to on-chain transaction logic, quoting, approval, or execution—only the fiat toggle feature is implemented.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/swap-widget-fiat-input-toggle

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/swap-widget/src/machines/__tests__/types.test.ts (1)

50-110: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include SET_SELL_FIAT_MODE in the event-union fixture.

Line 50 claims full SwapMachineEvent coverage, but Lines 51-108 omit the new SET_SELL_FIAT_MODE variant while still asserting toHaveLength(20). This leaves the contract test incomplete.

Proposed fix
     const events: SwapMachineEvent[] = [
@@
       { type: 'SET_SELL_AMOUNT', amount: '1.0', amountBaseUnit: '1000000000000000000' },
+      {
+        type: 'SET_SELL_FIAT_MODE',
+        isFiat: true,
+        fiatValue: '100',
+        amount: '0.03125',
+        amountBaseUnit: '31250000000000000',
+      },
@@
-    expect(events).toHaveLength(20)
+    expect(events).toHaveLength(21)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/swap-widget/src/machines/__tests__/types.test.ts` around lines 50 -
110, The test labeled 'SwapMachineEvent discriminated union covers all event
types' in the types.test.ts file is missing the SET_SELL_FIAT_MODE event variant
in its events array. Add a SET_SELL_FIAT_MODE event object to the events array
(with appropriate properties for this event type) and update the toHaveLength
assertion from 20 to 21 to reflect the complete coverage of all SwapMachineEvent
variants.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/swap-widget/src/utils/fiatConversion.ts`:
- Around line 30-31: The return statement that calculates fiat amount by
multiplying cryptoAmount by priceBn and calling toFixed(2) is forcing precision
to 2 decimals, which rounds small non-zero values to "0.00" and unintentionally
clears the sellAmountBaseUnit in the sync flow. Instead of always forcing 2
decimal places, preserve the calculated value with appropriate precision that
allows small non-zero amounts to remain visible and maintain the underlying
crypto amount value. Adjust the toFixed call to use a precision level that
balances display requirements without collapsing legitimate non-zero values.

---

Outside diff comments:
In `@packages/swap-widget/src/machines/__tests__/types.test.ts`:
- Around line 50-110: The test labeled 'SwapMachineEvent discriminated union
covers all event types' in the types.test.ts file is missing the
SET_SELL_FIAT_MODE event variant in its events array. Add a SET_SELL_FIAT_MODE
event object to the events array (with appropriate properties for this event
type) and update the toHaveLength assertion from 20 to 21 to reflect the
complete coverage of all SwapMachineEvent variants.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ecd6d27-5b3d-419f-ac30-97430a634e22

📥 Commits

Reviewing files that changed from the base of the PR and between a07b47c and ea03768.

📒 Files selected for processing (12)
  • packages/swap-widget/src/components/InputStep.tsx
  • packages/swap-widget/src/components/SwapWidget.css
  • packages/swap-widget/src/components/SwapWidget.tsx
  • packages/swap-widget/src/hooks/useSellFiatSync.ts
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/__tests__/guards.test.ts
  • packages/swap-widget/src/machines/__tests__/swapMachine.test.ts
  • packages/swap-widget/src/machines/__tests__/types.test.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/machines/types.ts
  • packages/swap-widget/src/utils/__tests__/fiatConversion.test.ts
  • packages/swap-widget/src/utils/fiatConversion.ts

Comment thread packages/swap-widget/src/utils/fiatConversion.ts
…IAT_MODE mode-only

SET_SELL_AMOUNT now carries the full sell amount (crypto amount, base unit, fiat
string) and SET_SELL_FIAT_MODE only flips the mode and seeds the fiat display.
This removes the overloaded event and lets handleSwapTokens clear the amount with
a single unconditional dispatch instead of branching on fiat mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/swap-widget/src/hooks/useSwapHandlers.ts (1)

16-19: ⚡ Quick win

Add explicit return types on modified handlers and exported hook result.

Lines 45-46 and Line 67-68 introduce/modify handler signatures without explicit return types, and Lines 165-175 return an inferred object shape. Please type these explicitly to match the TS ruleset.

Suggested diff
 type UseSwapHandlersParams = {
   partnerCode?: string
   allowShapeshiftRedirect: boolean
 }
+
+type UseSwapHandlersResult = {
+  handleSwapTokens: () => void
+  handleSellAssetSelect: (asset: Asset) => void
+  handleBuyAssetSelect: (asset: Asset) => void
+  handleSellAmountChange: (value: string, sellAssetUsdPrice?: string) => void
+  handleToggleSellFiat: (sellAssetUsdPrice?: string) => void
+  handleSelectRate: (rate: TradeRate) => void
+  handleSlippageChange: (value: string) => void
+  redirectToShapeShift: () => void
+  handleButtonClick: () => void
+}
 
 export const useSwapHandlers = ({
   partnerCode,
   allowShapeshiftRedirect,
-}: UseSwapHandlersParams) => {
+}: UseSwapHandlersParams): UseSwapHandlersResult => {
@@
-  const handleSellAmountChange = useCallback(
-    (value: string, sellAssetUsdPrice?: string) => {
+  const handleSellAmountChange = useCallback(
+    (value: string, sellAssetUsdPrice?: string): void => {
@@
-  const handleToggleSellFiat = useCallback(
-    (sellAssetUsdPrice?: string) => {
+  const handleToggleSellFiat = useCallback(
+    (sellAssetUsdPrice?: string): void => {

As per coding guidelines, "**/*.{ts,tsx}: ALWAYS use explicit types for function parameters and return values in TypeScript".

Also applies to: 45-46, 67-68, 165-175

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/swap-widget/src/hooks/useSwapHandlers.ts` around lines 16 - 19, Add
explicit return type annotations to all function handlers and the exported
useSwapHandlers hook to comply with the TypeScript ruleset. Specifically, add
return types to the handler functions at lines 45-46 and 67-68, and add a return
type annotation to the useSwapHandlers hook itself (lines 165-175) instead of
relying on type inference. Ensure each modified handler and the hook's return
value have clear, explicit type signatures that match the actual data being
returned.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/swap-widget/src/hooks/useSwapHandlers.ts`:
- Around line 16-19: Add explicit return type annotations to all function
handlers and the exported useSwapHandlers hook to comply with the TypeScript
ruleset. Specifically, add return types to the handler functions at lines 45-46
and 67-68, and add a return type annotation to the useSwapHandlers hook itself
(lines 165-175) instead of relying on type inference. Ensure each modified
handler and the hook's return value have clear, explicit type signatures that
match the actual data being returned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ee902b3-c1dd-4928-a6f1-c74c72adcfae

📥 Commits

Reviewing files that changed from the base of the PR and between ea03768 and 22c7a6b.

📒 Files selected for processing (8)
  • packages/swap-widget/src/components/InputStep.tsx
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/__tests__/swapMachine.test.ts
  • packages/swap-widget/src/machines/__tests__/types.test.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/machines/types.ts
  • packages/swap-widget/src/utils/__tests__/fiatConversion.test.ts
  • packages/swap-widget/src/utils/fiatConversion.ts
💤 Files with no reviewable changes (1)
  • packages/swap-widget/src/components/InputStep.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/swap-widget/src/machines/tests/types.test.ts
  • packages/swap-widget/src/machines/types.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/utils/fiatConversion.ts
  • packages/swap-widget/src/utils/tests/fiatConversion.test.ts

…toToFiat

SET_SELL_FIAT_MODE now carries only { isFiat } and SET_SELL_AMOUNT is the sole
writer of the sell amount triple (amount, base unit, fiat string). Entering fiat
mode seeds the fiat string via SET_SELL_AMOUNT then flips the mode; exiting is a
bare mode flip. Also rename cryptoToFiatInput -> cryptoToFiat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/swap-widget/src/utils/fiatConversion.ts (1)

20-30: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve tiny non-zero fiat when seeding fiat mode.

Line 30 forces two decimals for an editable value. Tiny non-zero crypto can become "0.00", and the fiat sync path can then clear sellAmountBaseUnit. Use higher precision (or trim trailing zeros) when generating the fiat input seed.

Proposed minimal fix
 export const cryptoToFiat = (
   amountBaseUnit: string | undefined,
   price: string,
   precision: number,
 ): string => {
@@
   const cryptoAmount = BigAmount.fromBaseUnit({ value: amountBaseUnit, precision }).toPrecision()
-  return bn(cryptoAmount).times(priceBn).toFixed(2)
+  const fiatValue = bn(cryptoAmount).times(priceBn)
+  return fiatValue.toFixed(Math.max(2, precision)).replace(/\.?0+$/, '')
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/swap-widget/src/utils/fiatConversion.ts` around lines 20 - 30, The
cryptoToFiat function's return statement uses .toFixed(2) which forces exactly
two decimal places, causing tiny but non-zero fiat amounts to round to "0.00"
and break fiat mode seeding. Instead of forcing two decimals, apply higher
precision to the return value or trim trailing zeros to preserve the actual
calculated amount so that small non-zero values are not lost when used as input
seeds in fiat mode.
🧹 Nitpick comments (1)
packages/swap-widget/src/machines/types.ts (1)

38-38: ⚡ Quick win

Use a more specific boolean payload name for fiat mode.

On Line 38, isFiat is valid but ambiguous in this state machine. Prefer isSellAmountFiat to match context naming and reduce cross-layer confusion in handlers/tests.

Suggested diff
-  | { type: 'SET_SELL_FIAT_MODE'; isFiat: boolean }
+  | { type: 'SET_SELL_FIAT_MODE'; isSellAmountFiat: boolean }

As per coding guidelines, “Use descriptive boolean variable names with is, has, can, should prefixes” and “Use camelCase for variables, functions, and methods with descriptive names that explain the purpose.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/swap-widget/src/machines/types.ts` at line 38, The boolean payload
name `isFiat` in the SET_SELL_FIAT_MODE action type is ambiguous and does not
clearly indicate what is being set to fiat mode. Rename the `isFiat` property to
`isSellAmountFiat` throughout the codebase to be more descriptive and match the
context naming conventions. This change should be applied to the type definition
in the state machine types, as well as all places where this action is
dispatched and handled to ensure consistency across handlers and tests.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/swap-widget/src/utils/fiatConversion.ts`:
- Around line 20-30: The cryptoToFiat function's return statement uses
.toFixed(2) which forces exactly two decimal places, causing tiny but non-zero
fiat amounts to round to "0.00" and break fiat mode seeding. Instead of forcing
two decimals, apply higher precision to the return value or trim trailing zeros
to preserve the actual calculated amount so that small non-zero values are not
lost when used as input seeds in fiat mode.

---

Nitpick comments:
In `@packages/swap-widget/src/machines/types.ts`:
- Line 38: The boolean payload name `isFiat` in the SET_SELL_FIAT_MODE action
type is ambiguous and does not clearly indicate what is being set to fiat mode.
Rename the `isFiat` property to `isSellAmountFiat` throughout the codebase to be
more descriptive and match the context naming conventions. This change should be
applied to the type definition in the state machine types, as well as all places
where this action is dispatched and handled to ensure consistency across
handlers and tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac1bc496-3992-4f6e-8d35-6355ea25f825

📥 Commits

Reviewing files that changed from the base of the PR and between 22c7a6b and a8f863e.

📒 Files selected for processing (6)
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/__tests__/swapMachine.test.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/machines/types.ts
  • packages/swap-widget/src/utils/__tests__/fiatConversion.test.ts
  • packages/swap-widget/src/utils/fiatConversion.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/tests/swapMachine.test.ts
  • packages/swap-widget/src/utils/tests/fiatConversion.test.ts

kaladinlight and others added 2 commits June 17, 2026 14:37
…vert on no price

- computeSellFiatSyncAction: revert to crypto entry before the empty-fiat check so a
  price-less asset in fiat mode never strands the user, and only fill crypto when it's
  missing instead of reconciling — never overwrite an exact amount (no cent-snapping).
- handleToggleSellFiat: keep the exact crypto on entering fiat mode (seed fiat for
  display only), so toggling crypto->fiat->crypto round-trips the amount unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/swap-widget/src/utils/fiatConversion.ts`:
- Around line 52-60: The validation check on line 52 for sellAssetUsdPrice only
catches falsy values, but invalid prices like '0' or non-finite strings pass
through and cause fiatToCrypto to fail while still emitting SET_SELL_AMOUNT with
empty values. Update the condition on line 52 to validate that sellAssetUsdPrice
is not just truthy but also represents a valid positive number (not zero, NaN,
or non-finite), and return the same fallback response of { type:
'SET_SELL_FIAT_MODE', isFiat: false } for all invalid prices to maintain
consistent behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52b0fda3-007b-4ede-8d1e-7cafc275da0c

📥 Commits

Reviewing files that changed from the base of the PR and between a8f863e and e81391d.

📒 Files selected for processing (6)
  • packages/swap-widget/src/components/SwapWidget.tsx
  • packages/swap-widget/src/hooks/useSellFiatSync.ts
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/utils/__tests__/fiatConversion.test.ts
  • packages/swap-widget/src/utils/fiatConversion.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/swap-widget/src/hooks/useSellFiatSync.ts
  • packages/swap-widget/src/hooks/useSwapHandlers.ts
  • packages/swap-widget/src/machines/swapMachine.ts
  • packages/swap-widget/src/components/SwapWidget.tsx
  • packages/swap-widget/src/utils/tests/fiatConversion.test.ts

Comment thread packages/swap-widget/src/utils/fiatConversion.ts
kaladinlight and others added 2 commits June 17, 2026 14:57
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Normalize sellAssetUsdPrice to undefined when the market price is missing, zero,
or non-finite, so fiat-mode gating (toggle visibility, handlers, useSellFiatSync
revert) handles price-less assets consistently instead of letting '0' slip through
as a valid price.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kaladinlight
kaladinlight enabled auto-merge (squash) June 17, 2026 21:04
@kaladinlight
kaladinlight merged commit 5eea5cf into develop Jun 17, 2026
4 checks passed
@kaladinlight
kaladinlight deleted the feat/swap-widget-fiat-input-toggle branch June 17, 2026 21:14
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.

Unable to type in a dollar amount to swap

1 participant