Repository: shapeshift/web
Pull request #9053: feat: make limit order better
Author: login: gomesalexandre
Created: 2025-03-17T10:22:30Z | Merged: 2025-03-17T15:39:26Z

## Description

This PR:

- decouples loading state and quote fetching (only blocking confirm button now)
- ensures we refetch market-data at interval
- improves loading state so that things are reactive immediately, so long as we have market-data

## Issue (if applicable)

<!-----------------------------------------------------------------------------
If applicable, please link to the github issue and put `closes #XXXX` in your comment to auto-close the issue that your PR fixes.
------------------------------------------------------------------------------>

closes https://github.com/shapeshift/web/issues/8999

## Risk

> High Risk PRs Require 2 approvals

<!-----------------------------------------------------------------------------
Outline the scope of your changes and the risk associated with them. You must use your discretion as an engineer to determine the potential impact of your changes.

WARNING: If your PR introduces a new on-chain transaction or modifies an existing one, please add the "High-Risk" label to this PR and ask for 2 reviewers to approve it before merging.

E.g. an upgrade to `hdwallet` or core state management would be considered higher risk, and might require a full regression test. UI or isolated view changes, or something behind a feature flag may have near zero risk. Small bug fixes might require testing isolated to the specific fix.
------------------------------------------------------------------------------>

Low

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

## Testing

<!-----------------------------------------------------------------------------
We treat every PR to be merged with the same scrutiny as if we were merging directly to production.

Your PR will not be merged if you do not complete the sections below for our engineering and operations teams to test.
------------------------------------------------------------------------------>

- Ensure there are now no loading states on inputs, relevant derived amounts, and rate/gas row
- Sanity check the EIP-712 data (by e.g using Rabby or MM) and ensure that the `buyAmount` and `sellAmount` are matching what we display

### Engineering

<!-----------------------------------------------------------------------------
Include sufficient information here for an engineer to test your PR. This may include how to test locally, in a built environment, changes to infrastructure etc.
------------------------------------------------------------------------------>

- ^

### Operations

- [ ] :checkered_flag: My feature is behind a flag and doesn't require operations testing (yet)

<!-----------------------------------------------------------------------------
If your changes have a user-facing impact, describe how a non-technical QA team can functionally test your changes in a preview environment.

If they are not user-facing please describe how to test for any regressions that may occur.
------------------------------------------------------------------------------>

- ^

## Screenshots (if applicable)

https://jam.dev/c/010a5c1b-de7f-4659-b2e9-1af603f173ee


<!-- av pr metadata
This information is embedded by the av CLI when creating PRs to track the status of stacks when using Aviator. Please do not delete or edit this section of the PR.
```
{"parent":"develop","parentHead":"","trunk":""}
```
-->



Diff:
diff --git a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderConfig.tsx b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderConfig.tsx
index 6188e448ef0..9e4ef9d8c64 100644
--- a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderConfig.tsx
+++ b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderConfig.tsx
@@ -261,7 +261,7 @@ export const LimitOrderConfig = ({
     handleInputChange(priceAmountRef.current)
   }, [handleInputChange])
 
