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

feat: lifi get final quote #8326

Merged
merged 3 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
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*
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
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',
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
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,
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
affiliateBps,
} as GetTradeRateInput,
swapperName,
Expand Down