Skip to content

Commit

Permalink
Merge branch 'develop' into feat_defi_ledger_gate
Browse files Browse the repository at this point in the history
  • Loading branch information
NeOMakinG authored Aug 27, 2024
2 parents 18ee336 + f5fd8c7 commit 37ed444
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const CustomAssetAcknowledgement: React.FC<CustomAssetAcknowledgementProp
// Add asset to the store
dispatch(assetsSlice.actions.upsertAsset(asset))
// Use the market API to get the market data for the custom asset
dispatch(marketApi.endpoints.findByAssetIds.initiate([asset.assetId]))
dispatch(marketApi.endpoints.findByAssetId.initiate(asset.assetId))
// Once the custom asset is in the store, proceed as if it was a normal asset
handleAssetClick(asset)
}, [dispatch, handleAssetClick, asset])
Expand Down
41 changes: 29 additions & 12 deletions src/components/TransactionHistoryRows/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Dex, TransferType } from '@shapeshiftoss/unchained-client'
import dayjs from 'dayjs'
import { useMemo, useState } from 'react'
import type { TxDetails } from 'hooks/useTxDetails/useTxDetails'
import { useFindPriceHistoryByAssetIdsQuery } from 'state/slices/marketDataSlice/marketDataSlice'
import { useFindPriceHistoryByAssetIdQuery } from 'state/slices/marketDataSlice/marketDataSlice'
import type { FindPriceHistoryByAssetIdArgs } from 'state/slices/marketDataSlice/types'
import { selectCryptoPriceHistoryTimeframe } from 'state/slices/selectors'
import { useAppSelector } from 'state/store'
Expand All @@ -17,11 +17,20 @@ export const useTradeFees = ({ txDetails }: { txDetails: TxDetails }) => {
selectCryptoPriceHistoryTimeframe(state, HistoryTimeframe.ALL),
)

const [priceHistoryParams, setPriceHistoryParams] = useState<
const [sellAssetPriceHistoryParams, setSellAssetPriceHistoryParams] = useState<
FindPriceHistoryByAssetIdArgs | typeof skipToken
>(skipToken)

const { isLoading } = useFindPriceHistoryByAssetIdsQuery(priceHistoryParams)
const [buyAssetPriceHistoryParams, setBuyAssetPriceHistoryParams] = useState<
FindPriceHistoryByAssetIdArgs | typeof skipToken
>(skipToken)

const { isLoading: isSellAssetPriceHistoryLoading } = useFindPriceHistoryByAssetIdQuery(
sellAssetPriceHistoryParams,
)
const { isLoading: isBuyAssetPriceHistoryLoading } = useFindPriceHistoryByAssetIdQuery(
buyAssetPriceHistoryParams,
)

const buy = useMemo(
() => txDetails.transfers.find(transfer => transfer.type === TransferType.Receive),
Expand All @@ -37,16 +46,16 @@ export const useTradeFees = ({ txDetails }: { txDetails: TxDetails }) => {
if (!(txDetails.tx.trade && buy && sell)) return
if (txDetails.tx.trade.dexName !== Dex.CowSwap) return

const assetIds = []
if (!cryptoPriceHistoryData?.[buy.asset.assetId]) assetIds.push(buy.asset.assetId)
if (!cryptoPriceHistoryData?.[sell.asset.assetId]) assetIds.push(sell.asset.assetId)

if (assetIds.length > 0 && !isLoading) {
setPriceHistoryParams({
assetIds,
if (!cryptoPriceHistoryData?.[buy.asset.assetId] && !isBuyAssetPriceHistoryLoading)
setBuyAssetPriceHistoryParams({
assetId: buy.asset.assetId,
timeframe: HistoryTimeframe.ALL,
})
if (!cryptoPriceHistoryData?.[sell.asset.assetId] && !isSellAssetPriceHistoryLoading)
setSellAssetPriceHistoryParams({
assetId: sell.asset.assetId,
timeframe: HistoryTimeframe.ALL,
})
}

const tradeFees = getTradeFees({
sell,
Expand All @@ -56,7 +65,15 @@ export const useTradeFees = ({ txDetails }: { txDetails: TxDetails }) => {
})

return tradeFees
}, [txDetails.tx.trade, txDetails.tx.blockTime, buy, sell, cryptoPriceHistoryData, isLoading])
}, [
txDetails.tx.trade,
txDetails.tx.blockTime,
buy,
sell,
cryptoPriceHistoryData,
isBuyAssetPriceHistoryLoading,
isSellAssetPriceHistoryLoading,
])

return tradeFees
}
54 changes: 28 additions & 26 deletions src/context/AppProvider/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { usePrevious, useToast } from '@chakra-ui/react'
import { fromAccountId } from '@shapeshiftoss/caip'
import { MetaMaskShapeShiftMultiChainHDWallet } from '@shapeshiftoss/hdwallet-shapeshift-multichain'
import type { AccountMetadataById } from '@shapeshiftoss/types'
import { useQuery } from '@tanstack/react-query'
import { useQueries } from '@tanstack/react-query'
import { DEFAULT_HISTORY_TIMEFRAME } from 'constants/Config'
import { LanguageTypeEnum } from 'constants/LanguageTypeEnum'
import React, { useEffect } from 'react'
Expand Down Expand Up @@ -243,31 +243,33 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
}, [dispatch, requestedAccountIds, portfolioLoadingStatus])

