Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: incorrect fox discounts reported by "you saved" UI #8598

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: confirming a quote sets the confirmed quote in redux, which is t…
…hen consumed thereafter
woodenfurniture committed Jan 16, 2025
commit 81d5ead97e2d223d03ccfb2c2bf91c3d44c6b547
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ import { Text } from 'components/Text'
import { bnOrZero } from 'lib/bignumber/bignumber'
import { fromBaseUnit } from 'lib/math'
import {
selectActiveQuote,
selectConfirmedQuote,
selectConfirmedTradeExecutionState,
selectLastHop,
} from 'state/slices/tradeQuoteSlice/selectors'
@@ -38,25 +38,27 @@ export const MultiHopTradeConfirm = memo(() => {
const history = useHistory()

const [shouldShowWarningAcknowledgement, setShouldShowWarningAcknowledgement] = useState(false)
const activeQuote = useAppSelector(selectActiveQuote)
const { isModeratePriceImpact, priceImpactPercentage } = usePriceImpact(activeQuote)
const confirmedQuote = useAppSelector(selectConfirmedQuote)
const { isModeratePriceImpact, priceImpactPercentage } = usePriceImpact(confirmedQuote)
const lastHop = useAppSelector(selectLastHop)

const initialActiveTradeIdRef = useRef(activeQuote?.id ?? '')
const initialActiveTradeIdRef = useRef(confirmedQuote?.id ?? '')

const { isLoading } = useIsApprovalInitiallyNeeded()

const isArbitrumBridgeWithdraw = useMemo(() => {
return isArbitrumBridgeTradeQuoteOrRate(activeQuote) && activeQuote.direction === 'withdrawal'
}, [activeQuote])
return (
isArbitrumBridgeTradeQuoteOrRate(confirmedQuote) && confirmedQuote?.direction === 'withdrawal'
)
}, [confirmedQuote])

useEffect(() => {
if (isLoading || !activeQuote) return
if (isLoading || !confirmedQuote) return
// Only set the trade to initialized if it was actually initializing previously. Now that we shove quotes in at confirm time, we can't rely on this effect only running once.
if (confirmedTradeExecutionState !== TradeExecutionState.Initializing) return

dispatch(tradeQuoteSlice.actions.setTradeInitialized(activeQuote.id))
}, [dispatch, isLoading, activeQuote, confirmedTradeExecutionState])
dispatch(tradeQuoteSlice.actions.setTradeInitialized(confirmedQuote.id))
}, [dispatch, isLoading, confirmedQuote, confirmedTradeExecutionState])

const isTradeComplete = useMemo(
() => confirmedTradeExecutionState === TradeExecutionState.TradeComplete,
@@ -93,9 +95,9 @@ export const MultiHopTradeConfirm = memo(() => {
])

const handleTradeConfirm = useCallback(() => {
if (!activeQuote) return
dispatch(tradeQuoteSlice.actions.confirmTrade(activeQuote.id))
}, [dispatch, activeQuote])
if (!confirmedQuote) return
dispatch(tradeQuoteSlice.actions.confirmTrade(confirmedQuote.id))
}, [dispatch, confirmedQuote])

