Skip to content

Commit

Permalink
fix: li.fi unstable quotes use original rate (#8350)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre authored Dec 13, 2024
1 parent 247113a commit 804c821
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
COW_SWAP_NATIVE_ASSET_MARKER_ADDRESS,
DEFAULT_ADDRESS,
} from '../../../cowswap-utils/constants'
import type { GetTradeQuoteInput, SwapperConfig, TradeQuote } from '../../../types'
import type { GetTradeQuoteInput, SwapperConfig, TradeQuote, TradeRate } from '../../../types'
import { SwapperName, TradeQuoteError } from '../../../types'
import {
ETH,
Expand Down Expand Up @@ -382,6 +382,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

const maybeTradeQuote = await getCowSwapTradeQuote(input, MOCK_COWSWAP_CONFIG)
Expand Down Expand Up @@ -409,6 +410,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

mockedCowService.post.mockReturnValue(
Expand Down Expand Up @@ -454,6 +456,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

mockedCowService.post.mockReturnValue(
Expand Down Expand Up @@ -499,6 +502,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

mockedCowService.post.mockReturnValue(
Expand Down Expand Up @@ -544,6 +548,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

mockedCowService.post.mockReturnValue(
Expand Down Expand Up @@ -589,6 +594,7 @@ describe('getCowSwapTradeQuote', () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: '0.005', // 0.5%
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

mockedCowService.post.mockReturnValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export const getTradeRate = async (
id: uuid(),
quoteOrRate: 'rate',
rate: inputOutputRate,
receiveAddress: undefined,
receiveAddress,
potentialAffiliateBps: affiliateBps,
affiliateBps,
slippageTolerancePercentageDecimal,
Expand Down
8 changes: 6 additions & 2 deletions packages/swapper/src/swappers/LifiSwapper/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export const lifiApi: SwapperApi = {
)

return tradeQuoteResult.map(quote =>
quote.map(({ selectedLifiRoute, ...tradeQuote }) => {
quote.map(tradeQuote => {
const { selectedLifiRoute } = tradeQuote

// TODO: quotes below the minimum aren't valid and should not be processed as such
// selectedLifiRoute will be missing for quotes below the minimum
if (!selectedLifiRoute) throw Error('missing selectedLifiRoute')
Expand Down Expand Up @@ -98,7 +100,9 @@ export const lifiApi: SwapperApi = {
const tradeRateResult = await getTradeRate(input as GetEvmTradeRateInput, deps, lifiChainMap)

return tradeRateResult.map(quote =>
quote.map(({ selectedLifiRoute, ...tradeQuote }) => {
quote.map(tradeQuote => {
const { selectedLifiRoute } = tradeQuote

// TODO: quotes below the minimum aren't valid and should not be processed as such
// selectedLifiRoute will be missing for quotes below the minimum
if (!selectedLifiRoute) throw Error('missing selectedLifiRoute')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant
import type {
GetEvmTradeQuoteInput,
GetEvmTradeQuoteInputBase,
GetEvmTradeRateInput,
MultiHopTradeQuoteSteps,
SingleHopTradeQuoteSteps,
SwapErrorRight,
Expand All @@ -40,7 +41,7 @@ export async function getTrade({
deps,
lifiChainMap,
}: {
input: GetEvmTradeQuoteInput & { lifiAllowedTools?: string[] | undefined }
input: GetEvmTradeQuoteInput | GetEvmTradeRateInput
deps: SwapperDeps
lifiChainMap: Map<ChainId, ChainKey>
}): Promise<Result<LifiTradeQuote[] | LifiTradeRate[], SwapErrorRight>> {
Expand All @@ -54,7 +55,6 @@ export async function getTrade({
supportsEIP1559,
affiliateBps,
potentialAffiliateBps,
lifiAllowedTools,
quoteOrRate,
} = input

Expand Down Expand Up @@ -104,9 +104,10 @@ 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 },
}),
...(quoteOrRate === 'quote' &&
(input.originalRate as LifiTradeRate).lifiTools && {
exchanges: { allow: (input.originalRate as LifiTradeRate).lifiTools },
}),
allowSwitchChain: true,
fee: affiliateBpsDecimalPercentage.isZero()
? undefined
Expand Down Expand Up @@ -147,6 +148,13 @@ export async function getTrade({
const { routes } = routesResponse.unwrap()

if (routes.length === 0) {
if (quoteOrRate === 'quote')
return Ok([
{
...input.originalRate,
quoteOrRate: 'quote',
} as LifiTradeQuote,
])
return Err(
makeSwapErrorRight({
message: 'no route found',
Expand Down Expand Up @@ -302,7 +310,7 @@ export async function getTrade({
}

export const getTradeQuote = async (
input: GetEvmTradeQuoteInputBase & { lifiAllowedTools?: string[] | undefined },
input: GetEvmTradeQuoteInputBase,
deps: SwapperDeps,
lifiChainMap: Map<ChainId, ChainKey>,
): Promise<Result<LifiTradeQuote[], SwapErrorRight>> => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { v4 as uuid } from 'uuid'

import { getDefaultSlippageDecimalPercentageForSwapper } from '../../..'
import type {
GetEvmTradeQuoteInput,
GetEvmTradeRateInput,
GetTradeRateInput,
GetUtxoTradeQuoteInput,
GetUtxoTradeRateInput,
ProtocolFee,
SwapErrorRight,
SwapperDeps,
Expand Down Expand Up @@ -216,7 +217,7 @@ export const getL1Rate = async (
const sellAdapter = deps.assertGetEvmChainAdapter(sellAsset.chainId)
const { networkFeeCryptoBaseUnit } = await getEvmTxFees({
adapter: sellAdapter,
supportsEIP1559: Boolean((input as GetEvmTradeQuoteInput).supportsEIP1559),
supportsEIP1559: Boolean((input as GetEvmTradeRateInput).supportsEIP1559),
})

const maybeRoutes = await Promise.allSettled(
Expand Down Expand Up @@ -327,7 +328,7 @@ export const getL1Rate = async (

const feeData = await (async () => {
// This is a rate without a wallet connected, so we can't get fees
if (!(input as GetUtxoTradeQuoteInput).xpub)
if (!(input as GetUtxoTradeRateInput).xpub)
return {
networkFeeCryptoBaseUnit: undefined,
protocolFees: getProtocolFees(quote),
Expand All @@ -340,7 +341,7 @@ export const getL1Rate = async (

const { vault, opReturnData, pubkey } = await getUtxoThorTxInfo({
sellAsset,
xpub: (input as GetUtxoTradeQuoteInput).xpub!,
xpub: (input as unknown as GetUtxoTradeQuoteInput).xpub!,
memo,
config: deps.config,
})
Expand Down
4 changes: 2 additions & 2 deletions packages/swapper/src/swappers/ZrxSwapper/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
EvmTransactionRequest,
GetEvmTradeQuoteInputBase,
GetEvmTradeRateInput,
GetTradeQuoteInput,
GetTradeRateInput,
GetUnsignedEvmTransactionArgs,
SwapErrorRight,
SwapperApi,
Expand All @@ -37,7 +37,7 @@ export const zrxApi: SwapperApi = {
return tradeQuoteResult.map(tradeQuote => [tradeQuote])
},
getTradeRate: async (
input: GetTradeQuoteInput,
input: GetTradeRateInput,
{ assetsById, config }: SwapperDeps,
): Promise<Result<TradeRate[], SwapErrorRight>> => {
const tradeRateResult = await getZrxTradeRate(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Asset } from '@shapeshiftoss/types'
import { KnownChainIds } from '@shapeshiftoss/types'

import type { GetTradeQuoteInput, TradeQuote } from '../../../types'
import type { GetTradeQuoteInput, TradeQuote, TradeRate } from '../../../types'
import { SwapperName } from '../../../types'
import { DEFAULT_SLIPPAGE } from '../constants'
import { FOX_MAINNET, WETH } from './assets'
Expand Down Expand Up @@ -51,6 +51,7 @@ export const setupQuote = () => {
allowMultiHop: false,
slippageTolerancePercentageDecimal: DEFAULT_SLIPPAGE,
quoteOrRate: 'quote',
originalRate: {} as TradeRate,
}

return { quoteInput, tradeQuote, buyAsset, sellAsset }
Expand Down
20 changes: 10 additions & 10 deletions packages/swapper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ type CommonTradeInputBase = {
potentialAffiliateBps: string
affiliateBps: string
allowMultiHop: boolean
lifiAllowedTools?: string[] | undefined
slippageTolerancePercentageDecimal?: string
}

Expand All @@ -166,6 +165,7 @@ export type CommonTradeQuoteInput = CommonTradeInputBase & {
receiveAddress: string
accountNumber: number
quoteOrRate: 'quote'
originalRate: TradeRate
}

type CommonTradeRateInput = CommonTradeInputBase & {
Expand All @@ -175,7 +175,7 @@ type CommonTradeRateInput = CommonTradeInputBase & {
quoteOrRate: 'rate'
}

type CommonTradeInput = CommonTradeQuoteInput | CommonTradeRateInput
type CommonTradeInput = CommonTradeQuoteInput

export type GetEvmTradeQuoteInputBase = CommonTradeQuoteInput & {
chainId: EvmChainId
Expand All @@ -185,7 +185,7 @@ export type GetEvmTradeRateInput = CommonTradeRateInput & {
chainId: EvmChainId
supportsEIP1559: false
}
export type GetEvmTradeQuoteInput = GetEvmTradeQuoteInputBase | GetEvmTradeRateInput
export type GetEvmTradeQuoteInput = GetEvmTradeQuoteInputBase

export type GetCosmosSdkTradeQuoteInputBase = CommonTradeQuoteInput & {
chainId: CosmosSdkChainId
Expand All @@ -206,16 +206,16 @@ type GetUtxoTradeQuoteWithWallet = CommonTradeQuoteInput & {
xpub: string
}

type GetUtxoTradeRateInput = CommonTradeRateInput & {
export type GetUtxoTradeRateInput = CommonTradeRateInput & {
chainId: UtxoChainId
// We need a dummy script type when getting a quote without a wallet
// so we always use SegWit (which works across all UTXO chains)
accountType: UtxoAccountType.P2pkh
accountNumber: undefined
xpub: undefined
accountType: UtxoAccountType
// accountNumber and accountType may be undefined if no wallet is connected
// accountType will default to UtxoAccountType.P2pkh without a wallet connected
accountNumber: number | undefined
xpub: string | undefined
}

export type GetUtxoTradeQuoteInput = GetUtxoTradeQuoteWithWallet | GetUtxoTradeRateInput
export type GetUtxoTradeQuoteInput = GetUtxoTradeQuoteWithWallet

export type GetTradeQuoteInput =
| GetUtxoTradeQuoteInput
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip'
import type { HDWallet } from '@shapeshiftoss/hdwallet-core'
import { supportsETH } from '@shapeshiftoss/hdwallet-core'
import type { GetTradeQuoteInput } from '@shapeshiftoss/swapper'
import type { GetTradeQuoteInput, GetTradeRateInput, TradeRate } from '@shapeshiftoss/swapper'
import type { Asset, CosmosSdkChainId, EvmChainId, UtxoChainId } from '@shapeshiftoss/types'
import { UtxoAccountType } from '@shapeshiftoss/types'
import type { TradeQuoteInputCommonArgs } from 'components/MultiHopTrade/types'
import { toBaseUnit } from 'lib/math'
import { assertUnreachable } from 'lib/utils'
import { assertGetCosmosSdkChainAdapter } from 'lib/utils/cosmosSdk'
import { assertGetEvmChainAdapter } from 'lib/utils/evm'
import { assertGetSolanaChainAdapter } from 'lib/utils/solana'
import { assertGetUtxoChainAdapter } from 'lib/utils/utxo'

export type GetTradeQuoteInputArgs = {
export type GetTradeQuoteOrRateInputArgs = {
sellAsset: Asset
buyAsset: Asset
sellAccountType: UtxoAccountType | undefined
slippageTolerancePercentageDecimal?: string
sellAmountBeforeFeesCryptoPrecision: string
allowMultiHop: boolean
lifiAllowedTools?: string[]
originalRate?: TradeRate
// 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 @@ -33,7 +32,7 @@ export type GetTradeQuoteInputArgs = {
wallet: HDWallet | undefined
}

export const getTradeQuoteInput = async ({
export const getTradeQuoteOrRateInput = async ({
sellAsset,
buyAsset,
sellAccountNumber,
Expand All @@ -43,28 +42,46 @@ export const getTradeQuoteInput = async ({
receiveAddress,
sellAmountBeforeFeesCryptoPrecision,
allowMultiHop,
lifiAllowedTools,
originalRate,
affiliateBps,
potentialAffiliateBps,
slippageTolerancePercentageDecimal,
pubKey,
}: GetTradeQuoteInputArgs): Promise<GetTradeQuoteInput> => {
const tradeQuoteInputCommonArgs: TradeQuoteInputCommonArgs = {
sellAmountIncludingProtocolFeesCryptoBaseUnit: toBaseUnit(
sellAmountBeforeFeesCryptoPrecision,
sellAsset.precision,
),
sellAsset,
buyAsset,
receiveAddress,
accountNumber: sellAccountNumber,
affiliateBps: affiliateBps ?? '0',
potentialAffiliateBps: potentialAffiliateBps ?? '0',
allowMultiHop,
lifiAllowedTools,
slippageTolerancePercentageDecimal,
quoteOrRate,
}
}: GetTradeQuoteOrRateInputArgs): Promise<GetTradeQuoteInput | GetTradeRateInput> => {
const tradeQuoteInputCommonArgs =
quoteOrRate === 'quote' && receiveAddress && sellAccountNumber !== undefined
? {
sellAmountIncludingProtocolFeesCryptoBaseUnit: toBaseUnit(
sellAmountBeforeFeesCryptoPrecision,
sellAsset.precision,
),
sellAsset,
buyAsset,
receiveAddress,
accountNumber: sellAccountNumber,
affiliateBps: affiliateBps ?? '0',
potentialAffiliateBps: potentialAffiliateBps ?? '0',
allowMultiHop,
slippageTolerancePercentageDecimal,
quoteOrRate: 'quote',
originalRate,
}
: {
sellAmountIncludingProtocolFeesCryptoBaseUnit: toBaseUnit(
sellAmountBeforeFeesCryptoPrecision,
sellAsset.precision,
),
sellAsset,
buyAsset,
receiveAddress,
originalRate,
accountNumber: sellAccountNumber,
affiliateBps: affiliateBps ?? '0',
potentialAffiliateBps: potentialAffiliateBps ?? '0',
allowMultiHop,
slippageTolerancePercentageDecimal,
quoteOrRate: 'rate',
}

const { chainNamespace } = fromChainId(sellAsset.chainId)

Expand Down
Loading

0 comments on commit 804c821

Please sign in to comment.