const marketDataPollingInterval = 60 * 15 * 1000 // refetch data every 15 minutes
useQuery({
queryKey: ['marketData', {}],
queryFn: async () => {
await dispatch(
marketApi.endpoints.findByAssetIds.initiate(portfolioAssetIds, {
// Since we use react-query as a polling wrapper, every initiate call *is* a force refetch here
forceRefetch: true,
}),
)
useQueries({
queries: portfolioAssetIds.map(assetId => ({
queryKey: ['marketData', assetId],
queryFn: async () => {
await dispatch(
marketApi.endpoints.findByAssetId.initiate(assetId, {
// Since we use react-query as a polling wrapper, every initiate call *is* a force refetch here
forceRefetch: true,
}),
)

// used to trigger mixpanel init after load of market data
dispatch(marketData.actions.setMarketDataLoaded())

// We *have* to return a value other than undefined from react-query queries, see
// https://tanstack.com/query/v4/docs/react/guides/migrating-to-react-query-4#undefined-is-an-illegal-cache-value-for-successful-queries
return null
},
// once the portfolio is loaded, fetch market data for all portfolio assets
// and start refetch timer to keep market data up to date
enabled: portfolioLoadingStatus !== 'loading',
refetchInterval: marketDataPollingInterval,
// Do NOT refetch market data in background to avoid spamming coingecko
refetchIntervalInBackground: false,
// Do NOT refetch market data on window focus to avoid spamming coingecko
refetchOnWindowFocus: false,
// used to trigger mixpanel init after load of market data
dispatch(marketData.actions.setMarketDataLoaded())

// We *have* to return a value other than undefined from react-query queries, see
// https://tanstack.com/query/v4/docs/react/guides/migrating-to-react-query-4#undefined-is-an-illegal-cache-value-for-successful-queries
return null
},
// once the portfolio is loaded, fetch market data for all portfolio assets
// and start refetch timer to keep market data up to date
enabled: portfolioLoadingStatus !== 'loading',
refetchInterval: marketDataPollingInterval,
// Do NOT refetch market data in background to avoid spamming coingecko
refetchIntervalInBackground: false,
// Do NOT refetch market data on window focus to avoid spamming coingecko
refetchOnWindowFocus: false,
})),
})

/**
Expand Down Expand Up @@ -308,7 +310,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
useEffect(() => {
// early return for routes that don't contain an assetId, no need to refetch marketData granularly
if (!routeAssetId) return
dispatch(marketApi.endpoints.findByAssetIds.initiate([routeAssetId]))
dispatch(marketApi.endpoints.findByAssetId.initiate(routeAssetId))
}, [dispatch, routeAssetId])

// If the assets aren't loaded, then the app isn't ready to render
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { thorchainAssetId, toAssetId } from '@shapeshiftoss/caip'
import { supportsETH } from '@shapeshiftoss/hdwallet-core'
import { SwapperName } from '@shapeshiftoss/swapper'
import type { Asset } from '@shapeshiftoss/types'
import { isUtxoChainId } from '@shapeshiftoss/utils'
import { useQuery } from '@tanstack/react-query'
import { Confirm as ReusableConfirm } from 'features/defi/components/Confirm/Confirm'
import { Summary } from 'features/defi/components/Summary'
Expand Down Expand Up @@ -197,7 +198,7 @@ export const Confirm: React.FC<ConfirmProps> = ({ accountId, onNext }) => {
return null
}, [isRunePool, quoteData?.quote.memo])

const { executeTransaction, isEstimatedFeesDataLoading, estimatedFeesData } = useSendThorTx({
const { isEstimatedFeesDataLoading, estimatedFeesData } = useSendThorTx({
accountId: accountId ?? null,
assetId,
amountCryptoBaseUnit: toBaseUnit(state?.deposit.cryptoAmount, asset.precision),
Expand All @@ -206,6 +207,66 @@ export const Confirm: React.FC<ConfirmProps> = ({ accountId, onNext }) => {
fromAddress: fromAddress ?? null,
})

const { amountMinusFeesCryptoBaseUnit, amountMinusFeesCryptoPrecision } = useMemo(() => {
if (isUtxoChainId(feeAsset.chainId)) {
return {
amountMinusFeesCryptoBaseUnit: bn(toBaseUnit(state?.deposit.cryptoAmount, asset.precision)),
amountMinusFeesCryptoPrecision: state?.deposit.cryptoAmount,
}
}

if (!estimatedFeesData)
return {
amountMinusFeesCryptoBaseUnit: undefined,
amountMinusFeesCryptoPrecision: undefined,
}

if (
state?.deposit.sendMax &&
estimatedFeesData &&
!isUtxoChainId(feeAsset.chainId) &&
feeAsset.assetId === asset.assetId
) {
return {
amountMinusFeesCryptoBaseUnit: bn(
toBaseUnit(state?.deposit.cryptoAmount, asset.precision),
).minus(estimatedFeesData.txFeeCryptoBaseUnit),
amountMinusFeesCryptoPrecision: fromBaseUnit(
bn(toBaseUnit(state?.deposit.cryptoAmount, asset.precision)).minus(
estimatedFeesData.txFeeCryptoBaseUnit,
),
asset.precision,
),
}
}

return {
amountMinusFeesCryptoBaseUnit: bn(toBaseUnit(state?.deposit.cryptoAmount, asset.precision)),
amountMinusFeesCryptoPrecision: state?.deposit.cryptoAmount,
}
}, [
state?.deposit.cryptoAmount,
asset.precision,
feeAsset,
asset.assetId,
estimatedFeesData,
state?.deposit.sendMax,
])

const { executeTransaction } = useSendThorTx({
accountId: accountId ?? null,
assetId,
amountCryptoBaseUnit: amountMinusFeesCryptoBaseUnit?.toFixed() ?? null,
action: isRunePool ? 'depositRunepool' : 'depositSavers',
memo,
fromAddress: fromAddress ?? null,
enableEstimateFees: Boolean(
!state?.deposit.sendMax ||
isUtxoChainId(feeAsset.chainId) ||
feeAsset.assetId !== asset.assetId,
),
})

const estimatedGasCryptoPrecision = useMemo(() => {
if (!estimatedFeesData) return
return fromBaseUnit(estimatedFeesData.txFeeCryptoBaseUnit, feeAsset.precision)
Expand Down Expand Up @@ -387,7 +448,9 @@ export const Confirm: React.FC<ConfirmProps> = ({ accountId, onNext }) => {
disableSmartContractDeposit ||
isTradingActive === false
}
loading={state.loading || !fromAddress || isAddressByteCodeLoading}
loading={
state.loading || !amountMinusFeesCryptoBaseUnit || !fromAddress || isAddressByteCodeLoading
}
loadingText={translate('common.confirm')}
headerText='modals.confirm.deposit.header'
>
Expand All @@ -402,7 +465,9 @@ export const Confirm: React.FC<ConfirmProps> = ({ accountId, onNext }) => {
<RawText>{asset.name}</RawText>
</Stack>
<Row.Value>
<Amount.Crypto value={state.deposit.cryptoAmount} symbol={asset.symbol} />
<Skeleton isLoaded={!!amountMinusFeesCryptoPrecision}>
<Amount.Crypto value={amountMinusFeesCryptoPrecision} symbol={asset.symbol} />
</Skeleton>
</Row.Value>
</Row>
</Row>
Expand Down
76 changes: 41 additions & 35 deletions src/hooks/useFetchPriceHistories/useFetchPriceHistories.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { AssetId } from '@shapeshiftoss/caip'
import type { HistoryTimeframe } from '@shapeshiftoss/types'
import { useQuery } from '@tanstack/react-query'
import { useQueries } from '@tanstack/react-query'
import { DEFAULT_HISTORY_TIMEFRAME } from 'constants/Config'
import { useEffect } from 'react'
import { marketApi, marketData } from 'state/slices/marketDataSlice/marketDataSlice'
import { selectPortfolioLoadingStatus, selectSelectedCurrency } from 'state/slices/selectors'
import { useAppDispatch, useAppSelector } from 'state/store'

const { findPriceHistoryByAssetIds, findPriceHistoryByFiatSymbol } = marketApi.endpoints
const { findPriceHistoryByFiatSymbol } = marketApi.endpoints

const marketDataPollingInterval = 60 * 15 * 1000 // refetch data every 15 minutes

Expand All @@ -16,47 +16,53 @@ export const useFetchPriceHistories = (assetIds: AssetId[], timeframe: HistoryTi
const symbol = useAppSelector(selectSelectedCurrency)

useEffect(() => {
dispatch(findPriceHistoryByAssetIds.initiate({ assetIds, timeframe }))
assetIds.forEach(assetId => {
dispatch(marketApi.endpoints.findPriceHistoryByAssetId.initiate({ assetId, timeframe }))
})
}, [assetIds, dispatch, timeframe])

const portfolioLoadingStatus = useAppSelector(selectPortfolioLoadingStatus)

useQuery({
queryKey: ['marketData', assetIds],
queryFn: async () => {
// Only commented out to make it clear we do NOT want to use RTK options here
// We use react-query as a wrapper because it allows us to disable refetch for background tabs
// const opts = { subscriptionOptions: { pollingInterval: marketDataPollingInterval } }
const timeframe = DEFAULT_HISTORY_TIMEFRAME
useQueries({
queries: assetIds.map(assetId => ({
queryKey: ['marketData', assetId],
queryFn: async () => {
// Only commented out to make it clear we do NOT want to use RTK options here
// We use react-query as a wrapper because it allows us to disable refetch for background tabs
// const opts = { subscriptionOptions: { pollingInterval: marketDataPollingInterval } }
const timeframe = DEFAULT_HISTORY_TIMEFRAME

await Promise.all([
dispatch(
marketApi.endpoints.findPriceHistoryByAssetIds.initiate(
{
timeframe,
assetIds,
},
// Since we use react-query as a polling wrapper, every initiate call *is* a force refetch here
{ forceRefetch: true },
await Promise.all(
assetIds.map(assetId =>
dispatch(
marketApi.endpoints.findPriceHistoryByAssetId.initiate(
{
timeframe,
assetId,
},
// Since we use react-query as a polling wrapper, every initiate call *is* a force refetch here
{ forceRefetch: true },
),
),
),
),
])
)

// used to trigger mixpanel init after load of market data
dispatch(marketData.actions.setMarketDataLoaded())
// used to trigger mixpanel init after load of market data
dispatch(marketData.actions.setMarketDataLoaded())

// We *have* to return a value other than undefined from react-query queries, see
// https://tanstack.com/query/v4/docs/react/guides/migrating-to-react-query-4#undefined-is-an-illegal-cache-value-for-successful-queries
return null
},
// once the portfolio is loaded, fetch market data for all portfolio assets
// and start refetch timer to keep market data up to date
enabled: portfolioLoadingStatus !== 'loading',
refetchInterval: marketDataPollingInterval,
// Do NOT refetch market data in background to avoid spamming coingecko
refetchIntervalInBackground: false,
// Do NOT refetch market data on window focus to avoid spamming coingecko
refetchOnWindowFocus: false,
// We *have* to return a value other than undefined from react-query queries, see
// https://tanstack.com/query/v4/docs/react/guides/migrating-to-react-query-4#undefined-is-an-illegal-cache-value-for-successful-queries
return null
},
// once the portfolio is loaded, fetch market data for all portfolio assets
// and start refetch timer to keep market data up to date
enabled: portfolioLoadingStatus !== 'loading',
refetchInterval: marketDataPollingInterval,
// Do NOT refetch market data in background to avoid spamming coingecko
refetchIntervalInBackground: false,
// Do NOT refetch market data on window focus to avoid spamming coingecko
refetchOnWindowFocus: false,
})),
})

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/RFOX/components/Stake/StakeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const StakeInput: React.FC<StakeInputProps & StakeRouteProps> = ({

useEffect(() => {
// hydrate FOX.ARB market data in case the user doesn't hold it
dispatch(marketApi.endpoints.findByAssetIds.initiate([stakingAssetId]))
dispatch(marketApi.endpoints.findByAssetId.initiate(stakingAssetId))
}, [dispatch, selectedAssetId, stakingAssetId])
useEffect(() => {
// Only set this once, never collapse out
Expand Down
6 changes: 4 additions & 2 deletions src/state/apis/fiatRamps/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ export const useFetchFiatAssetMarketData = (assetIds: AssetId[]): void => {
const timeframe = HistoryTimeframe.DAY

if (assetIds.length > 0) {
dispatch(marketApi.endpoints.findPriceHistoryByAssetIds.initiate({ assetIds, timeframe }))
dispatch(marketApi.endpoints.findByAssetIds.initiate(assetIds))
assetIds.forEach(assetId => {
dispatch(marketApi.endpoints.findPriceHistoryByAssetId.initiate({ assetId, timeframe }))
dispatch(marketApi.endpoints.findByAssetId.initiate(assetId))
})
}
}, [assetIds, dispatch])
}
Loading

0 comments on commit 37ed444

Please sign in to comment.