Skip to content

Commit

Permalink
fix: attach fox discount info to swapper state before executing trade
Browse files Browse the repository at this point in the history
  • Loading branch information
woodenfurniture committed Jan 16, 2025
1 parent 81d5ead commit dbe8838
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 21 deletions.
17 changes: 14 additions & 3 deletions src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { useWallet } from 'hooks/useWallet/useWallet'
import { fromBaseUnit } from 'lib/math'
import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton'
import { MixPanelEvent } from 'lib/mixpanel/types'
import { selectIsVotingPowerLoading } from 'state/apis/snapshot/selectors'
import { selectCalculatedFees, selectIsVotingPowerLoading } from 'state/apis/snapshot/selectors'
import type { ApiQuote } from 'state/apis/swapper/types'
import {
selectIsAnyAccountMetadataLoadedForChainId,
Expand All @@ -49,11 +49,12 @@ import {
selectFirstHop,
selectIsTradeQuoteRequestAborted,
selectIsUnsafeActiveQuote,
selectQuoteSellAmountUsd,
selectShouldShowTradeQuoteOrAwaitInput,
selectSortedTradeQuotes,
} from 'state/slices/tradeQuoteSlice/selectors'
import { tradeQuoteSlice } from 'state/slices/tradeQuoteSlice/tradeQuoteSlice'
import { store, useAppDispatch, useAppSelector } from 'state/store'
import { store, useAppDispatch, useAppSelector, useSelectorWithArgs } from 'state/store'

import { useAccountIds } from '../../hooks/useAccountIds'
import { SharedTradeInput } from '../SharedTradeInput/SharedTradeInput'
Expand Down Expand Up @@ -107,6 +108,7 @@ export const TradeInput = ({ isCompact, tradeInputRef, onChangeTab }: TradeInput
const activeQuoteMeta = useAppSelector(selectActiveQuoteMeta)
const sellAmountCryptoPrecision = useAppSelector(selectInputSellAmountCryptoPrecision)
const sellAmountUserCurrency = useAppSelector(selectInputSellAmountUserCurrency)
const sellAmountUsd = useAppSelector(selectQuoteSellAmountUsd)
const buyAmountAfterFeesCryptoPrecision = useAppSelector(selectBuyAmountAfterFeesCryptoPrecision)
const buyAmountAfterFeesUserCurrency = useAppSelector(selectBuyAmountAfterFeesUserCurrency)
const shouldShowTradeQuoteOrAwaitInput = useAppSelector(selectShouldShowTradeQuoteOrAwaitInput)
Expand All @@ -126,6 +128,14 @@ export const TradeInput = ({ isCompact, tradeInputRef, onChangeTab }: TradeInput
selectIsAnyAccountMetadataLoadedForChainId(state, isAnyAccountMetadataLoadedForChainIdFilter),
)
const walletId = useAppSelector(selectWalletId)
const calculatedFeesParams = useMemo(
() => ({
feeModel: 'SWAPPER',
inputAmountUsd: sellAmountUsd,
}),
[sellAmountUsd],
)
const calculatedFees = useSelectorWithArgs(selectCalculatedFees, calculatedFeesParams)

const sellAssetUsdRate = useAppSelector(state => selectUsdRateByAssetId(state, sellAsset.assetId))
const buyAssetUsdRate = useAppSelector(state => selectUsdRateByAssetId(state, buyAsset.assetId))
Expand Down Expand Up @@ -257,7 +267,7 @@ export const TradeInput = ({ isCompact, tradeInputRef, onChangeTab }: TradeInput

// Set the confirmed quote for execution, with a snapshot of the affiliate fees for display after the trade is executed.
// This is done to handle the fox power calculation changing due to FOX balance changes after the trade is executed.
dispatch(tradeQuoteSlice.actions.setConfirmedQuote({ quote: activeQuote }))
dispatch(tradeQuoteSlice.actions.setConfirmedQuote({ quote: activeQuote, calculatedFees }))
dispatch(tradeQuoteSlice.actions.clearQuoteExecutionState(activeQuote.id))

if (isLedger(wallet)) {
Expand All @@ -275,6 +285,7 @@ export const TradeInput = ({ isCompact, tradeInputRef, onChangeTab }: TradeInput
}, [
activeQuote,
activeQuoteMeta,
calculatedFees,
dispatch,
handleConnect,
history,
Expand Down
1 change: 1 addition & 0 deletions src/state/slices/tradeQuoteSlice/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const initialTradeExecutionState = {
export const initialState: TradeQuoteSliceState = {
activeQuoteMeta: undefined,
confirmedQuote: undefined,
confirmedFees: undefined,
activeStep: undefined,
tradeExecution: {},
tradeQuotes: {},
Expand Down
30 changes: 12 additions & 18 deletions src/state/slices/tradeQuoteSlice/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import type { Asset } from '@shapeshiftoss/types'
import { identity } from 'lodash'
import type { Selector } from 'reselect'
import { bn, bnOrZero } from 'lib/bignumber/bignumber'
import type { CalculateFeeBpsReturn } from 'lib/fees/model'
import { fromBaseUnit } from 'lib/math'
import { selectCalculatedFees } from 'state/apis/snapshot/selectors'
import { validateQuoteRequest } from 'state/apis/swapper/helpers/validateQuoteRequest'
import { selectIsTradeQuoteApiQueryPending } from 'state/apis/swapper/selectors'
import type { ApiQuote, ErrorWithMeta, TradeQuoteError } from 'state/apis/swapper/types'
Expand Down Expand Up @@ -205,6 +205,11 @@ export const selectConfirmedQuote: Selector<ReduxState, TradeQuote | TradeRate |
return tradeQuoteState.confirmedQuote
})

export const selectConfirmedFees: Selector<ReduxState, CalculateFeeBpsReturn | undefined> =
createDeepEqualOutputSelector(selectTradeQuoteSlice, tradeQuoteState => {
return tradeQuoteState.confirmedFees
})

export const selectActiveQuoteMetaOrDefault: Selector<
ReduxState,
{ swapperName: SwapperName; identifier: string } | undefined
Expand Down Expand Up @@ -564,32 +569,21 @@ export const selectActiveQuoteAffiliateBps: Selector<ReduxState, string | undefi
})

export const selectTradeQuoteAffiliateFeeAfterDiscountUsd = createSelector(
(state: ReduxState) =>
selectCalculatedFees(state, {
feeModel: 'SWAPPER',
inputAmountUsd: selectQuoteSellAmountUsd(state),
}),
selectConfirmedFees,
selectActiveQuoteAffiliateBps,
(calculatedFees, affiliateBps) => {
if (!affiliateBps) return
if (!affiliateBps || !calculatedFees) return
if (affiliateBps === '0') return bn(0)

return calculatedFees.feeUsd
},
)

export const selectTradeQuoteAffiliateFeeDiscountUsd = createSelector(
(state: ReduxState) =>
selectCalculatedFees(state, {
feeModel: 'SWAPPER',
inputAmountUsd: selectQuoteSellAmountUsd(state),
}),
selectActiveQuoteAffiliateBps,
(calculatedFees, affiliateBps) => {
if (!affiliateBps) return
if (affiliateBps === '0') return bn(0)

return calculatedFees.foxDiscountUsd
selectConfirmedFees,
confirmedFees => {
if (!confirmedFees) return
return confirmedFees.foxDiscountUsd
},
)

Expand Down
3 changes: 3 additions & 0 deletions src/state/slices/tradeQuoteSlice/tradeQuoteSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit'
import type { SwapperName, TradeQuote, TradeRate } from '@shapeshiftoss/swapper'
import { orderBy, uniqBy } from 'lodash'
import type { InterpolationOptions } from 'node-polyglot'
import type { CalculateFeeBpsReturn } from 'lib/fees/model'
import type { ApiQuote } from 'state/apis/swapper/types'

import { initialState, initialTradeExecutionState } from './constants'
Expand Down Expand Up @@ -56,9 +57,11 @@ export const tradeQuoteSlice = createSlice({
state,
action: PayloadAction<{
quote: TradeQuote | TradeRate
calculatedFees: CalculateFeeBpsReturn
}>,
) => {
state.confirmedQuote = action.payload.quote
state.confirmedFees = action.payload.calculatedFees
},
clearQuoteExecutionState: (state, action: PayloadAction<TradeQuote['id']>) => {
state.tradeExecution[action.payload] = initialTradeExecutionState
Expand Down
4 changes: 4 additions & 0 deletions src/state/slices/tradeQuoteSlice/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SwapperName, TradeQuote, TradeRate } from '@shapeshiftoss/swapper'
import type { PartialRecord } from '@shapeshiftoss/types'
import type { InterpolationOptions } from 'node-polyglot'
import type { CalculateFeeBpsReturn } from 'lib/fees/model'
import type { ApiQuote } from 'state/apis/swapper/types'

export type ActiveQuoteMeta = {
Expand All @@ -12,6 +13,9 @@ export type TradeQuoteSliceState = {
activeStep: number | undefined // Make sure to actively check for undefined vs. falsy here. 0 is the first step, undefined means no active step yet
activeQuoteMeta: ActiveQuoteMeta | undefined // the selected quote metadata used to find the active quote in the api responses
confirmedQuote: TradeQuote | TradeRate | undefined // the quote being executed
// Used to display the "You saved" message in the TradeSuccess component. This needs to be stored
// here because trading fox will affect the calculation after the trade has been executed.
confirmedFees: CalculateFeeBpsReturn | undefined
tradeExecution: Record<TradeQuote['id'], TradeExecutionMetadata>
tradeQuotes: PartialRecord<SwapperName, Record<string, ApiQuote>> // mapping from swapperName to quoteId to ApiQuote
tradeQuoteDisplayCache: ApiQuote[]
Expand Down
1 change: 1 addition & 0 deletions src/test/mocks/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export const mockStore: ReduxState = {
tradeQuoteSlice: {
activeQuoteMeta: undefined,
confirmedQuote: undefined,
confirmedFees: undefined,
activeStep: undefined,
tradeExecution: {},
tradeQuotes: {},
Expand Down

0 comments on commit dbe8838

Please sign in to comment.