From 271aae08239d248cc43f814cabb93be66150cc66 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:06:57 +0100 Subject: [PATCH 01/33] wip: wip --- src/assets/translations/en/main.json | 2 ++ src/pages/ThorChainLP/AvailablePools.tsx | 18 +++++++---- src/pages/ThorChainLP/Pool/Pool.tsx | 25 ++++++++++++++-- .../AddLiquitity/AddLiquidityInput.tsx | 30 +++++++++++++++---- .../ReusableLpStatus/TransactionRow.tsx | 25 +++++++++++++++- src/react-queries/index.ts | 14 ++++++++- 6 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index a4a784c28c3..d337d2a3207 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -127,6 +127,8 @@ "yourBalance": "Your balance", "signTransaction": "Sign transaction", "halted": "Halted", + "poolHalted": "Pool Halted", + "available": "Available", "carousel": { "next": "Next", "prev": "Previous", diff --git a/src/pages/ThorChainLP/AvailablePools.tsx b/src/pages/ThorChainLP/AvailablePools.tsx index 0cf0d1f32a9..2b64a4ed9c8 100644 --- a/src/pages/ThorChainLP/AvailablePools.tsx +++ b/src/pages/ThorChainLP/AvailablePools.tsx @@ -51,7 +51,7 @@ type PoolButtonProps = { const PoolButton = ({ pool }: PoolButtonProps) => { const history = useHistory() - const { data: isTradingActive } = useQuery( + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( reactQueries.common.isTradingActive({ assetId: pool.assetId, swapperName: SwapperName.Thorchain, @@ -106,11 +106,17 @@ const PoolButton = ({ pool }: PoolButtonProps) => { - {isTradingActive === false && ( - - - - )} + + {isTradingActive === false ? ( + + + + ) : ( + + + + )} + diff --git a/src/pages/ThorChainLP/Pool/Pool.tsx b/src/pages/ThorChainLP/Pool/Pool.tsx index 543fcd351d7..30aaf20c597 100644 --- a/src/pages/ThorChainLP/Pool/Pool.tsx +++ b/src/pages/ThorChainLP/Pool/Pool.tsx @@ -10,9 +10,11 @@ import { Heading, IconButton, Stack, + Tooltip, } from '@chakra-ui/react' import type { AccountId, AssetId } from '@shapeshiftoss/caip' import { thorchainAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import { useQuery } from '@tanstack/react-query' import type { Property } from 'csstype' import React, { useCallback, useMemo } from 'react' @@ -100,6 +102,13 @@ export const Pool = () => { return parsedPools.find(pool => pool.opportunityId === routeOpportunityId) }, [params, parsedPools]) + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( + reactQueries.common.isTradingActive({ + assetId: foundPool?.assetId, + swapperName: SwapperName.Thorchain, + }), + ) + const poolAssetIds = useMemo(() => { if (!foundPool) return [] @@ -186,9 +195,19 @@ export const Pool = () => { > - + + + diff --git a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx index c23f4b61d06..721fe3b3b1a 100644 --- a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx @@ -18,6 +18,7 @@ import { } from '@chakra-ui/react' import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' import { fromAssetId, thorchainAssetId, thorchainChainId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset, KnownChainIds, MarketData } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -183,6 +184,13 @@ export const AddLiquidityInput: React.FC = ({ const [poolAsset, setPoolAsset] = useState(foundPoolAsset) + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( + reactQueries.common.isTradingActive({ + assetId: poolAsset?.assetId, + swapperName: SwapperName.Thorchain, + }), + ) + useEffect(() => { if (!(poolAsset && parsedPools)) return // We only want to run this effect in the standalone AddLiquidity page @@ -730,7 +738,12 @@ export const AddLiquidityInput: React.FC = ({ useEffect(() => { ;(async () => { - if (!actualRuneCryptoLiquidityAmount || !actualAssetCryptoLiquidityAmount || !poolAsset) + if ( + !actualRuneCryptoLiquidityAmount || + !actualAssetCryptoLiquidityAmount || + !poolAsset || + isTradingActive === false + ) return const runeAmountCryptoThorPrecision = convertPrecision({ @@ -775,6 +788,7 @@ export const AddLiquidityInput: React.FC = ({ isAsymAssetSide, isAsymRuneSide, virtualRuneFiatLiquidityAmount, + isTradingActive, ]) useEffect(() => { @@ -955,6 +969,7 @@ export const AddLiquidityInput: React.FC = ({ = ({ const errorCopy = useMemo(() => { // Order matters here. Since we're dealing with two assets potentially, we want to show the most relevant error message possible i.e - // 1 pool asset balance - // 2. pool asset fee balance, since gas would usually be more expensive on the pool asset fee side vs. RUNE side - // 3. RUNE balance - // 4. RUNE fee balance + // 1. pool halted + // 2. pool asset balance + // 3. pool asset fee balance, since gas would usually be more expensive on the pool asset fee side vs. RUNE side + // 4. RUNE balance + // 5. RUNE fee balance // Not enough *pool* asset, but possibly enough *fee* asset + if (isTradingActive === false) return translate('common.poolHalted') if (poolAsset && notEnoughPoolAssetError) return translate('common.insufficientFunds') // Not enough *fee* asset if (poolAssetFeeAsset && notEnoughFeeAssetError) @@ -1041,6 +1058,7 @@ export const AddLiquidityInput: React.FC = ({ return null }, [ + isTradingActive, notEnoughFeeAssetError, notEnoughPoolAssetError, notEnoughRuneError, @@ -1142,6 +1160,7 @@ export const AddLiquidityInput: React.FC = ({ size='lg' colorScheme={errorCopy ? 'red' : 'blue'} isDisabled={ + isTradingActive === false || !confirmedQuote || isVotingPowerLoading || !hasEnoughAssetBalance || @@ -1163,6 +1182,7 @@ export const AddLiquidityInput: React.FC = ({ isAllowanceDataLoading || isApprovalTxPending || isSweepNeededLoading || + isTradingActiveLoading || isEstimatedPoolAssetFeesDataLoading } onClick={handleSubmit} diff --git a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx index c9c6d4ef02d..744f865b003 100644 --- a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx @@ -23,6 +23,7 @@ import { type FeeDataEstimate, FeeDataKey, } from '@shapeshiftoss/chain-adapters' +import { SwapperName } from '@shapeshiftoss/swapper' import type { KnownChainIds } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -99,6 +100,18 @@ export const TransactionRow: React.FC = ({ const [txId, setTxId] = useState(null) const wallet = useWallet().state.wallet + const { + data: isTradingActive, + isLoading: isTradingActiveLoading, + isRefetching: isTradingActiveRefetching, + refetch: refetchIsTradingActive, + } = useQuery( + reactQueries.common.isTradingActive({ + assetId: poolAssetId, + swapperName: SwapperName.Thorchain, + }), + ) + const runeAccountId = accountIdsByChainId[thorchainChainId] const poolAssetAccountId = accountIdsByChainId[poolAsset?.assetId ? fromAssetId(poolAsset.assetId).chainId : ''] @@ -376,6 +389,10 @@ export const TransactionRow: React.FC = ({ return return (async () => { + // Refetch the trading active state JIT to ensure the pool didn't just become halted + const { data: isTradingActiveData } = await refetchIsTradingActive() + if (!isTradingActiveData) return + const accountId = isRuneTx ? runeAccountId : poolAssetAccountId if (!accountId) throw new Error(`No accountId found for asset ${asset.assetId}`) const { account } = fromAccountId(accountId) @@ -614,7 +631,13 @@ export const TransactionRow: React.FC = ({ size='lg' colorScheme='blue' onClick={handleSignTx} - isLoading={status === TxStatus.Pending || isInboundAddressLoading} + isDisabled={isTradingActive === false} + isLoading={ + status === TxStatus.Pending || + isInboundAddressLoading || + isTradingActiveLoading || + isTradingActiveRefetching + } > {translate('common.signTransaction')} diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 3464826f121..dec38da7542 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -65,16 +65,28 @@ const common = createQueryKeys('common', { from && getSupportedEvmChainIds().includes(fromAssetId(assetId).chainId as KnownChainIds), }), - isTradingActive: ({ assetId, swapperName }: { assetId: AssetId; swapperName: SwapperName }) => ({ + isTradingActive: ({ + assetId, + swapperName, + }: { + assetId: AssetId | undefined + swapperName: SwapperName + }) => ({ queryKey: ['isTradingActive', assetId, swapperName], queryFn: async () => { + if (!assetId) throw new Error('assetId is required') + const maybeIsTradingActive = await isTradingActive(assetId, swapperName) // Do not return things in a monadic way so that we can leverage native react-query error-handling if (maybeIsTradingActive.isErr()) throw maybeIsTradingActive.unwrapErr() return maybeIsTradingActive.unwrap() }, + enabled: Boolean(assetId), + // Go stale instantly staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, refetchOnWindowFocus: true, refetchOnMount: true, refetchInterval: 60_000, From 66beabb8be80801f7b9041a0dbb7c08e0c5b6410 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:35:33 +0100 Subject: [PATCH 02/33] feat: throw on pool just halted at sign step so we don't infinitely spin --- .../components/ReusableLpStatus/TransactionRow.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx index 744f865b003..e3ddf2cfea6 100644 --- a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx @@ -391,7 +391,7 @@ export const TransactionRow: React.FC = ({ return (async () => { // Refetch the trading active state JIT to ensure the pool didn't just become halted const { data: isTradingActiveData } = await refetchIsTradingActive() - if (!isTradingActiveData) return + if (!isTradingActiveData) throw new Error('Pool Halted') const accountId = isRuneTx ? runeAccountId : poolAssetAccountId if (!accountId) throw new Error(`No accountId found for asset ${asset.assetId}`) @@ -570,6 +570,7 @@ export const TransactionRow: React.FC = ({ wallet, isRuneTx, inboundAddressData, + refetchIsTradingActive, runeAccountId, poolAssetAccountId, amountCryptoPrecision, @@ -583,6 +584,12 @@ export const TransactionRow: React.FC = ({ const txIdLink = useMemo(() => `${asset?.explorerTxLink}${txId}`, [asset?.explorerTxLink, txId]) + const confirmTranslation = useMemo(() => { + if (isTradingActive === false) return translate('common.poolHalted') + + return translate('common.signTransaction') + }, [isTradingActive, translate]) + if (!asset) return null return ( @@ -629,7 +636,7 @@ export const TransactionRow: React.FC = ({ From 67b2af8feba5eacfb5b8867cd16a86b22176701f Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:44:43 +0100 Subject: [PATCH 03/33] feat: improve thorchain savers JIT halted checks --- .../Deposit/components/Confirm.tsx | 32 ++++++++++------- .../Overview/ThorchainSaversOverview.tsx | 14 ++++---- .../Withdraw/components/Confirm.tsx | 34 ++++++++++++------- src/state/apis/swapper/swapperApi.ts | 3 +- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx index eb9678104b4..44973c4729b 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx @@ -15,6 +15,7 @@ import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/t import { supportsETH } from '@shapeshiftoss/hdwallet-core' import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset, KnownChainIds } from '@shapeshiftoss/types' +import { useQuery } from '@tanstack/react-query' import { getConfig } from 'config' import { getOrCreateContractByType } from 'contracts/contractManager' import { ContractType } from 'contracts/types' @@ -28,6 +29,7 @@ import type { import { DefiStep } from 'features/defi/contexts/DefiManagerProvider/DefiCommon' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { useTranslate } from 'react-polyglot' +import { reactQueries } from 'react-queries' import { encodeFunctionData, getAddress } from 'viem' import { Amount } from 'components/Amount/Amount' import { AssetIcon } from 'components/AssetIcon' @@ -58,7 +60,6 @@ import { import { fromThorBaseUnit, getThorchainFromAddress, toThorBaseUnit } from 'lib/utils/thorchain' import { BASE_BPS_POINTS } from 'lib/utils/thorchain/constants' import { getInboundAddressDataForChain } from 'lib/utils/thorchain/getInboundAddressDataForChain' -import { swapperApi } from 'state/apis/swapper/swapperApi' import { getMaybeThorchainSaversDepositQuote, getThorchainSaversPosition, @@ -75,7 +76,7 @@ import { selectPortfolioCryptoBalanceBaseUnitByFilter, selectSelectedCurrency, } from 'state/slices/selectors' -import { store, useAppDispatch, useAppSelector } from 'state/store' +import { store, useAppSelector } from 'state/store' import { ThorchainSaversDepositActionType } from '../DepositCommon' import { DepositContext } from '../DepositContext' @@ -96,7 +97,6 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { null, ) const [daysToBreakEven, setDaysToBreakEven] = useState(null) - const appDispatch = useAppDispatch() const translate = useTranslate() const mixpanel = getMixPanel() // TODO: Allow user to set fee priority @@ -540,6 +540,13 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { })() }, [accountId, accountMetadata, accountType, assetId, bip44Params, chainAdapter, wallet]) + const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery( + reactQueries.common.isTradingActive({ + assetId, + swapperName: SwapperName.Thorchain, + }), + ) + const handleDeposit = useCallback(async () => { if (!contextDispatch || !bip44Params || !accountId || !assetId) return try { @@ -561,15 +568,15 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { contextDispatch({ type: ThorchainSaversDepositActionType.SET_LOADING, payload: true }) - const { getIsTradingActive } = swapperApi.endpoints - const { data: isTradingActive } = await appDispatch( - getIsTradingActive.initiate({ - assetId, - swapperName: SwapperName.Thorchain, - }), - ) + // Was the pool active when it was fetched at the time of the component mount + if (isTradingActive === false) { + throw new Error(`THORChain pool halted for assetId: ${assetId}`) + } + + // Refetch the trading active state JIT to ensure the pool didn't just become halted + const { data: isTradingActiveData } = await refetchIsTradingActive() - if (!isTradingActive) { + if (!isTradingActiveData) { throw new Error(`THORChain pool halted for assetId: ${assetId}`) } @@ -633,7 +640,8 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { chainAdapter, state?.deposit.cryptoAmount, state?.deposit.fiatAmount, - appDispatch, + isTradingActive, + refetchIsTradingActive, protocolFeeCryptoBaseUnit, maybeFromUTXOAccountAddress, onNext, diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx index ca2ca5afb38..cac00a066ab 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx @@ -17,6 +17,7 @@ import { toAssetId } from '@shapeshiftoss/caip' import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' +import { useQuery } from '@tanstack/react-query' import BigNumber from 'bignumber.js' import type { DefiButtonProps } from 'features/defi/components/DefiActionButtons' import { Overview } from 'features/defi/components/Overview/Overview' @@ -28,6 +29,7 @@ import { DefiAction } from 'features/defi/contexts/DefiManagerProvider/DefiCommo import { useCallback, useEffect, useMemo, useState } from 'react' import { FaTwitter } from 'react-icons/fa' import { useTranslate } from 'react-polyglot' +import { reactQueries } from 'react-queries' import type { AccountDropdownProps } from 'components/AccountDropdown/AccountDropdown' import { Amount } from 'components/Amount/Amount' import { CircularProgress } from 'components/CircularProgress/CircularProgress' @@ -36,7 +38,6 @@ import { Text } from 'components/Text' import { useBrowserRouter } from 'hooks/useBrowserRouter/useBrowserRouter' import { bnOrZero } from 'lib/bignumber/bignumber' import { fromBaseUnit, toBaseUnit } from 'lib/math' -import { useGetIsTradingActiveQuery } from 'state/apis/swapper/swapperApi' import type { ThorchainSaversStakingSpecificMetadata } from 'state/slices/opportunitiesSlice/resolvers/thorchainsavers/types' import { getMaybeThorchainSaversDepositQuote, @@ -129,11 +130,12 @@ export const ThorchainSaversOverview: React.FC = ({ [accountId, defaultAccountId, highestBalanceAccountId], ) - const { data: isTradingActive, isFetching: isTradingActiveQueryPending } = - useGetIsTradingActiveQuery({ + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( + reactQueries.common.isTradingActive({ assetId, swapperName: SwapperName.Thorchain, - }) + }), + ) useEffect(() => { if (!maybeAccountId) return @@ -272,7 +274,7 @@ export const ThorchainSaversOverview: React.FC = ({ isFull: opportunityMetadata?.isFull || isHardCapReached, hasPendingTxs, hasPendingQueries, - isHalted: !isTradingActive, + isHalted: isTradingActive === false, }) }, [ earnOpportunityData, @@ -355,7 +357,7 @@ export const ThorchainSaversOverview: React.FC = ({ const handleThorchainSaversEmptyClick = useCallback(() => setHideEmptyState(true), []) - if (!earnOpportunityData || isTradingActiveQueryPending) { + if (!earnOpportunityData || isTradingActiveLoading) { return (
diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx index 524ec484c59..c0af1afcd7c 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx @@ -16,6 +16,7 @@ import { FeeDataKey } from '@shapeshiftoss/chain-adapters' import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/types' import { supportsETH } from '@shapeshiftoss/hdwallet-core' import { SwapperName } from '@shapeshiftoss/swapper' +import { useQuery } from '@tanstack/react-query' import { getConfig } from 'config' import { getOrCreateContractByType } from 'contracts/contractManager' import { ContractType } from 'contracts/types' @@ -29,6 +30,7 @@ import type { import { DefiStep } from 'features/defi/contexts/DefiManagerProvider/DefiCommon' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { useTranslate } from 'react-polyglot' +import { reactQueries } from 'react-queries' import { encodeFunctionData, getAddress } from 'viem' import { Amount } from 'components/Amount/Amount' import { AssetIcon } from 'components/AssetIcon' @@ -58,7 +60,6 @@ import { import { fromThorBaseUnit, toThorBaseUnit } from 'lib/utils/thorchain' import { BASE_BPS_POINTS } from 'lib/utils/thorchain/constants' import { getInboundAddressDataForChain } from 'lib/utils/thorchain/getInboundAddressDataForChain' -import { swapperApi } from 'state/apis/swapper/swapperApi' import { getThorchainSaversPosition, getThorchainSaversWithdrawQuote, @@ -79,7 +80,7 @@ import { selectPortfolioCryptoBalanceBaseUnitByFilter, selectSelectedCurrency, } from 'state/slices/selectors' -import { useAppDispatch, useAppSelector } from 'state/store' +import { useAppSelector } from 'state/store' import { ThorchainSaversWithdrawActionType } from '../WithdrawCommon' import { WithdrawContext } from '../WithdrawContext' @@ -98,7 +99,6 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { null, ) const { state, dispatch: contextDispatch } = useContext(WithdrawContext) - const appDispatch = useAppDispatch() const translate = useTranslate() const mixpanel = getMixPanel() const { query } = useBrowserRouter() @@ -594,6 +594,13 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { return txId }, [getWithdrawInput, wallet]) + const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery( + reactQueries.common.isTradingActive({ + assetId, + swapperName: SwapperName.Thorchain, + }), + ) + const handleConfirm = useCallback(async () => { if (!contextDispatch || !bip44Params || !accountId || !assetId || !opportunityData) return try { @@ -625,15 +632,15 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { return } - const { getIsTradingActive } = swapperApi.endpoints - const { data: isTradingActive } = await appDispatch( - getIsTradingActive.initiate({ - assetId, - swapperName: SwapperName.Thorchain, - }), - ) + // Was the pool active when it was fetched at the time of the component mount + if (isTradingActive === false) { + throw new Error(`THORChain pool halted for assetId: ${assetId}`) + } + + // Refetch the trading active state JIT to ensure the pool didn't just become halted + const { data: isTradingActiveData } = await refetchIsTradingActive() - if (!isTradingActive) { + if (!isTradingActiveData) { throw new Error(`THORChain pool halted for assetId: ${assetId}`) } @@ -701,8 +708,8 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { state?.withdraw.cryptoAmount, state?.withdraw.fiatAmount, expiry, - appDispatch, - getWithdrawInput, + isTradingActive, + refetchIsTradingActive, dustAmountCryptoBaseUnit, protocolFeeCryptoBaseUnit, onNext, @@ -710,6 +717,7 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { toast, translate, isTokenWithdraw, + getWithdrawInput, handleMultiTxSend, handleCustomTx, ]) diff --git a/src/state/apis/swapper/swapperApi.ts b/src/state/apis/swapper/swapperApi.ts index 426db3c8897..a4f292cfc15 100644 --- a/src/state/apis/swapper/swapperApi.ts +++ b/src/state/apis/swapper/swapperApi.ts @@ -244,5 +244,4 @@ export const swapperApi = swapperApiBase.injectEndpoints({ }), }) -export const { useGetTradeQuoteQuery, useGetSupportedAssetsQuery, useGetIsTradingActiveQuery } = - swapperApi +export const { useGetTradeQuoteQuery, useGetSupportedAssetsQuery } = swapperApi From 0e0e9e551ba19cb23a2b664326cfd4210603b162 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 8 Feb 2024 23:18:30 +0100 Subject: [PATCH 04/33] feat: handle @lukemorales/query-key-factory shenanigans --- .../Deposit/components/Confirm.tsx | 15 ++++++++++++--- .../Overview/ThorchainSaversOverview.tsx | 15 ++++++++++++--- .../Withdraw/components/Confirm.tsx | 15 ++++++++++++--- src/pages/ThorChainLP/AvailablePools.tsx | 14 +++++++++++--- src/pages/ThorChainLP/Pool/Pool.tsx | 15 ++++++++++++--- .../components/AddLiquitity/AddLiquidityInput.tsx | 15 ++++++++++++--- .../ReusableLpStatus/TransactionRow.tsx | 15 ++++++++++++--- src/react-queries/index.ts | 8 -------- 8 files changed, 83 insertions(+), 29 deletions(-) diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx index 44973c4729b..ed2203d3027 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx @@ -540,12 +540,21 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { })() }, [accountId, accountMetadata, accountType, assetId, bip44Params, chainAdapter, wallet]) - const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(assetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) const handleDeposit = useCallback(async () => { if (!contextDispatch || !bip44Params || !accountId || !assetId) return diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx index cac00a066ab..cf1698ca25d 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx @@ -130,12 +130,21 @@ export const ThorchainSaversOverview: React.FC = ({ [accountId, defaultAccountId, highestBalanceAccountId], ) - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(assetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) useEffect(() => { if (!maybeAccountId) return diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx index c0af1afcd7c..7ac32132920 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx @@ -594,12 +594,21 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { return txId }, [getWithdrawInput, wallet]) - const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, refetch: refetchIsTradingActive } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(assetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) const handleConfirm = useCallback(async () => { if (!contextDispatch || !bip44Params || !accountId || !assetId || !opportunityData) return diff --git a/src/pages/ThorChainLP/AvailablePools.tsx b/src/pages/ThorChainLP/AvailablePools.tsx index 2b64a4ed9c8..f6a379abe97 100644 --- a/src/pages/ThorChainLP/AvailablePools.tsx +++ b/src/pages/ThorChainLP/AvailablePools.tsx @@ -51,12 +51,20 @@ type PoolButtonProps = { const PoolButton = ({ pool }: PoolButtonProps) => { const history = useHistory() - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId: pool.assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) const handlePoolClick = useCallback(() => { const { opportunityId } = pool diff --git a/src/pages/ThorChainLP/Pool/Pool.tsx b/src/pages/ThorChainLP/Pool/Pool.tsx index 30aaf20c597..7850ce6d594 100644 --- a/src/pages/ThorChainLP/Pool/Pool.tsx +++ b/src/pages/ThorChainLP/Pool/Pool.tsx @@ -102,12 +102,21 @@ export const Pool = () => { return parsedPools.find(pool => pool.opportunityId === routeOpportunityId) }, [params, parsedPools]) - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId: foundPool?.assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(foundPool?.assetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) const poolAssetIds = useMemo(() => { if (!foundPool) return [] diff --git a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx index 721fe3b3b1a..525d7191edb 100644 --- a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx @@ -184,12 +184,21 @@ export const AddLiquidityInput: React.FC = ({ const [poolAsset, setPoolAsset] = useState(foundPoolAsset) - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery( - reactQueries.common.isTradingActive({ + const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId: poolAsset?.assetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(poolAsset?.assetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) useEffect(() => { if (!(poolAsset && parsedPools)) return diff --git a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx index e3ddf2cfea6..2091aa1f4a7 100644 --- a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx @@ -105,12 +105,21 @@ export const TransactionRow: React.FC = ({ isLoading: isTradingActiveLoading, isRefetching: isTradingActiveRefetching, refetch: refetchIsTradingActive, - } = useQuery( - reactQueries.common.isTradingActive({ + } = useQuery({ + ...reactQueries.common.isTradingActive({ assetId: poolAssetId, swapperName: SwapperName.Thorchain, }), - ) + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + enabled: Boolean(poolAssetId), + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) const runeAccountId = accountIdsByChainId[thorchainChainId] const poolAssetAccountId = diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index dec38da7542..27acd90d49c 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -82,14 +82,6 @@ const common = createQueryKeys('common', { if (maybeIsTradingActive.isErr()) throw maybeIsTradingActive.unwrapErr() return maybeIsTradingActive.unwrap() }, - enabled: Boolean(assetId), - // Go stale instantly - staleTime: 0, - // Never store queries in cache since we always want fresh data - gcTime: 0, - refetchOnWindowFocus: true, - refetchOnMount: true, - refetchInterval: 60_000, }), }) From 10997b2de743dac2285659caa542b77178387d34 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 8 Feb 2024 23:55:37 +0100 Subject: [PATCH 05/33] feat: remove swapperApi's getIsTradingActive --- .../hooks/useIsTradingActive.tsx | 80 ++++++++----------- src/react-queries/index.ts | 3 +- src/state/apis/swapper/swapperApi.ts | 44 +++------- 3 files changed, 45 insertions(+), 82 deletions(-) diff --git a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx index 123e08ca80d..ebd76c0f5d3 100644 --- a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx +++ b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx @@ -1,71 +1,57 @@ -import { useEffect, useState } from 'react' +import { useQuery } from '@tanstack/react-query' +import { reactQueries } from 'react-queries' import type { ThorEvmTradeQuote } from 'lib/swapper/swappers/ThorchainSwapper/getThorTradeQuote/getTradeQuote' import { TradeType } from 'lib/swapper/swappers/ThorchainSwapper/utils/longTailHelpers' -import { swapperApi } from 'state/apis/swapper/swapperApi' import { selectInputBuyAsset, selectInputSellAsset } from 'state/slices/tradeInputSlice/selectors' import { selectActiveQuote, selectActiveSwapperName } from 'state/slices/tradeQuoteSlice/selectors' -import { useAppDispatch, useAppSelector } from 'state/store' +import { useAppSelector } from 'state/store' export const useIsTradingActive = () => { const activeQuote = useAppSelector(selectActiveQuote) const tradeType = (activeQuote as ThorEvmTradeQuote)?.tradeType - const [isTradingActiveOnSellPool, setIsTradingActiveOnSellPool] = useState(false) - const [isTradingActiveOnBuyPool, setIsTradingActiveOnBuyPool] = useState(false) - - const dispatch = useAppDispatch() - const buyAssetId = useAppSelector(selectInputBuyAsset).assetId const sellAssetId = useAppSelector(selectInputSellAsset).assetId - const { getIsTradingActive } = swapperApi.endpoints const swapperName = useAppSelector(selectActiveSwapperName) - useEffect(() => { - ;(async () => { - const isTradingActiveOnSellPoolResult = - sellAssetId && - swapperName && - ( - await dispatch( - getIsTradingActive.initiate({ - assetId: sellAssetId, - swapperName, - }), - ) - ).data - - setIsTradingActiveOnSellPool(!!isTradingActiveOnSellPoolResult) - })() - }, [dispatch, getIsTradingActive, sellAssetId, swapperName]) - - useEffect(() => { - ;(async () => { - const isTradingActiveOnBuyPoolResult = - buyAssetId && - swapperName && - ( - await dispatch( - getIsTradingActive.initiate({ - assetId: buyAssetId, - swapperName, - }), - ) - ).data - - setIsTradingActiveOnBuyPool(!!isTradingActiveOnBuyPoolResult) - })() - }, [buyAssetId, dispatch, getIsTradingActive, swapperName]) + const { data: isTradingActiveOnSellPool } = useQuery({ + ...reactQueries.common.isTradingActive({ + assetId: sellAssetId, + swapperName, + }), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) + + const { data: isTradingActiveOnBuyPool } = useQuery({ + ...reactQueries.common.isTradingActive({ + assetId: buyAssetId, + swapperName, + }), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Go stale instantly + // Never store queries in cache since we always want fresh data + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + }) return { isTradingActiveOnSellPool: tradeType === TradeType.L1ToL1 || tradeType === TradeType.L1ToLongTail - ? isTradingActiveOnSellPool + ? Boolean(isTradingActiveOnSellPool) : true, isTradingActiveOnBuyPool: tradeType === TradeType.L1ToL1 || tradeType === TradeType.LongTailToL1 - ? isTradingActiveOnBuyPool + ? Boolean(isTradingActiveOnBuyPool) : true, - isTradingActive: isTradingActiveOnSellPool && isTradingActiveOnBuyPool, + isTradingActive: Boolean(isTradingActiveOnSellPool && isTradingActiveOnBuyPool), } } diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 27acd90d49c..2ce0a6eab33 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -70,11 +70,12 @@ const common = createQueryKeys('common', { swapperName, }: { assetId: AssetId | undefined - swapperName: SwapperName + swapperName: SwapperName | undefined }) => ({ queryKey: ['isTradingActive', assetId, swapperName], queryFn: async () => { if (!assetId) throw new Error('assetId is required') + if (!swapperName) throw new Error('swapperName is required') const maybeIsTradingActive = await isTradingActive(assetId, swapperName) diff --git a/src/state/apis/swapper/swapperApi.ts b/src/state/apis/swapper/swapperApi.ts index a4f292cfc15..9d490f5c549 100644 --- a/src/state/apis/swapper/swapperApi.ts +++ b/src/state/apis/swapper/swapperApi.ts @@ -1,7 +1,8 @@ import { createApi } from '@reduxjs/toolkit/dist/query/react' import type { ChainId } from '@shapeshiftoss/caip' import { type AssetId, fromAssetId } from '@shapeshiftoss/caip' -import type { SwapperName } from '@shapeshiftoss/swapper' +import { reactQueries } from 'react-queries' +import { queryClient } from 'context/QueryClientProvider/queryClient' import { getSupportedBuyAssetIds, getSupportedSellAssetIds, @@ -21,36 +22,15 @@ import { selectFeatureFlags } from 'state/slices/preferencesSlice/selectors' import { selectInputSellAsset } from 'state/slices/tradeInputSlice/selectors' import { BASE_RTK_CREATE_API_CONFIG } from '../const' -import { apiErrorHandler } from '../utils' -import { isTradingActive } from './helpers' import { validateTradeQuote } from './helpers/validateTradeQuote' -const getIsTradingActiveErrorHandler = apiErrorHandler( - 'getIsTradingActiveApi: error getting trading status', -) - export const GET_TRADE_QUOTE_POLLING_INTERVAL = 20_000 export const swapperApiBase = createApi({ ...BASE_RTK_CREATE_API_CONFIG, reducerPath: 'swapperApi', keepUnusedDataFor: Number.MAX_SAFE_INTEGER, // never clear, we will manage this tagTypes: ['TradeQuote'], - endpoints: build => ({ - getIsTradingActive: build.query< - boolean, - { assetId: AssetId | undefined; swapperName: SwapperName } - >({ - queryFn: async ({ assetId, swapperName }) => { - const maybeIsTradingActive = await isTradingActive(assetId, swapperName) - if (maybeIsTradingActive.isErr()) { - return getIsTradingActiveErrorHandler(maybeIsTradingActive.unwrapErr()) - } - return { - data: maybeIsTradingActive.unwrap(), - } - }, - }), - }), + endpoints: () => ({}), }) export const swapperApi = swapperApiBase.injectEndpoints({ @@ -128,17 +108,13 @@ export const swapperApi = swapperApiBase.injectEndpoints({ return { isTradingActiveOnSellPool: false, isTradingActiveOnBuyPool: false } } - const [{ data: isTradingActiveOnSellPool }, { data: isTradingActiveOnBuyPool }] = - await Promise.all( - [sellAsset.assetId, buyAsset.assetId].map(assetId => { - return dispatch( - swapperApiBase.endpoints.getIsTradingActive.initiate({ - assetId, - swapperName, - }), - ) - }), - ) + const [isTradingActiveOnSellPool, isTradingActiveOnBuyPool] = await Promise.all( + [sellAsset.assetId, buyAsset.assetId].map(assetId => { + return queryClient.fetchQuery( + reactQueries.common.isTradingActive({ assetId, swapperName }), + ) + }), + ) return { isTradingActiveOnSellPool: tradeType === TradeType.LongTailToL1 || isTradingActiveOnSellPool, From 2fad5f81adf31406ece47cc16a4601c1b5e39e95 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:02:10 +0100 Subject: [PATCH 06/33] feat: safety --- src/components/MultiHopTrade/hooks/useIsTradingActive.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx index ebd76c0f5d3..5aee74fd996 100644 --- a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx +++ b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx @@ -20,6 +20,7 @@ export const useIsTradingActive = () => { assetId: sellAssetId, swapperName, }), + enabled: !!swapperName, // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object // Go stale instantly staleTime: 0, @@ -35,6 +36,7 @@ export const useIsTradingActive = () => { assetId: buyAssetId, swapperName, }), + enabled: !!swapperName, // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object // Go stale instantly // Never store queries in cache since we always want fresh data From b1380a8790ad60bb418efcd14136718f3f3fa889 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:15:29 +0100 Subject: [PATCH 07/33] [skip ci] wip: query-key-factory shenanigans --- .../components/AddLiquitity/AddLiquidityInput.tsx | 8 ++++++++ src/react-queries/index.ts | 5 ----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx index 525d7191edb..119d5d1b66c 100644 --- a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx @@ -458,6 +458,14 @@ export const AddLiquidityInput: React.FC = ({ inboundAddressData?.router, poolAssetAccountAddress, ), + enabled: Boolean( + poolAsset?.assetId && + inboundAddressData?.router && + poolAssetAccountAddress && + getSupportedEvmChainIds().includes( + fromAssetId(poolAsset?.assetId).chainId as KnownChainIds, + ), + ), }) const isApprovalRequired = useMemo(() => { diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 2ce0a6eab33..7224f58b2b3 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -59,11 +59,6 @@ const common = createQueryKeys('common', { return allowanceOnChainCryptoBaseUnit }, - enabled: - assetId && - spender && - from && - getSupportedEvmChainIds().includes(fromAssetId(assetId).chainId as KnownChainIds), }), isTradingActive: ({ assetId, From a1019fdb08c193f0da0568f7c340c772aaef5a1e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:46:16 +0100 Subject: [PATCH 08/33] [skip ci] feat: swapData query --- src/pages/ThorChainLP/AvailablePools.tsx | 2 ++ src/pages/ThorChainLP/Pool/Pool.tsx | 9 +++++++++ src/pages/ThorChainLP/Position/Position.tsx | 9 +++++++++ src/react-queries/index.ts | 3 --- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pages/ThorChainLP/AvailablePools.tsx b/src/pages/ThorChainLP/AvailablePools.tsx index f6a379abe97..48e5e643ed8 100644 --- a/src/pages/ThorChainLP/AvailablePools.tsx +++ b/src/pages/ThorChainLP/AvailablePools.tsx @@ -89,6 +89,8 @@ const PoolButton = ({ pool }: PoolButtonProps) => { const { data: volume7D, isLoading: isVolume7DLoading } = useQuery({ ...reactQueries.midgard.swapsData(pool.assetId, '7d'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, select: data => getVolume(runeMarketData.price, data), }) diff --git a/src/pages/ThorChainLP/Pool/Pool.tsx b/src/pages/ThorChainLP/Pool/Pool.tsx index 7850ce6d594..693213f6e60 100644 --- a/src/pages/ThorChainLP/Pool/Pool.tsx +++ b/src/pages/ThorChainLP/Pool/Pool.tsx @@ -144,15 +144,24 @@ export const Pool = () => { const { data: volume24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, '24h'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, select: data => getVolume(runeMarketData.price, data), }) const { data: swapDataPrevious24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, 'previous24h'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, }) const { data: swapData24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, '24h'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, }) const fees24h = useMemo(() => { diff --git a/src/pages/ThorChainLP/Position/Position.tsx b/src/pages/ThorChainLP/Position/Position.tsx index b23680e2c45..4cb058ad5f3 100644 --- a/src/pages/ThorChainLP/Position/Position.tsx +++ b/src/pages/ThorChainLP/Position/Position.tsx @@ -177,10 +177,16 @@ export const Position = () => { const { data: swapDataPrevious24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, 'previous24h'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, }) const { data: swapData24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, '24h'), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, }) const fees24h = useMemo(() => { @@ -192,6 +198,9 @@ export const Position = () => { const { data: volume24h } = useQuery({ ...reactQueries.midgard.swapsData(foundPool?.assetId, '24h'), select: data => getVolume(runeMarketData.price, data), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + staleTime: Infinity, + enabled: !!foundPool?.assetId, }) const swap24hChange = useMemo(() => { diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 7224f58b2b3..98f14e5e3d4 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -148,9 +148,6 @@ const mutations = createMutationKeys('mutations', { // Feature-agnostic, abstracts away midgard endpoints const midgard = createQueryKeys('midgard', { swapsData: (assetId: AssetId | undefined, timeframe: '24h' | 'previous24h' | '7d') => ({ - // We may or may not want to revisit this, but this will prevent overfetching for now - staleTime: Infinity, - enabled: !!assetId, queryKey: ['midgardSwapsData', assetId ?? '', timeframe], queryFn: async () => { if (!assetId) throw new Error('assetId is required') From 7fd16135f0ec1e4631e42a8078ff1305ab1fadcb Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:49:09 +0100 Subject: [PATCH 09/33] [skip ci] feat: midgard.poolData query --- src/pages/ThorChainLP/queries/hooks/useUserLpData.ts | 6 +++++- src/react-queries/index.ts | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts b/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts index 024b6be2ae9..bcb23118570 100644 --- a/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts +++ b/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts @@ -37,6 +37,9 @@ export const useUserLpData = ({ const { data: midgardPoolData } = useQuery({ ...reactQueries.midgard.poolData(assetId), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // 60 seconds staleTime since this is used to get the current position value + staleTime: 60_000, }) const selectLiquidityPositionsData = ( @@ -104,7 +107,8 @@ export const useUserLpData = ({ const liquidityPoolPositionData = useQuery({ ...reactQueries.thorchainLp.userLpData(assetId), - staleTime: Infinity, + // 60 seconds staleTime since this is used to get the current position value + staleTime: 60_000, queryFn: async ({ queryKey }) => { const [, , , { assetId }] = queryKey diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 98f14e5e3d4..b1b0b07e7f8 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -184,9 +184,6 @@ const midgard = createQueryKeys('midgard', { }, }), poolData: (assetId: AssetId | undefined) => ({ - // We may or may not want to revisit this, but this will prevent overfetching for now - staleTime: Infinity, - enabled: !!assetId, queryKey: ['midgardPoolData', assetId], queryFn: async () => { if (!assetId) throw new Error('assetId is required') From 2a0d339b432bc2a065ceac792e18777bbbc412a1 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:51:46 +0100 Subject: [PATCH 10/33] feat: midgard.poolsData and use 5mn staleTime --- src/pages/ThorChainLP/queries/hooks/usePools.ts | 3 +++ src/react-queries/index.ts | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/ThorChainLP/queries/hooks/usePools.ts b/src/pages/ThorChainLP/queries/hooks/usePools.ts index 34d9b219907..9a5066e4ed7 100644 --- a/src/pages/ThorChainLP/queries/hooks/usePools.ts +++ b/src/pages/ThorChainLP/queries/hooks/usePools.ts @@ -79,6 +79,9 @@ export const usePools = (excludeVirtualPools?: boolean) => { ) const pools = useQuery({ ...reactQueries.midgard.poolsData(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // 5 minutes, since this is related to pools data, not user data - we can afford to have this stale for longer + staleTime: 60_000 * 5, // Parses pools with 3 "positions" per pool: // - RUNE asym // - Asset asym diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index b1b0b07e7f8..32eef6a3c1c 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -196,8 +196,6 @@ const midgard = createQueryKeys('midgard', { }, }), poolsData: () => ({ - // We may or may not want to revisit this, but this will prevent overfetching for now - staleTime: Infinity, queryKey: ['midgardPoolsData'], queryFn: async () => { const { data: poolsData } = await axios.get( From 041aca3c3e62cd0cfa6d88369dd3256ba0765036 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:54:04 +0100 Subject: [PATCH 11/33] feat: thornode.poolData --- src/pages/ThorChainLP/queries/hooks/useUserLpData.ts | 10 ++++++++-- src/react-queries/index.ts | 3 --- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts b/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts index bcb23118570..e3acba0d1bf 100644 --- a/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts +++ b/src/pages/ThorChainLP/queries/hooks/useUserLpData.ts @@ -33,13 +33,19 @@ export const useUserLpData = ({ const { data: thornodePoolData } = useQuery({ ...reactQueries.thornode.poolData(assetId), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // 0 seconds garbage collect and stale times since this is used to get the current position value, we want this to always be cached-then-fresh + staleTime: 0, + gcTime: 0, + enabled: !!assetId, }) const { data: midgardPoolData } = useQuery({ ...reactQueries.midgard.poolData(assetId), // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object - // 60 seconds staleTime since this is used to get the current position value - staleTime: 60_000, + // 0 seconds garbage collect and stale times since this is used to get the current position value, we want this to always be cached-then-fresh + staleTime: 0, + gcTime: 0, }) const selectLiquidityPositionsData = ( diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 32eef6a3c1c..51940a5ba90 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -209,9 +209,6 @@ const midgard = createQueryKeys('midgard', { // Feature-agnostic, abstracts away THORNode endpoints const thornode = createQueryKeys('thornode', { poolData: (assetId: AssetId | undefined) => ({ - // We may or may not want to revisit this, but this will prevent overfetching for now - staleTime: Infinity, - enabled: !!assetId, queryKey: ['thornodePoolData', assetId], queryFn: async () => { if (!assetId) throw new Error('assetId is required') From 93334a161950b5147d735586bc00ee1bf2f7ebe9 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:35:45 +0100 Subject: [PATCH 12/33] feat: thornode.poolsData --- src/pages/Lending/hooks/useLendingSupportedAssets/index.ts | 3 +++ src/pages/ThorChainLP/queries/hooks/useAllUserLpData.ts | 3 +++ src/react-queries/index.ts | 3 --- .../opportunitiesSlice/resolvers/thorchainsavers/index.ts | 6 ++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pages/Lending/hooks/useLendingSupportedAssets/index.ts b/src/pages/Lending/hooks/useLendingSupportedAssets/index.ts index 04c706957c4..ba2e91645c5 100644 --- a/src/pages/Lending/hooks/useLendingSupportedAssets/index.ts +++ b/src/pages/Lending/hooks/useLendingSupportedAssets/index.ts @@ -23,6 +23,9 @@ export const useLendingSupportedAssets = ({ type }: { type: 'collateral' | 'borr const { data: availablePools } = useQuery({ ...reactQueries.thornode.poolsData(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Infinity staleTime as we handle halted state JIT + staleTime: Infinity, select: pools => pools.filter(pool => pool.status === 'Available'), }) diff --git a/src/pages/ThorChainLP/queries/hooks/useAllUserLpData.ts b/src/pages/ThorChainLP/queries/hooks/useAllUserLpData.ts index 717fde3c143..a19f4340bd6 100644 --- a/src/pages/ThorChainLP/queries/hooks/useAllUserLpData.ts +++ b/src/pages/ThorChainLP/queries/hooks/useAllUserLpData.ts @@ -35,6 +35,9 @@ export const useAllUserLpData = ({ const { data: availableThornodePools, isSuccess: isAvailableThornodePoolsDataLoaded } = useQuery({ ...reactQueries.thornode.poolsData(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Infinity staleTime as we handle halted state JIT + staleTime: Infinity, select: pools => pools.filter(pool => pool.status === 'Available'), }) diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 51940a5ba90..00c1db5517e 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -222,9 +222,6 @@ const thornode = createQueryKeys('thornode', { }), poolsData: () => ({ queryKey: ['thornodePoolsData'], - // Typically 60 second staleTime to handle pools going to live/halt states - // This may not be required in your specific consumption, override if needed - staleTime: 60_000, queryFn: async () => { const daemonUrl = getConfig().REACT_APP_THORCHAIN_NODE_URL const poolResponse = await thorService.get( diff --git a/src/state/slices/opportunitiesSlice/resolvers/thorchainsavers/index.ts b/src/state/slices/opportunitiesSlice/resolvers/thorchainsavers/index.ts index be4b938339b..31afbebf211 100644 --- a/src/state/slices/opportunitiesSlice/resolvers/thorchainsavers/index.ts +++ b/src/state/slices/opportunitiesSlice/resolvers/thorchainsavers/index.ts @@ -34,6 +34,9 @@ export const thorchainSaversOpportunityIdsResolver = async (): Promise<{ }> => { const thorchainPools = await queryClient.fetchQuery({ ...reactQueries.thornode.poolsData(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Infinity staleTime as we handle halted state JIT + staleTime: Infinity, }) if (!thorchainPools.length) { @@ -92,6 +95,9 @@ export const thorchainSaversStakingOpportunitiesMetadataResolver = async ({ const thorchainPools = await queryClient.fetchQuery({ ...reactQueries.thornode.poolsData(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Infinity staleTime as we handle halted state JIT + staleTime: Infinity, }) if (!thorchainPools.length) { From 9b4f215d005db1194565a3d91f0d8da707d5ab53 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:37:38 +0100 Subject: [PATCH 13/33] feat: thornode.mimir --- src/pages/Lending/hooks/useRepaymentLockData.tsx | 11 +++++++++-- src/react-queries/index.ts | 7 ------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/Lending/hooks/useRepaymentLockData.tsx b/src/pages/Lending/hooks/useRepaymentLockData.tsx index 7906b658585..dc867cd55cd 100644 --- a/src/pages/Lending/hooks/useRepaymentLockData.tsx +++ b/src/pages/Lending/hooks/useRepaymentLockData.tsx @@ -2,8 +2,8 @@ import type { AccountId, AssetId } from '@shapeshiftoss/caip' import type { QueryObserverOptions } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' -import { reactQueries, thorchainBlockTimeSeconds } from 'react-queries' -import { bnOrZero } from 'lib/bignumber/bignumber' +import { reactQueries } from 'react-queries' +import { bn, bnOrZero } from 'lib/bignumber/bignumber' import { thorchainLendingPositionQueryFn } from './useLendingPositionData' @@ -12,6 +12,10 @@ type UseLendingPositionDataProps = { assetId?: AssetId } +// Current blocktime as per https://thorchain.network/stats +export const thorchainBlockTimeSeconds = '6.1' + +const thorchainBlockTimeMs = bn(thorchainBlockTimeSeconds).times(1000).toNumber() export const useRepaymentLockData = ({ accountId, assetId, @@ -44,6 +48,9 @@ export const useRepaymentLockData = ({ const repaymentLockData = useQuery({ ...reactQueries.thornode.mimir(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // We use the mimir query to get the repayment maturity block, so need to mark it stale at the end of each THOR block + staleTime: thorchainBlockTimeMs, select: mimirData => { if (!mimirData || !blockHeight) return null diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 00c1db5517e..0a0b4731805 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -7,7 +7,6 @@ import type { SwapperName } from '@shapeshiftoss/swapper' import type { KnownChainIds } from '@shapeshiftoss/types' import axios from 'axios' import { getConfig } from 'config' -import { bn } from 'lib/bignumber/bignumber' import type { MidgardPoolResponse, ThornodePoolResponse, @@ -29,10 +28,6 @@ import type { MidgardSwapHistoryResponse } from 'lib/utils/thorchain/lp/types' import { thorchainLp } from 'pages/ThorChainLP/queries/queries' import { isTradingActive } from 'state/apis/swapper/helpers' -// Current blocktime as per https://thorchain.network/stats -export const thorchainBlockTimeSeconds = '6.1' -const thorchainBlockTimeMs = bn(thorchainBlockTimeSeconds).times(1000).toNumber() - const common = createQueryKeys('common', { allowanceCryptoBaseUnit: ( assetId: AssetId | undefined, @@ -237,8 +232,6 @@ const thornode = createQueryKeys('thornode', { }), mimir: () => { return { - // We use the mimir query to get the repayment maturity block, so need to mark it stale at the end of each THOR block - staleTime: thorchainBlockTimeMs, queryKey: ['thorchainMimir'], queryFn: async () => { const daemonUrl = getConfig().REACT_APP_THORCHAIN_NODE_URL From 0325f7cbb2881e0f776abeb260ad33e2162fbbe1 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:38:51 +0100 Subject: [PATCH 14/33] feat: thornode.block --- src/pages/Lending/hooks/useRepaymentLockData.tsx | 3 +++ src/react-queries/index.ts | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/Lending/hooks/useRepaymentLockData.tsx b/src/pages/Lending/hooks/useRepaymentLockData.tsx index dc867cd55cd..d1cf0368cf6 100644 --- a/src/pages/Lending/hooks/useRepaymentLockData.tsx +++ b/src/pages/Lending/hooks/useRepaymentLockData.tsx @@ -24,6 +24,9 @@ export const useRepaymentLockData = ({ enabled = true, }: UseLendingPositionDataProps & QueryObserverOptions) => { const { data: blockHeight } = useQuery({ + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // We use the block query to get the current height, so we obviously need to mark it stale at the end of each THOR block + staleTime: thorchainBlockTimeMs, ...reactQueries.thornode.block(), select: block => block.header.height, enabled, diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 0a0b4731805..0b9cb741061 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -245,8 +245,6 @@ const thornode = createQueryKeys('thornode', { }, block: () => { return { - // Mark blockHeight query as stale at the end of each THOR block - staleTime: thorchainBlockTimeMs, queryKey: ['thorchainBlockHeight'], queryFn: async () => { const daemonUrl = getConfig().REACT_APP_THORCHAIN_NODE_URL From 60a2ee00045bb20ef80a45cac322c603211d0f0e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:52:25 +0100 Subject: [PATCH 15/33] feat: thornode.inboundAddress --- .../components/ReusableLpStatus/TransactionRow.tsx | 5 +++++ src/react-queries/index.ts | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx index 2091aa1f4a7..26a218bc8ba 100644 --- a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx @@ -260,6 +260,11 @@ export const TransactionRow: React.FC = ({ const { data: inboundAddressData, isLoading: isInboundAddressLoading } = useQuery({ ...reactQueries.thornode.inboundAddress(assetId), enabled: !!assetId, + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // We technically don't care about going stale immediately here - halted checks are done JIT at signing time in case the pool went + // halted by the time the user clicked the confirm button + // But we still have some sane 60s stale time rather than 0 for paranoia's sake, as a balance of safety and not overfetching + staleTime: 60_000, select: data => data?.unwrap(), }) diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index 0b9cb741061..74c027f8976 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -257,7 +257,6 @@ const thornode = createQueryKeys('thornode', { }, inboundAddress: (assetId: AssetId | undefined) => { return { - staleTime: 60_000, // 60 seconds to handle pools going to/from live/halt states queryKey: ['thorchainInboundAddress', assetId], queryFn: async () => { if (!assetId) throw new Error('assetId is required') From 2439281e1da045ee985b8dcc6ce9bf4dcb546243 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:01:08 +0100 Subject: [PATCH 16/33] feat: thorchainLp.earnings --- src/pages/ThorChainLP/YourPositions.tsx | 4 ++++ src/pages/ThorChainLP/queries/queries.ts | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/ThorChainLP/YourPositions.tsx b/src/pages/ThorChainLP/YourPositions.tsx index 25e1f76edc3..a4d8f6d5491 100644 --- a/src/pages/ThorChainLP/YourPositions.tsx +++ b/src/pages/ThorChainLP/YourPositions.tsx @@ -92,6 +92,10 @@ const PositionButton = ({ const { data: earnings, isLoading: isEarningsLoading } = useQuery({ ...reactQueries.thorchainLp.earnings(userPoolData.dateFirstAdded), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // No staleTime, meaning cache-then-fresh (or fresh if now > garbage collection time) + // That ensures new active listeners always get fresh earnings data + staleTime: 0, select: data => { if (!data) return null const poolAssetId = assetIdToPoolAssetId({ assetId }) diff --git a/src/pages/ThorChainLP/queries/queries.ts b/src/pages/ThorChainLP/queries/queries.ts index 685780081a8..da6c7cd11ad 100644 --- a/src/pages/ThorChainLP/queries/queries.ts +++ b/src/pages/ThorChainLP/queries/queries.ts @@ -54,9 +54,6 @@ export const liquidityMembers = () => ({ export const thorchainLp = createQueryKeys('thorchainLp', { earnings: (from: string | undefined) => ({ - enabled: Boolean(from), - // We may or may not want to revisit this, but this will prevent overfetching for now - staleTime: Infinity, queryKey: ['thorchainearnings', from], queryFn: () => { if (!from) throw new Error('from is required') From cbbfbbb6e20a962f707132d7e1a824a0a9f3d34d Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:01:40 +0100 Subject: [PATCH 17/33] feat: thorchainLp.tvl24hChange --- src/pages/ThorChainLP/Position/Position.tsx | 1 + src/pages/ThorChainLP/queries/queries.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ThorChainLP/Position/Position.tsx b/src/pages/ThorChainLP/Position/Position.tsx index 4cb058ad5f3..a14424a03ee 100644 --- a/src/pages/ThorChainLP/Position/Position.tsx +++ b/src/pages/ThorChainLP/Position/Position.tsx @@ -216,6 +216,7 @@ export const Position = () => { const { data: tvl24hChange } = useQuery({ ...reactQueries.thorchainLp.tvl24hChange(foundPool?.assetId), + enabled: !!foundPool?.assetId, }) const { data: allTimeVolume } = useQuery({ diff --git a/src/pages/ThorChainLP/queries/queries.ts b/src/pages/ThorChainLP/queries/queries.ts index da6c7cd11ad..20e5441506d 100644 --- a/src/pages/ThorChainLP/queries/queries.ts +++ b/src/pages/ThorChainLP/queries/queries.ts @@ -66,7 +66,6 @@ export const thorchainLp = createQueryKeys('thorchainLp', { if (!assetId) throw new Error('assetId is required') return get24hTvlChangePercentage(assetId) }, - enabled: !!assetId, }), allTimeVolume: (assetId: AssetId | undefined, runePrice: string) => ({ queryKey: ['thorchainAllTimeVolume', assetId], From dbf0818346821ded339330c6931a33ba555ca3ea Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:02:35 +0100 Subject: [PATCH 18/33] feat: thorchainLp.allTimeVolume --- src/pages/ThorChainLP/Position/Position.tsx | 2 ++ src/pages/ThorChainLP/queries/queries.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/ThorChainLP/Position/Position.tsx b/src/pages/ThorChainLP/Position/Position.tsx index a14424a03ee..84211013c0d 100644 --- a/src/pages/ThorChainLP/Position/Position.tsx +++ b/src/pages/ThorChainLP/Position/Position.tsx @@ -29,6 +29,7 @@ import { AssetIcon } from 'components/AssetIcon' import { DynamicComponent } from 'components/DynamicComponent' import { Main } from 'components/Layout/Main' import { RawText, Text } from 'components/Text' +import { bnOrZero } from 'lib/bignumber/bignumber' import { assetIdToPoolAssetId } from 'lib/swapper/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers' import { calculateEarnings, @@ -221,6 +222,7 @@ export const Position = () => { const { data: allTimeVolume } = useQuery({ ...reactQueries.thorchainLp.allTimeVolume(foundPool?.assetId, runeMarketData.price), + enabled: Boolean(!!foundPool?.assetId && !!bnOrZero(runeMarketData.price).gt(0)), }) const { data: thornodePoolData } = useQuery({ diff --git a/src/pages/ThorChainLP/queries/queries.ts b/src/pages/ThorChainLP/queries/queries.ts index 20e5441506d..0f9afab3ec6 100644 --- a/src/pages/ThorChainLP/queries/queries.ts +++ b/src/pages/ThorChainLP/queries/queries.ts @@ -73,7 +73,6 @@ export const thorchainLp = createQueryKeys('thorchainLp', { if (!assetId) throw new Error('assetId is required') return getAllTimeVolume(assetId, runePrice) }, - enabled: !!assetId && !!runePrice, }), liquidityMembers, liquidityMember, From 92b73b1f3e7312e94df32d2faa139f0b116eec9e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:06:14 +0100 Subject: [PATCH 19/33] feat: thorchainLp.liquidityProviderPosition --- src/pages/ThorChainLP/queries/queries.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/ThorChainLP/queries/queries.ts b/src/pages/ThorChainLP/queries/queries.ts index 0f9afab3ec6..ccd69f1e74c 100644 --- a/src/pages/ThorChainLP/queries/queries.ts +++ b/src/pages/ThorChainLP/queries/queries.ts @@ -87,10 +87,6 @@ export const thorchainLp = createQueryKeys('thorchainLp', { assetId: AssetId }) => { return { - // Since this isn't a query per se but rather a fetching util deriving from multiple queries, we want data to be considered stale immediately - // Note however that the two underlying liquidityMember and liquidityMembers queries in this query *have* an Infinity staleTime themselves - staleTime: 0, - enabled: !!accountId && !!assetId, queryKey: ['thorchainLiquidityProviderPosition', { accountId, assetId }], queryFn: async () => { const accountPosition = await (async () => { @@ -139,9 +135,13 @@ export const getThorchainLpPosition = async ({ }) => { if (!opportunityId) throw new Error('opportunityId is required') - const lpPositions = await queryClient.fetchQuery( - thorchainLp.liquidityProviderPosition({ accountId, assetId: poolAssetId }), - ) + const lpPositions = await queryClient.fetchQuery({ + ...thorchainLp.liquidityProviderPosition({ accountId, assetId: poolAssetId }), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Since this isn't a query per se but rather a fetching util deriving from multiple queries, we want data to be considered stale immediately + // Note however that the two underlying liquidityMember and liquidityMembers queries in this query *have* an Infinity staleTime themselves + staleTime: 0, + }) if (!lpPositions) return null From 6c2be01af9344a75d40d767f35a60d69a2486f62 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:08:18 +0100 Subject: [PATCH 20/33] feat: commentary --- src/pages/ThorChainLP/queries/queries.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/ThorChainLP/queries/queries.ts b/src/pages/ThorChainLP/queries/queries.ts index ccd69f1e74c..878dccb9a8d 100644 --- a/src/pages/ThorChainLP/queries/queries.ts +++ b/src/pages/ThorChainLP/queries/queries.ts @@ -16,6 +16,8 @@ import { } from 'lib/utils/thorchain/lp/types' import { isUtxoChainId } from 'state/slices/portfolioSlice/utils' +// Note: since this isn't consumes as part of reactQueries queries, but directly as a regular function call within this file, +// the additional property on top of queryKey and queryFn (i.e staleTime) *is* working const liquidityMember = (address: string) => ({ queryKey: ['thorchainLiquidityMember', { address }] as [string, { address: string }], // Don't forget to invalidate me alongside thorchainUserLpData if you want to refresh the data @@ -39,6 +41,8 @@ const liquidityMember = (address: string) => ({ }, }) +// Note: since this isn't consumes as part of reactQueries queries, but directly as a regular function call within this file, +// the additional property on top of queryKey and queryFn (i.e staleTime) *is* working export const liquidityMembers = () => ({ queryKey: ['thorchainLiquidityMembers'] as [string], // Don't forget to invalidate me alongside thorchainUserLpData if you want to refresh the data From ad8137d9b39de03371072b8b28a7a07c05b60285 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:06:41 +0100 Subject: [PATCH 21/33] wip: closer to network queries --- .../hooks/useIsTradingActive.tsx | 94 ++++++++++++++----- src/lib/utils/thorchain/constants.ts | 6 ++ .../Lending/hooks/useRepaymentLockData.tsx | 7 +- src/pages/ThorChainLP/AvailablePools.tsx | 29 ++++-- src/pages/ThorChainLP/Pool/Pool.tsx | 35 +++++-- .../AddLiquitity/AddLiquidityInput.tsx | 73 ++++++++------ .../ReusableLpStatus/TransactionRow.tsx | 46 +++++---- src/react-queries/index.ts | 44 ++++----- src/react-queries/selectors/index.ts | 57 +++++++++++ src/state/apis/swapper/helpers.test.ts | 22 ++++- src/state/apis/swapper/helpers.ts | 49 ---------- 11 files changed, 287 insertions(+), 175 deletions(-) create mode 100644 src/react-queries/selectors/index.ts delete mode 100644 src/state/apis/swapper/helpers.ts diff --git a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx index 5aee74fd996..a23bfb019bd 100644 --- a/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx +++ b/src/components/MultiHopTrade/hooks/useIsTradingActive.tsx @@ -1,7 +1,10 @@ import { useQuery } from '@tanstack/react-query' +import { useMemo } from 'react' import { reactQueries } from 'react-queries' +import { selectInboundAddressData, selectIsTradingActive } from 'react-queries/selectors' import type { ThorEvmTradeQuote } from 'lib/swapper/swappers/ThorchainSwapper/getThorTradeQuote/getTradeQuote' import { TradeType } from 'lib/swapper/swappers/ThorchainSwapper/utils/longTailHelpers' +import { thorchainBlockTimeMs } from 'lib/utils/thorchain/constants' import { selectInputBuyAsset, selectInputSellAsset } from 'state/slices/tradeInputSlice/selectors' import { selectActiveQuote, selectActiveSwapperName } from 'state/slices/tradeQuoteSlice/selectors' import { useAppSelector } from 'state/store' @@ -15,35 +18,78 @@ export const useIsTradingActive = () => { const swapperName = useAppSelector(selectActiveSwapperName) - const { data: isTradingActiveOnSellPool } = useQuery({ - ...reactQueries.common.isTradingActive({ - assetId: sellAssetId, - swapperName, - }), + const { data: sellAssetInboundAddressData, isLoading: isSellAssetInboundAddressLoading } = + useQuery({ + ...reactQueries.thornode.inboundAddresses(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + select: data => selectInboundAddressData(data, sellAssetId), + enabled: Boolean(sellAssetId), + }) + + const { data: buyAssetInboundAddressData, isLoading: isBuyAssetInboundAddressLoading } = useQuery( + { + ...reactQueries.thornode.inboundAddresses(), + // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + // Go stale instantly + staleTime: 0, + // Never store queries in cache since we always want fresh data + gcTime: 0, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchInterval: 60_000, + select: data => selectInboundAddressData(data, buyAssetId), + enabled: Boolean(buyAssetId), + }, + ) + + const { data: mimir, isLoading: isMimirLoading } = useQuery({ + ...reactQueries.thornode.mimir(), + staleTime: thorchainBlockTimeMs, enabled: !!swapperName, - // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object - // Go stale instantly - staleTime: 0, - // Never store queries in cache since we always want fresh data - gcTime: 0, - refetchOnWindowFocus: true, - refetchOnMount: true, - refetchInterval: 60_000, }) - const { data: isTradingActiveOnBuyPool } = useQuery({ - ...reactQueries.common.isTradingActive({ + const isTradingActiveOnSellPool = useMemo(() => { + if (isSellAssetInboundAddressLoading || isMimirLoading || !mimir || !swapperName) return + + return selectIsTradingActive({ + assetId: sellAssetId, + inboundAddressResponse: sellAssetInboundAddressData, + swapperName, + mimir, + }) + }, [ + isSellAssetInboundAddressLoading, + isMimirLoading, + sellAssetId, + sellAssetInboundAddressData, + swapperName, + mimir, + ]) + + const isTradingActiveOnBuyPool = useMemo(() => { + if (isBuyAssetInboundAddressLoading || isMimirLoading || !mimir || !swapperName) return + + return selectIsTradingActive({ assetId: buyAssetId, + inboundAddressResponse: buyAssetInboundAddressData, swapperName, - }), - enabled: !!swapperName, - // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object - // Go stale instantly - // Never store queries in cache since we always want fresh data - refetchOnWindowFocus: true, - refetchOnMount: true, - refetchInterval: 60_000, - }) + mimir, + }) + }, [ + isBuyAssetInboundAddressLoading, + isMimirLoading, + buyAssetId, + buyAssetInboundAddressData, + swapperName, + mimir, + ]) return { isTradingActiveOnSellPool: diff --git a/src/lib/utils/thorchain/constants.ts b/src/lib/utils/thorchain/constants.ts index eef232916e3..9a1d36a77f1 100644 --- a/src/lib/utils/thorchain/constants.ts +++ b/src/lib/utils/thorchain/constants.ts @@ -1,4 +1,10 @@ +import { bn } from 'lib/bignumber/bignumber' + export const THOR_PRECISION = 8 export const BASE_BPS_POINTS = '10000' export const THORCHAIN_AFFILIATE_NAME = 'ss' export const THORCHAIN_POOL_MODULE_ADDRESS = 'thor1g98cy3n9mmjrpn0sxmn63lztelera37n8n67c0' + +// Current blocktime as per https://thorchain.network/stats +export const thorchainBlockTimeSeconds = '6.1' +export const thorchainBlockTimeMs = bn(thorchainBlockTimeSeconds).times(1000).toNumber() diff --git a/src/pages/Lending/hooks/useRepaymentLockData.tsx b/src/pages/Lending/hooks/useRepaymentLockData.tsx index d1cf0368cf6..75fdc7dc2af 100644 --- a/src/pages/Lending/hooks/useRepaymentLockData.tsx +++ b/src/pages/Lending/hooks/useRepaymentLockData.tsx @@ -3,7 +3,8 @@ import type { QueryObserverOptions } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' import { reactQueries } from 'react-queries' -import { bn, bnOrZero } from 'lib/bignumber/bignumber' +import { bnOrZero } from 'lib/bignumber/bignumber' +import { thorchainBlockTimeMs, thorchainBlockTimeSeconds } from 'lib/utils/thorchain/constants' import { thorchainLendingPositionQueryFn } from './useLendingPositionData' @@ -12,10 +13,6 @@ type UseLendingPositionDataProps = { assetId?: AssetId } -// Current blocktime as per https://thorchain.network/stats -export const thorchainBlockTimeSeconds = '6.1' - -const thorchainBlockTimeMs = bn(thorchainBlockTimeSeconds).times(1000).toNumber() export const useRepaymentLockData = ({ accountId, assetId, diff --git a/src/pages/ThorChainLP/AvailablePools.tsx b/src/pages/ThorChainLP/AvailablePools.tsx index 48e5e643ed8..c6b3d02ce76 100644 --- a/src/pages/ThorChainLP/AvailablePools.tsx +++ b/src/pages/ThorChainLP/AvailablePools.tsx @@ -5,10 +5,12 @@ import { SwapperName } from '@shapeshiftoss/swapper' import { useQuery } from '@tanstack/react-query' import { useCallback, useMemo } from 'react' import { reactQueries } from 'react-queries' +import { selectInboundAddressData, selectIsTradingActive } from 'react-queries/selectors' import { generatePath, useHistory } from 'react-router' import { Amount } from 'components/Amount/Amount' import { Main } from 'components/Layout/Main' import { RawText, Text } from 'components/Text' +import { thorchainBlockTimeMs } from 'lib/utils/thorchain/constants' import { calculateTVL, getVolume } from 'lib/utils/thorchain/lp' import { selectMarketDataById } from 'state/slices/marketDataSlice/selectors' import { useAppSelector } from 'state/store' @@ -51,12 +53,8 @@ type PoolButtonProps = { const PoolButton = ({ pool }: PoolButtonProps) => { const history = useHistory() - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ - ...reactQueries.common.isTradingActive({ - assetId: pool.assetId, - swapperName: SwapperName.Thorchain, - }), - // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object + const { data: inboundAddressesData, isLoading: isInboundAddressesDataLoading } = useQuery({ + ...reactQueries.thornode.inboundAddresses(), // Go stale instantly staleTime: 0, // Never store queries in cache since we always want fresh data @@ -64,8 +62,25 @@ const PoolButton = ({ pool }: PoolButtonProps) => { refetchOnWindowFocus: true, refetchOnMount: true, refetchInterval: 60_000, + select: data => selectInboundAddressData(data, pool?.assetId), + }) + + const { data: mimir, isLoading: isMimirLoading } = useQuery({ + ...reactQueries.thornode.mimir(), + staleTime: thorchainBlockTimeMs, }) + const isTradingActive = useMemo(() => { + if (isMimirLoading || !mimir) return + + return selectIsTradingActive({ + assetId: pool?.assetId, + inboundAddressResponse: inboundAddressesData, + swapperName: SwapperName.Thorchain, + mimir, + }) + }, [inboundAddressesData, isMimirLoading, mimir, pool?.assetId]) + const handlePoolClick = useCallback(() => { const { opportunityId } = pool history.push(generatePath('/pools/poolAccount/:opportunityId', { opportunityId })) @@ -116,7 +131,7 @@ const PoolButton = ({ pool }: PoolButtonProps) => { - + {isTradingActive === false ? ( diff --git a/src/pages/ThorChainLP/Pool/Pool.tsx b/src/pages/ThorChainLP/Pool/Pool.tsx index 693213f6e60..ca3eb188e8d 100644 --- a/src/pages/ThorChainLP/Pool/Pool.tsx +++ b/src/pages/ThorChainLP/Pool/Pool.tsx @@ -21,9 +21,11 @@ import React, { useCallback, useMemo } from 'react' import { FaPlus } from 'react-icons/fa6' import { useTranslate } from 'react-polyglot' import { reactQueries } from 'react-queries' +import { selectInboundAddressData, selectIsTradingActive } from 'react-queries/selectors' import { generatePath, matchPath, useHistory, useParams, useRouteMatch } from 'react-router' import { SwapIcon } from 'components/Icons/SwapIcon' import { Main } from 'components/Layout/Main' +import { thorchainBlockTimeMs } from 'lib/utils/thorchain/constants' import { calculateTVL, get24hSwapChangePercentage, @@ -102,13 +104,9 @@ export const Pool = () => { return parsedPools.find(pool => pool.opportunityId === routeOpportunityId) }, [params, parsedPools]) - const { data: isTradingActive, isLoading: isTradingActiveLoading } = useQuery({ - ...reactQueries.common.isTradingActive({ - assetId: foundPool?.assetId, - swapperName: SwapperName.Thorchain, - }), - // @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object - enabled: Boolean(foundPool?.assetId), + const { data: inboundAddressesData, isLoading: isInboundAddressesDataLoading } = useQuery({ + ...reactQueries.thornode.inboundAddresses(), + enabled: !!foundPool, // Go stale instantly staleTime: 0, // Never store queries in cache since we always want fresh data @@ -116,8 +114,25 @@ export const Pool = () => { refetchOnWindowFocus: true, refetchOnMount: true, refetchInterval: 60_000, + select: data => selectInboundAddressData(data, foundPool?.assetId), + }) + + const { data: mimir, isLoading: isMimirLoading } = useQuery({ + ...reactQueries.thornode.mimir(), + staleTime: thorchainBlockTimeMs, }) + const isTradingActive = useMemo(() => { + if (isMimirLoading || !mimir) return + + return selectIsTradingActive({ + assetId: foundPool?.assetId, + inboundAddressResponse: inboundAddressesData, + swapperName: SwapperName.Thorchain, + mimir, + }) + }, [foundPool?.assetId, inboundAddressesData, isMimirLoading, mimir]) + const poolAssetIds = useMemo(() => { if (!foundPool) return [] @@ -215,11 +230,13 @@ export const Pool = () => {