const handleSubmit = useCallback(() => {
if (isModeratePriceImpact) {
@@ -139,17 +141,17 @@ export const MultiHopTradeConfirm = memo(() => {
</Heading>
</WithBackButton>
</CardHeader>
{isTradeComplete && activeQuote && lastHop ? (
{isTradeComplete && confirmedQuote && lastHop ? (
<TradeSuccess
handleBack={handleBack}
titleTranslation={
isArbitrumBridgeWithdraw ? 'bridge.arbitrum.success.tradeSuccess' : undefined
}
sellAsset={activeQuote?.steps[0].sellAsset}
sellAsset={confirmedQuote?.steps[0].sellAsset}
buyAsset={lastHop.buyAsset}
sellAmountCryptoPrecision={fromBaseUnit(
activeQuote.steps[0].sellAmountIncludingProtocolFeesCryptoBaseUnit,
activeQuote.steps[0].sellAsset.precision,
confirmedQuote.steps[0].sellAmountIncludingProtocolFeesCryptoBaseUnit,
confirmedQuote.steps[0].sellAsset.precision,
)}
buyAmountCryptoPrecision={fromBaseUnit(
lastHop.buyAmountAfterFeesCryptoBaseUnit,
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ import { useLocaleFormatter } from 'hooks/useLocaleFormatter/useLocaleFormatter'
import { fromBaseUnit } from 'lib/math'
import { assertUnreachable } from 'lib/utils'
import {
selectActiveQuote,
selectConfirmedQuote,
selectHopExecutionMetadata,
selectHopNetworkFeeUserCurrency,
selectHopTotalProtocolFeesFiatPrecision,
@@ -91,11 +91,13 @@ export const Hop = ({
const history = useHistory()
const isMultiHopTrade = useAppSelector(selectIsActiveQuoteMultiHop)

const activeQuote = useAppSelector(selectActiveQuote)
const confirmedQuote = useAppSelector(selectConfirmedQuote)

const isArbitrumBridgeWithdraw = useMemo(() => {
return isArbitrumBridgeTradeQuoteOrRate(activeQuote) && activeQuote.direction === 'withdrawal'
}, [activeQuote])
return (
isArbitrumBridgeTradeQuoteOrRate(confirmedQuote) && confirmedQuote.direction === 'withdrawal'
)
}, [confirmedQuote])

const hopExecutionMetadataFilter = useMemo(() => {
return {
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Box, Stack } from '@chakra-ui/react'
import { memo, useMemo } from 'react'
import {
selectActiveQuote,
selectActiveSwapperName,
selectConfirmedQuote,
selectFirstHop,
selectIsActiveQuoteMultiHop,
selectLastHop,
@@ -30,17 +30,17 @@ export const Hops = memo((props: HopsProps) => {
const swapperName = useAppSelector(selectActiveSwapperName)
const firstHop = useAppSelector(selectFirstHop)
const lastHop = useAppSelector(selectLastHop)
const activeQuote = useAppSelector(selectActiveQuote)
const confirmedQuote = useAppSelector(selectConfirmedQuote)
const isMultiHopTrade = useAppSelector(selectIsActiveQuoteMultiHop)

const divider = useMemo(
() => <Box height={2} borderColor='border.bold' bg='background.surface.base' />,
[],
)

if (!activeQuote || !firstHop || !swapperName) return null
if (!confirmedQuote || !firstHop || !swapperName) return null

const activeTradeId = activeQuote.id
const activeTradeId = confirmedQuote.id

return (
<Stack spacing={0} divider={divider} borderColor='border.base'>
@@ -50,7 +50,7 @@ export const Hops = memo((props: HopsProps) => {
hopIndex={0}
isOpen={isFirstHopOpen}
onToggleIsOpen={onToggleFirstHop}
slippageTolerancePercentageDecimal={activeQuote.slippageTolerancePercentageDecimal}
slippageTolerancePercentageDecimal={confirmedQuote.slippageTolerancePercentageDecimal}
activeTradeId={activeTradeId}
initialActiveTradeId={initialActiveTradeId}
/>
@@ -61,7 +61,7 @@ export const Hops = memo((props: HopsProps) => {
hopIndex={1}
isOpen={isSecondHopOpen}
onToggleIsOpen={onToggleSecondHop}
slippageTolerancePercentageDecimal={activeQuote.slippageTolerancePercentageDecimal}
slippageTolerancePercentageDecimal={confirmedQuote.slippageTolerancePercentageDecimal}
activeTradeId={activeTradeId}
initialActiveTradeId={initialActiveTradeId}
/>
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import {
} from '@chakra-ui/react'
import { InlineCopyButton } from 'components/InlineCopyButton'
import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis'
import { selectActiveQuote } from 'state/slices/tradeQuoteSlice/selectors'
import { selectConfirmedQuote } from 'state/slices/tradeQuoteSlice/selectors'
import { useAppSelector } from 'state/store'

const stepStyle = {
@@ -54,8 +54,8 @@ export type StepperStepProps = {
}

const LastStepTag = () => {
const activeQuote = useAppSelector(selectActiveQuote)
const receiveAddress = activeQuote?.receiveAddress
const confirmedQuote = useAppSelector(selectConfirmedQuote)
const receiveAddress = confirmedQuote?.receiveAddress

if (!receiveAddress) return null

Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import {
selectSecondHopSellAccountId,
} from 'state/slices/tradeInputSlice/selectors'
import {
selectActiveQuote,
selectConfirmedQuote,
selectFirstHop,
selectIsActiveQuoteMultiHop,
selectSecondHop,
@@ -107,7 +107,7 @@ const useIsAllowanceResetInitiallyRequiredForHop = (

export const useIsApprovalInitiallyNeeded = () => {
const dispatch = useAppDispatch()
const activeQuote = useAppSelector(selectActiveQuote)
const confirmedQuote = useAppSelector(selectConfirmedQuote)
const firstHop = useAppSelector(selectFirstHop)
const secondHop = useAppSelector(selectSecondHop)
const isMultiHopTrade = useAppSelector(selectIsActiveQuoteMultiHop)
@@ -117,18 +117,18 @@ export const useIsApprovalInitiallyNeeded = () => {
const {
isLoading: isFirstHopAllowanceApprovalRequirementsLoading,
isApprovalInitiallyNeeded: isApprovalInitiallyNeededForFirstHop,
} = useIsApprovalInitiallyNeededForHop(activeQuote?.id, firstHop, firstHopSellAssetAccountId)
} = useIsApprovalInitiallyNeededForHop(confirmedQuote?.id, firstHop, firstHopSellAssetAccountId)

const {
isLoading: isSecondHopAllowanceApprovalRequirementsLoading,
isApprovalInitiallyNeeded: isApprovalInitiallyNeededForSecondHop,
} = useIsApprovalInitiallyNeededForHop(activeQuote?.id, secondHop, secondHopSellAssetAccountId)
} = useIsApprovalInitiallyNeededForHop(confirmedQuote?.id, secondHop, secondHopSellAssetAccountId)

const {
isLoading: isFirstHopAllowanceResetRequirementsLoading,
isAllowanceResetNeeded: isAllowanceResetNeededForFirstHop,
} = useIsAllowanceResetInitiallyRequiredForHop(
activeQuote?.id,
confirmedQuote?.id,
firstHop,
firstHopSellAssetAccountId,
)
@@ -137,7 +137,7 @@ export const useIsApprovalInitiallyNeeded = () => {
isLoading: isSecondHopAllowanceResetRequirementsLoading,
isAllowanceResetNeeded: isAllowanceResetNeededForSecondHop,
} = useIsAllowanceResetInitiallyRequiredForHop(
activeQuote?.id,
confirmedQuote?.id,
secondHop,
secondHopSellAssetAccountId,
)
@@ -157,33 +157,33 @@ export const useIsApprovalInitiallyNeeded = () => {

useEffect(() => {
if (isFirstHopLoading || (secondHop !== undefined && isSecondHopLoading)) return
if (!activeQuote?.id) return
if (!confirmedQuote?.id) return

dispatch(
tradeQuoteSlice.actions.setInitialApprovalRequirements({
id: activeQuote.id,
id: confirmedQuote.id,
firstHop: isApprovalInitiallyNeededForFirstHop ?? false,
secondHop: isApprovalInitiallyNeededForSecondHop ?? false,
}),
)

dispatch(
tradeQuoteSlice.actions.setAllowanceResetRequirements({
id: activeQuote.id,
id: confirmedQuote.id,
firstHop: isAllowanceResetNeededForFirstHop ?? false,
secondHop: isAllowanceResetNeededForSecondHop ?? false,
}),
)

dispatch(
tradeQuoteSlice.actions.setPermit2Requirements({
id: activeQuote.id,
id: confirmedQuote.id,
firstHop: isPermit2RequiredForFirstHop,
secondHop: isPermit2RequiredForSecondHop,
}),
)
}, [
activeQuote?.id,
confirmedQuote?.id,
dispatch,
isAllowanceResetNeededForFirstHop,
isAllowanceResetNeededForSecondHop,
Original file line number Diff line number Diff line change
@@ -41,8 +41,8 @@ import { assertGetUtxoChainAdapter } from 'lib/utils/utxo'
import { selectThorVotingPower } from 'state/apis/snapshot/selectors'
import { selectAssetById, selectPortfolioAccountMetadataByAccountId } from 'state/slices/selectors'
import {
selectActiveQuote,
selectActiveSwapperName,
selectConfirmedQuote,
selectHopExecutionMetadata,
selectHopSellAccountId,
selectTradeSlippagePercentageDecimal,
@@ -101,7 +101,7 @@ export const useTradeExecution = (
selectPortfolioAccountMetadataByAccountId(state, accountMetadataFilter),
)
const swapperName = useAppSelector(selectActiveSwapperName)
const tradeQuote = useAppSelector(selectActiveQuote)
const tradeQuote = useAppSelector(selectConfirmedQuote)

// This is ugly, but we need to use refs to get around the fact that the
// poll fn effectively creates a closure and will hold stale variables forever
@@ -277,7 +277,7 @@ export const useTradeExecution = (

const output = await execution.execEvmMessage({
swapperName,
tradeQuote,
tradeQuote: tradeQuote as TradeQuote,
stepIndex: hopIndex,
slippageTolerancePercentageDecimal,
from,
@@ -307,8 +307,8 @@ export const useTradeExecution = (

const receiverAddress =
stepBuyAssetAssetId === bchAssetId
? tradeQuote.receiveAddress.replace('bitcoincash:', '')
: tradeQuote.receiveAddress
? (tradeQuote as TradeQuote).receiveAddress?.replace('bitcoincash:', '')
: (tradeQuote as TradeQuote).receiveAddress

switch (stepSellAssetChainNamespace) {
case CHAIN_NAMESPACE.Evm: {
@@ -318,7 +318,7 @@ export const useTradeExecution = (

const output = await execution.execEvmTransaction({
swapperName,
tradeQuote,
tradeQuote: tradeQuote as TradeQuote,
stepIndex: hopIndex,
slippageTolerancePercentageDecimal,
from,
@@ -359,7 +359,7 @@ export const useTradeExecution = (

const output = await execution.execUtxoTransaction({
swapperName,
tradeQuote,
tradeQuote: tradeQuote as TradeQuote,
stepIndex: hopIndex,
slippageTolerancePercentageDecimal,
xpub,
@@ -389,7 +389,7 @@ export const useTradeExecution = (
const from = await adapter.getAddress({ accountNumber, wallet })
const output = await execution.execCosmosSdkTransaction({
swapperName,
tradeQuote,
tradeQuote: tradeQuote as TradeQuote,
stepIndex: hopIndex,
slippageTolerancePercentageDecimal,
from,
@@ -415,7 +415,7 @@ export const useTradeExecution = (
})
const output = await adapter.broadcastTransaction({
senderAddress: from,
receiverAddress: tradeQuote.receiveAddress,
receiverAddress: (tradeQuote as TradeQuote).receiveAddress,
hex: signedTx,
})

@@ -431,7 +431,7 @@ export const useTradeExecution = (
const from = await adapter.getAddress({ accountNumber, wallet })
const output = await execution.execSolanaTransaction({
swapperName,
tradeQuote,
tradeQuote: tradeQuote as TradeQuote,
stepIndex: hopIndex,
slippageTolerancePercentageDecimal,
from,
@@ -442,7 +442,7 @@ export const useTradeExecution = (
})
const output = await adapter.broadcastTransaction({
senderAddress: from,
receiverAddress: tradeQuote.receiveAddress,
receiverAddress: (tradeQuote as TradeQuote).receiveAddress,
hex: signedTx,
})

Loading