-  const receiveAmount = useMemo(() => {
+  const receiveAmountFormatted = useMemo(() => {
     if (
       bnOrZero(sellAmountCryptoPrecision).isZero() ||
       bnOrZero(limitPrice.buyAssetDenomination).isZero()
@@ -270,11 +270,23 @@ export const LimitOrderConfig = ({
     }
 
     if (priceDirection === PriceDirection.BuyAssetDenomination) {
-      return bnOrZero(sellAmountCryptoPrecision).times(limitPrice.buyAssetDenomination).toString()
+      return bnOrZero(sellAmountCryptoPrecision)
+        .times(limitPrice.buyAssetDenomination)
+        .decimalPlaces(buyAsset.precision)
+        .toString()
     }
 
-    return bnOrZero(sellAmountCryptoPrecision).div(limitPrice.sellAssetDenomination).toString()
-  }, [limitPrice, priceDirection, sellAmountCryptoPrecision])
+    return bnOrZero(sellAmountCryptoPrecision)
+      .div(limitPrice.sellAssetDenomination)
+      .decimalPlaces(sellAsset.precision)
+      .toString()
+  }, [
+    limitPrice,
+    priceDirection,
+    sellAmountCryptoPrecision,
+    buyAsset.precision,
+    sellAsset.precision,
+  ])
 
   const limitOrderExplanation = useMemo(() => {
     if (
@@ -328,7 +340,7 @@ export const LimitOrderConfig = ({
           color='white'
           ml={1}
           fontWeight='medium'
-          value={receiveAmount}
+          value={receiveAmountFormatted}
           symbol={buyAsset.symbol}
           omitDecimalTrailingZeros
         />
@@ -343,7 +355,7 @@ export const LimitOrderConfig = ({
     sellAsset.symbol,
     buyAsset.symbol,
     priceAsset.symbol,
-    receiveAmount,
+    receiveAmountFormatted,
   ])
 
   const marketPriceCryptoPrecision = useMemo(() => {
diff --git a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
index f4cebfb15b1..0b0f834adbf 100644
--- a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
+++ b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
@@ -62,9 +62,10 @@ import {
   selectSellAccountId,
   selectSellAssetBalanceCryptoBaseUnit,
 } from '@/state/slices/limitOrderInputSlice/selectors'
-import { calcLimitPriceBuyAsset } from '@/state/slices/limitOrderSlice/helpers'
+import { makeLimitInputOutputRatio } from '@/state/slices/limitOrderSlice/helpers'
 import { limitOrderSlice } from '@/state/slices/limitOrderSlice/limitOrderSlice'
 import { selectActiveQuoteNetworkFeeUserCurrency } from '@/state/slices/limitOrderSlice/selectors'
+import { useFindMarketDataByAssetIdQuery } from '@/state/slices/marketDataSlice/marketDataSlice'
 import {
   selectIsAnyAccountMetadataLoadedForChainId,
   selectUsdRateByAssetId,
@@ -84,6 +85,8 @@ type LimitOrderInputProps = {
   noExpand?: boolean
 }
 
+const MARKET_DATA_POLLING_INTERVAL = 10_000
+
 export const LimitOrderInput = ({
   isCompact,
   tradeInputRef,
@@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindMarketDataByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindMarketDataByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  const sellAssetMarketDataUsd = useAppSelector(state =>
+    selectUsdRateByAssetId(state, sellAsset.assetId),
+  )
+  const buyAssetMarketDataUsd = useAppSelector(state =>
+    selectUsdRateByAssetId(state, buyAsset.assetId),
+  )
+
   const marketPriceBuyAsset = useMemo(() => {
-    // Ensure we zero out the price if there is an error, and when we are fetching, as `quoteResponse` will be stale data in both cases
-    if (isLimitOrderQuoteFetching || quoteResponseError) return '0'
-    // RTK query returns stale data when `skipToken` is used, so we need to handle that case here.
-    if (!quoteResponse || limitOrderQuoteParams === skipToken) return '0'
+    if (!(sellAssetMarketDataUsd && buyAssetMarketDataUsd)) return '0'
 
-    return calcLimitPriceBuyAsset({
-      sellAmountCryptoBaseUnit: quoteResponse.quote.sellAmount,
-      buyAmountCryptoBaseUnit: quoteResponse.quote.buyAmount,
-      sellAsset,
-      buyAsset,
+    return makeLimitInputOutputRatio({
+      sellPriceUsd: sellAssetMarketDataUsd,
+      buyPriceUsd: buyAssetMarketDataUsd,
+      targetAssetPrecision: buyAsset.precision,
     })
-  }, [
-    isLimitOrderQuoteFetching,
-    quoteResponseError,
-    quoteResponse,
-    limitOrderQuoteParams,
-    sellAsset,
-    buyAsset,
-  ])
+  }, [sellAssetMarketDataUsd, buyAssetMarketDataUsd, buyAsset])
 
   // Update the limit price when the market price changes.
   useEffect(() => {
@@ -376,7 +383,6 @@ export const LimitOrderInput = ({
   const isLoading = useMemo(() => {
     return (
       isCheckingAllowance ||
-      isLimitOrderQuoteFetching ||
       // No account meta loaded for that chain
       !isAnyAccountMetadataLoadedForChainId ||
       (!shouldShowTradeQuoteOrAwaitInput && !isTradeQuoteRequestAborted) ||
@@ -388,7 +394,6 @@ export const LimitOrderInput = ({
   }, [
     isCheckingAllowance,
     isAnyAccountMetadataLoadedForChainId,
-    isLimitOrderQuoteFetching,
     isTradeQuoteRequestAborted,
     isVotingPowerLoading,
     shouldShowTradeQuoteOrAwaitInput,
@@ -538,7 +543,7 @@ export const LimitOrderInput = ({
         hasUserEnteredAmount={hasUserEnteredAmount}
         inputAmountUsd={inputSellAmountUsd}
         isError={isError}
-        isLoading={isLoading}
+        isLoading={isLimitOrderQuoteFetching}
         quoteStatusTranslation={quoteStatusTranslation}
         rate={limitPrice.buyAssetDenomination}
         marketRate={marketPriceBuyAsset}
@@ -573,7 +578,6 @@ export const LimitOrderInput = ({
     hasUserEnteredAmount,
     inputSellAmountUsd,
     isError,
-    isLoading,
     quoteStatusTranslation,
     limitPrice.buyAssetDenomination,
     marketPriceBuyAsset,
@@ -582,6 +586,7 @@ export const LimitOrderInput = ({
     networkFeeUserCurrency,
     renderedRecipientAddress,
     noExpand,
+    isLimitOrderQuoteFetching,
   ])
 
   return (
diff --git a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx
index f7387beb93b..7760435da0f 100644
--- a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx
+++ b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx
@@ -123,7 +123,7 @@ export const SharedTradeInputFooter = ({
             isDisabled={shouldDisableGasRateRowClick}
             rate={rate}
             deltaPercentage={deltaPercentage?.toString()}
-            isLoading={isLoading}
+            isLoading={isLoading && !rate}
             networkFeeFiatUserCurrency={networkFeeFiatUserCurrency}
             swapperName={swapperName}
             swapSource={swapSource}
@@ -156,8 +156,8 @@ export const SharedTradeInputFooter = ({
         {children}
 
         <ButtonWalletPredicate
-          isLoading={isAccountsMetadataLoading && !sellAccountId}
-          loadingText={buttonText}
+          isLoading={isLoading || (isAccountsMetadataLoading && !sellAccountId)}
+          loadingText={isLoading ? undefined : buttonText}
           type='submit'
           colorScheme={isError ? 'red' : 'blue'}
           size='lg-multiline'
diff --git a/src/context/AppProvider/AppContext.tsx b/src/context/AppProvider/AppContext.tsx
index 06f693e7899..bd3d1a69a38 100644
--- a/src/context/AppProvider/AppContext.tsx
+++ b/src/context/AppProvider/AppContext.tsx
@@ -20,7 +20,7 @@ import { snapshotApi } from '@/state/apis/snapshot/snapshot'
 import {
   marketApi,
   marketData,
-  useFindAllQuery,
+  useFindAllMarketDataQuery,
 } from '@/state/slices/marketDataSlice/marketDataSlice'
 import { portfolio } from '@/state/slices/portfolioSlice/portfolioSlice'
 import { preferences } from '@/state/slices/preferencesSlice/preferencesSlice'
@@ -90,7 +90,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
   // load top 1000 assets market data
   // this is needed to sort assets by market cap
   // and covers most assets users will have
-  useFindAllQuery()
+  useFindAllMarketDataQuery()
 
   // Master hook for accounts fetch as a react-query
   useAccountsFetchQuery()
diff --git a/src/state/slices/limitOrderSlice/helpers.ts b/src/state/slices/limitOrderSlice/helpers.ts
index ec0bdc3899f..ffd3d792093 100644
--- a/src/state/slices/limitOrderSlice/helpers.ts
+++ b/src/state/slices/limitOrderSlice/helpers.ts
@@ -22,7 +22,7 @@ export const buildUnsignedLimitOrder = ({
   }
 }
 
-export const calcLimitPriceBuyAsset = ({
+export const calcLimitPriceTargetAsset = ({
   sellAmountCryptoBaseUnit,
   buyAmountCryptoBaseUnit,
   sellAsset,
@@ -37,3 +37,17 @@ export const calcLimitPriceBuyAsset = ({
     .div(fromBaseUnit(sellAmountCryptoBaseUnit, sellAsset.precision))
     .toFixed()
 }
+
+export const makeLimitInputOutputRatio = ({
+  sellPriceUsd,
+  buyPriceUsd,
+  targetAssetPrecision,
+}: {
+  sellPriceUsd: string
+  buyPriceUsd: string
+  targetAssetPrecision: number
+}): string => {
+  const ratio = bnOrZero(buyPriceUsd).div(bnOrZero(sellPriceUsd))
+
+  return ratio.toFixed(targetAssetPrecision)
+}
diff --git a/src/state/slices/limitOrderSlice/selectors.ts b/src/state/slices/limitOrderSlice/selectors.ts
index acee098a5c5..f27c95f9798 100644
--- a/src/state/slices/limitOrderSlice/selectors.ts
+++ b/src/state/slices/limitOrderSlice/selectors.ts
@@ -9,7 +9,7 @@ import {
   selectMarketDataUsd,
   selectUserCurrencyToUsdRate,
 } from '../selectors'
-import { calcLimitPriceBuyAsset } from './helpers'
+import { calcLimitPriceTargetAsset } from './helpers'
 import type { LimitOrderState, LimitOrderSubmissionMetadata } from './types'
 
 import type { ReduxState } from '@/state/reducer'
@@ -169,7 +169,7 @@ export const selectActiveQuoteLimitPrice = createSelector(
   (sellAmountCryptoBaseUnit, buyAmountCryptoBaseUnit, sellAsset, buyAsset) => {
     if (!sellAsset || !buyAsset) return
 
-    const marketPriceBuyAsset = calcLimitPriceBuyAsset({
+    const marketPriceBuyAsset = calcLimitPriceTargetAsset({
       sellAmountCryptoBaseUnit,
       buyAmountCryptoBaseUnit,
       sellAsset,
diff --git a/src/state/slices/marketDataSlice/marketDataSlice.tsx b/src/state/slices/marketDataSlice/marketDataSlice.tsx
index db15a70dd07..d308c2d7790 100644
--- a/src/state/slices/marketDataSlice/marketDataSlice.tsx
+++ b/src/state/slices/marketDataSlice/marketDataSlice.tsx
@@ -250,4 +250,8 @@ export const marketApi = createApi({
   }),
 })
 
-export const { useFindAllQuery, useFindPriceHistoryByAssetIdQuery } = marketApi
+export const {
+  useFindAllQuery: useFindAllMarketDataQuery,
+  useFindPriceHistoryByAssetIdQuery,
+  useFindByAssetIdQuery: useFindMarketDataByAssetIdQuery,
+} = marketApi


Reviews:
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:25:08Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:25:53Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:26:14Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:27:05Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:27:20Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T10:27:43Z
user: login: NeOMakinG
body: https://jam.dev/c/e5447480-5f67-412d-9f40-eb28e5c26d91

🚤 ⚡ 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-17T14:46:17Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T14:50:15Z
user: login: NeOMakinG
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T14:59:33Z
user: login: gomesalexandre
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T15:28:11Z
user: login: NeOMakinG
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T15:30:13Z

Review comments:
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  const sellAssetMarketDataUsd = useAppSelector(state =>
+    selectUsdRateByAssetId(state, sellAsset.assetId),
+  )
+  const buyAssetMarketDataUsd = useAppSelector(state =>
+    selectUsdRateByAssetId(state, buyAsset.assetId),
+  )
+
   const marketPriceBuyAsset = useMemo(() => {
-    // Ensure we zero out the price if there is an error, and when we are fetching, as `quoteResponse` will be stale data in both cases
-    if (isLimitOrderQuoteFetching || quoteResponseError) return '0'
-    // RTK query returns stale data when `skipToken` is used, so we need to handle that case here.
-    if (!quoteResponse || limitOrderQuoteParams === skipToken) return '0'
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: gomesalexandre
body: Not relying on quote data anymore
created_at: 2025-03-17T10:25:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -376,7 +383,6 @@ export const LimitOrderInput = ({
   const isLoading = useMemo(() => {
     return (
       isCheckingAllowance ||
-      isLimitOrderQuoteFetching ||
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: gomesalexandre
body: Not relevant to general-purpose loading state anymore, only for the button loading/disabled state now
created_at: 2025-03-17T10:25:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -538,7 +543,7 @@ export const LimitOrderInput = ({
         hasUserEnteredAmount={hasUserEnteredAmount}
         inputAmountUsd={inputSellAmountUsd}
         isError={isError}
-        isLoading={isLoading}
+        isLoading={isLimitOrderQuoteFetching}
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: gomesalexandre
body: i.e button loading state mentioned above
created_at: 2025-03-17T10:26:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -123,7 +123,7 @@ export const SharedTradeInputFooter = ({
             isDisabled={shouldDisableGasRateRowClick}
             rate={rate}
             deltaPercentage={deltaPercentage?.toString()}
-            isLoading={isLoading}
+            isLoading={isLoading && !rate}
path: src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx
user: login: gomesalexandre
body: Ensures no loading state so long as we have a rate, will only display `RateGasRow` as loading on initial fetch
created_at: 2025-03-17T10:27:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -156,8 +156,8 @@ export const SharedTradeInputFooter = ({
         {children}
 
         <ButtonWalletPredicate
-          isLoading={isAccountsMetadataLoading && !sellAccountId}
-          loadingText={buttonText}
+          isLoading={isLoading || (isAccountsMetadataLoading && !sellAccountId)}
+          loadingText={isLoading ? undefined : buttonText}
path: src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputFooter/SharedTradeInputFooter.tsx
user: login: gomesalexandre
body: Ensures no copy when we are loading or things will look off
created_at: 2025-03-17T10:27:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -250,4 +250,5 @@ export const marketApi = createApi({
   }),
 })
 
-export const { useFindAllQuery, useFindPriceHistoryByAssetIdQuery } = marketApi
+export const { useFindAllQuery, useFindPriceHistoryByAssetIdQuery, useFindByAssetIdQuery } =
path: src/state/slices/marketDataSlice/marketDataSlice.tsx
user: login: gomesalexandre
body: Not sure why we've never exposed this before but here it is
created_at: 2025-03-17T10:27:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: NeOMakinG
body: Not related to the PR but the naming `useFindByAssetIdQuery` isn't very descriptive, I can't determine what is it doing by reading those queries
created_at: 2025-03-17T14:36:23Z
author_association: COLLABORATOR
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: gomesalexandre
body: Actually related! 

Very valid point and I don't think we should change the naming in the slice (we usually use sliceName.endpoints.endpointName which already contains the domain, and more rarely queries) but rather it's probably a good idea to re-expose the queries with the name of the domain to make things more obvious at first read. 
created_at: 2025-03-17T14:50:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: NeOMakinG
body: Yes I was thinking about renaming the export instead of the slice of course, the endpoint name seems valid considering you are reading the slice scope, but outside it would make sense to rename the query hook to a more readable name
created_at: 2025-03-17T14:59:33Z
author_association: COLLABORATOR
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: gomesalexandre
body: gm? c8d92f97cedbf601dfa5d64eab3e00848791ac84

created_at: 2025-03-17T15:28:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,26 +241,30 @@ export const LimitOrderInput = ({
     isFetching: isLimitOrderQuoteFetching,
   } = useQuoteLimitOrderQuery(limitOrderQuoteParams)
 
+  useFindByAssetIdQuery(sellAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
+
+  useFindByAssetIdQuery(buyAsset.assetId, {
+    pollingInterval: MARKET_DATA_POLLING_INTERVAL,
+  })
path: src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx
user: login: NeOMakinG
body: purfect
created_at: 2025-03-17T15:30:13Z
author_association: COLLABORATOR

PR comments:
user: login: 0xApotheosis
created_at: 2025-03-18T05:11:46Z
body: @gomesalexandre @NeOMakinG this guy needs some love before it can be released. Unfortunately, it's in the current release, and is some merge fun to revert (though not impossible).

The buy/sell limit price ratios are backwards:

<img width="520" alt="Screenshot 2025-03-18 at 4 02 33 pm" src="https://github.com/user-attachments/assets/24de4ec4-94d2-433c-bb31-681cfee2e826" /><img width="525" alt="Screenshot 2025-03-18 at 4 02 55 pm" src="https://github.com/user-attachments/assets/82a0cb94-c258-4440-9506-eb9157fc8468" />

The `receiveAmountFormatted` value used here changes when changing the `PriceDirection` changes, likely due to precision/rounding issues as we format it in the buy asset precision(?). `receiveAmountFormatted` should be a constant that does not change based on the state of `PriceDirection`.

<img width="463" alt="Screenshot 2025-03-18 at 4 03 25 pm" src="https://github.com/user-attachments/assets/e8763e92-b5e8-431a-8e8b-5aedbed57ef9" />
<img width="467" alt="Screenshot 2025-03-18 at 4 03 29 pm" src="https://github.com/user-attachments/assets/bb2d9533-a579-4081-a0e8-aa73669a7061" />
author_association: MEMBER
user: login: 0xApotheosis
created_at: 2025-03-18T06:03:10Z
body: The fix was actually simple once I found what was inverted 🙏  https://github.com/shapeshift/web/pull/9074/files
author_association: MEMBER