Skip to content

Commit

Permalink
feat: lifi get final quote (#8326)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre authored Dec 11, 2024
1 parent 3f0a0ef commit 56743c0
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { transformLifiStepFeeData } from '../utils/transformLifiFeeData/transfor
import type { LifiTradeQuote } from '../utils/types'

export async function getTrade(
input: GetEvmTradeQuoteInput,
input: GetEvmTradeQuoteInput & { lifiAllowedTools?: string[] | undefined },
deps: SwapperDeps,
lifiChainMap: Map<ChainId, ChainKey>,
): Promise<Result<LifiTradeQuote[], SwapErrorRight>> {
Expand All @@ -49,6 +49,7 @@ export async function getTrade(
supportsEIP1559,
affiliateBps,
potentialAffiliateBps,
lifiAllowedTools,
} = input

const slippageTolerancePercentageDecimal =
Expand Down Expand Up @@ -97,6 +98,9 @@ export async function getTrade(
// reverts, partial swaps, wrong received tokens (due to out-of-gas mid-trade), etc. For now,
// these bridges are disabled.
bridges: { deny: ['stargate', 'stargateV2', 'stargateV2Bus', 'amarok', 'arbitrum'] },
...(lifiAllowedTools && {
exchanges: { allow: lifiAllowedTools },
}),
allowSwitchChain: true,
fee: affiliateBpsDecimalPercentage.isZero()
? undefined
Expand Down Expand Up @@ -243,10 +247,11 @@ export async function getTrade(

return {
id: selectedLifiRoute.id,
// This isn't a mistake - with Li.Fi, we can never go with our full-on intent of rate vs. quotes. As soon as a wallet is connected, we get a *quote*
// even though we're lying and saying this is a rate. With the "rate" containing a receiveAddress, a quote will *not* be fired at pre-sign time, which
// ensures users aren't rugged with routes that aren't available anymore when going from input to confirm
receiveAddress,
// TODO(gomes): when https://github.com/shapeshift/web/pull/8309 goes in, this goes out
// We do need receiveAddress in *input* to send it as fromAddress for routes req for more reliable rates, but with receiveAddress currently being the quotes/rates discriminator,
// we need to exclude it from method in *output*
receiveAddress: input.quoteOrRate === 'quote' ? receiveAddress : undefined,
lifiTools: selectedLifiRoute.steps.map(step => step.tool),
affiliateBps,
potentialAffiliateBps,
steps,
Expand Down Expand Up @@ -292,7 +297,7 @@ export async function getTrade(
// This isn't a mistake - With Li.Fi, we get the exact same thing back whether quote or rate, however, the input *is* different

export const getTradeQuote = (
input: GetEvmTradeQuoteInputBase,
input: GetEvmTradeQuoteInputBase & { lifiAllowedTools?: string[] | undefined },
deps: SwapperDeps,
lifiChainMap: Map<ChainId, ChainKey>,
): Promise<Result<LifiTradeQuote[], SwapErrorRight>> => getTrade(input, deps, lifiChainMap)
2 changes: 2 additions & 0 deletions packages/swapper/src/swappers/LifiSwapper/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import type { TradeQuote, TradeRate } from '../../../types'

export interface LifiTradeQuote extends TradeQuote {
selectedLifiRoute?: Route
lifiTools?: string[] | undefined
}
export interface LifiTradeRate extends TradeRate {
selectedLifiRoute?: Route
lifiTools?: string[] | undefined
}

export type LifiTool = {
Expand Down
1 change: 1 addition & 0 deletions packages/swapper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ type CommonTradeInputBase = {
potentialAffiliateBps: string
affiliateBps: string
allowMultiHop: boolean
lifiAllowedTools?: string[] | undefined
slippageTolerancePercentageDecimal?: string
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type GetTradeQuoteInputArgs = {
slippageTolerancePercentageDecimal?: string
sellAmountBeforeFeesCryptoPrecision: string
allowMultiHop: boolean
lifiAllowedTools?: string[]
// Potential affiliate bps - may be waved out either entirely or partially with FOX discounts
potentialAffiliateBps: string
// Actual affiliate bps - if the FOX discounts is off, this will be the same as *affiliateBps*
Expand All @@ -42,6 +43,7 @@ export const getTradeQuoteInput = async ({
receiveAddress,
sellAmountBeforeFeesCryptoPrecision,
allowMultiHop,
lifiAllowedTools,
affiliateBps,
potentialAffiliateBps,
slippageTolerancePercentageDecimal,
Expand All @@ -59,6 +61,7 @@ export const getTradeQuoteInput = async ({
affiliateBps: affiliateBps ?? '0',
potentialAffiliateBps: potentialAffiliateBps ?? '0',
allowMultiHop,
lifiAllowedTools,
slippageTolerancePercentageDecimal,
quoteOrRate,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
swappers,
} from '@shapeshiftoss/swapper'
import { isThorTradeQuote } from '@shapeshiftoss/swapper/dist/swappers/ThorchainSwapper/getThorTradeQuote/getTradeQuote'
import type { LifiTradeRate } from '@shapeshiftoss/swapper/src/swappers/LifiSwapper/utils/types'
import { skipToken as reactQuerySkipToken, useQuery } from '@tanstack/react-query'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useTradeReceiveAddress } from 'components/MultiHopTrade/components/TradeInput/hooks/useTradeReceiveAddress'
Expand Down Expand Up @@ -228,10 +229,6 @@ export const useGetTradeQuotes = () => {
}, [hopExecutionMetadata?.permit2, hopExecutionMetadata?.state, swapperName])

const shouldFetchTradeQuotes = useMemo(() => {
// This isn't a mistake, we're not fetching anything.
// Li.Fi rate is an actual quote and we want to leverage cache we got from "rate" time, see swapperApi's `getTradeQuote` for more details
if (swapperName === SwapperName.LIFI) return true

return Boolean(
hasFocus &&
// Only fetch quote if the current "quote" is a rate (which we have gotten from input step)
Expand All @@ -243,27 +240,16 @@ export const useGetTradeQuotes = () => {
sellAccountMetadata &&
receiveAddress,
)
}, [
swapperName,
hasFocus,
activeTrade,
isFetchStep,
sellAccountId,
sellAccountMetadata,
receiveAddress,
])
}, [hasFocus, activeTrade, isFetchStep, sellAccountId, sellAccountMetadata, receiveAddress])

const queryFnOrSkip = useMemo(() => {
// Only run this query when we're actually ready
if (!isFetchStep) return reactQuerySkipToken
// And only run it once
if (activeTrade && swapperName !== SwapperName.LIFI && isExecutableTradeQuote(activeTrade))
return reactQuerySkipToken
if (activeTrade && isExecutableTradeQuote(activeTrade)) return reactQuerySkipToken

return async () => {
if (swapperName !== SwapperName.LIFI) {
dispatch(swapperApi.util.invalidateTags(['TradeQuote']))
}
dispatch(swapperApi.util.invalidateTags(['TradeQuote']))

const sellAccountNumber = sellAccountMetadata?.bip44Params?.accountNumber

Expand All @@ -289,9 +275,8 @@ export const useGetTradeQuotes = () => {
sellAccountType: sellAccountMetadata?.accountType,
buyAsset,
wallet: wallet ?? undefined,
// This isn't a mistake, we're not fetching anything.
// Li.Fi rate is an actual quote and we want to leverage cache, see swapperApi's `getTradeQuote` for more details
quoteOrRate: swapperName === SwapperName.LIFI ? 'rate' : 'quote',
lifiAllowedTools: (activeRateRef?.current as LifiTradeRate)?.lifiTools,
quoteOrRate: 'quote',
receiveAddress,
sellAmountBeforeFeesCryptoPrecision: sellAmountCryptoPrecision,
allowMultiHop: true,
Expand Down Expand Up @@ -320,7 +305,6 @@ export const useGetTradeQuotes = () => {
sellAmountCryptoPrecision,
sellAsset,
sellAssetUsdRate,
swapperName,
thorVotingPower,
userSlippageTolerancePercentageDecimal,
votingPower,
Expand Down
1 change: 1 addition & 0 deletions src/components/MultiHopTrade/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type TradeQuoteInputCommonArgs = Pick<
| 'allowMultiHop'
| 'slippageTolerancePercentageDecimal'
| 'quoteOrRate'
| 'lifiAllowedTools'
>

export enum TradeInputTab {
Expand Down
9 changes: 0 additions & 9 deletions src/state/apis/swapper/swapperApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,6 @@ export const swapperApi = createApi({
return getTradeRates(
{
...tradeQuoteInput,
// Receive address should always be undefined for trade *rates*, however, we *do* pass it to check for cross-account support
// so we have to ensure it is gone by the time we call getTradeRates
receiveAddress:
// Ok, we may have lied just before. Li.Fi is the odd one, and we don't want to fetch a final quote for it.
// The reason is Li.Fi routes are very fragile and from one request to the other, it's more likely than not a tool may not be returned anymore, resulting in early null returns
// in swapper, i.e a totally broken state.
// By keeping the receiveAddress in all the way through, and returning it in LIFI's `getTradeRate()`, the rate becomes an effective quote, and the
// `shouldFetchTradeQuote = isExecutableTradeQuote(activeTrade)` never becomes true, meaning we keep the rate as a quote.
swapperName === SwapperName.LIFI ? tradeQuoteInput.receiveAddress : undefined,
affiliateBps,
} as GetTradeRateInput,
swapperName,
Expand Down

0 comments on commit 56743c0

Please sign in to comment.