Skip to content

Commit

Permalink
feat: display swap source in UI to differentiate multiple quotes from…
Browse files Browse the repository at this point in the history
… same swapper
  • Loading branch information
woodenfurniture authored and 0xApotheosis committed Aug 18, 2023
1 parent 4369c8d commit e00ed08
Show file tree
Hide file tree
Showing 34 changed files with 132 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,21 @@ export const TradeConfirm = () => {
const getSellTxLink = useCallback(
(sellTxHash: string) =>
getTxLink({
name: tradeQuoteStep?.sources[0]?.name,
name: tradeQuoteStep?.source,
defaultExplorerBaseUrl: tradeQuoteStep?.sellAsset.explorerTxLink ?? '',
tradeId: sellTxHash,
}),
[tradeQuoteStep?.sellAsset.explorerTxLink, tradeQuoteStep?.sources],
[tradeQuoteStep?.sellAsset.explorerTxLink, tradeQuoteStep?.source],
)

const getBuyTxLink = useCallback(
(buyTxHash: string) =>
getTxLink({
name: lastStep?.sources[0]?.name,
name: lastStep?.source,
defaultExplorerBaseUrl: lastStep?.buyAsset.explorerTxLink ?? '',
txId: buyTxHash,
}),
[lastStep?.buyAsset.explorerTxLink, lastStep?.sources],
[lastStep?.buyAsset.explorerTxLink, lastStep?.source],
)

const txLink = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ export const TradeQuoteLoaded: React.FC<TradeQuoteLoadedProps> = ({
)

const handleQuoteSelection = useCallback(() => {
dispatch(tradeQuoteSlice.actions.setSwapperName(quoteData.swapperName))
}, [dispatch, quoteData.swapperName])
dispatch(tradeQuoteSlice.actions.setActiveQuoteIndex(quoteData.index))
}, [dispatch, quoteData.index])

const feeAsset = useAppSelector(state => selectFeeAssetByChainId(state, sellAsset.chainId ?? ''))
if (!feeAsset)
Expand Down Expand Up @@ -236,7 +236,7 @@ export const TradeQuoteLoaded: React.FC<TradeQuoteLoadedProps> = ({
<Flex justifyContent='space-between' alignItems='center'>
<Flex gap={2} alignItems='center'>
<SwapperIcon swapperName={quoteData.swapperName} />
<RawText>{quoteData.swapperName}</RawText>
<RawText>{quote?.steps[0].source ?? quoteData.swapperName}</RawText>
</Flex>
{quote && (
<Amount.Crypto
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Collapse, Flex } from '@chakra-ui/react'
import { memo, useMemo } from 'react'
import type { ApiQuote } from 'state/apis/swappers'
import { selectActiveSwapperName } from 'state/slices/tradeQuoteSlice/selectors'
import { selectActiveQuoteIndex } from 'state/slices/tradeQuoteSlice/selectors'
import { useAppSelector } from 'state/store'

import { TradeQuote } from './TradeQuote'
Expand All @@ -12,29 +12,29 @@ type TradeQuotesProps = {
}

export const TradeQuotes: React.FC<TradeQuotesProps> = memo(({ isOpen, sortedQuotes }) => {
const activeSwapperName = useAppSelector(selectActiveSwapperName)
const activeQuoteIndex = useAppSelector(selectActiveQuoteIndex)

const bestQuoteData = sortedQuotes[0]

const quotes = useMemo(
() =>
sortedQuotes.map((quoteData, i) => {
const { swapperName } = quoteData
const { index } = quoteData

// TODO(woodenfurniture): use quote ID when we want to support multiple quotes per swapper
const isActive = activeSwapperName === swapperName
const isActive = activeQuoteIndex === index

return (
<TradeQuote
isActive={isActive}
isBest={i === 0}
key={swapperName}
key={index}
quoteData={quoteData}
bestInputOutputRatio={bestQuoteData.inputOutputRatio}
/>
)
}),
[activeSwapperName, bestQuoteData, sortedQuotes],
[activeQuoteIndex, bestQuoteData, sortedQuotes],
)

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/MultiHopTrade/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getMaybeCompositeAssetSymbol } from 'lib/mixpanel/helpers'
import type { ReduxState } from 'state/reducer'
import { selectAssets, selectWillDonate } from 'state/slices/selectors'
import {
selectActiveSwapperName,
selectActiveQuoteIndex,
selectBuyAmountBeforeFeesCryptoPrecision,
selectFirstHopSellAsset,
selectLastHopBuyAsset,
Expand Down Expand Up @@ -32,7 +32,7 @@ export const getMixpanelEventData = () => {
const buyAmountBeforeFeesCryptoPrecision = selectBuyAmountBeforeFeesCryptoPrecision(state)
const sellAmountBeforeFeesCryptoPrecision = selectSellAmountBeforeFeesCryptoPrecision(state)
const willDonate = selectWillDonate(state)
const swapperName = selectActiveSwapperName(state)
const swapperName = selectActiveQuoteIndex(state)

const compositeBuyAsset = getMaybeCompositeAssetSymbol(buyAsset.assetId, assets)
const compositeSellAsset = getMaybeCompositeAssetSymbol(sellAsset.assetId, assets)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,15 @@ export const useGetTradeQuotes = () => {
if (isEqualExceptAffiliateBpsAndSlippage(tradeQuoteInput, updatedTradeQuoteInput)) {
return
} else {
dispatch(tradeQuoteSlice.actions.resetSwapperName())
dispatch(tradeQuoteSlice.actions.resetActiveQuoteIndex())
}
}
})()
} else {
// if the quote input args changed, reset the selected swapper and update the trade quote args
if (tradeQuoteInput !== skipToken) {
setTradeQuoteInput(skipToken)
dispatch(tradeQuoteSlice.actions.resetSwapperName())
dispatch(tradeQuoteSlice.actions.resetActiveQuoteIndex())
}
}
}, [
Expand Down
2 changes: 1 addition & 1 deletion src/lib/getTxLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { SwapSource } from 'lib/swapper/api'
import { SwapperName } from 'lib/swapper/api'

type GetBaseUrl = {
name: SwapSource['name'] | Dex | undefined
name: SwapSource | Dex | undefined
defaultExplorerBaseUrl: string
isOrder?: boolean
}
Expand Down
7 changes: 2 additions & 5 deletions src/lib/swapper/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export type TradeBase<C extends ChainId> = {
sellAmountIncludingProtocolFeesCryptoBaseUnit: string
feeData: QuoteFeeData<C>
rate: string
sources: SwapSource[]
source: SwapSource
buyAsset: Asset
sellAsset: Asset
accountNumber: number
Expand All @@ -137,10 +137,7 @@ export type TradeQuote<C extends ChainId = ChainId> = {
rate: string // top-level rate for all steps (i.e. output amount / input amount)
}

export type SwapSource = {
name: SwapperName | string
proportion: string
}
export type SwapSource = SwapperName | `${SwapperName} • ${string}`

export enum SwapperName {
Thorchain = 'THORChain',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const expectedTradeQuoteWethToFox: TradeQuote<KnownChainIds.EthereumMainnet> = {
},
sellAmountIncludingProtocolFeesCryptoBaseUnit: '1000000000000000000',
buyAmountBeforeFeesCryptoBaseUnit: '14913256100953839475750', // 14913 FOX
sources: [{ name: SwapperName.CowSwap, proportion: '1' }],
source: SwapperName.CowSwap,
buyAsset: FOX_MAINNET,
sellAsset: WETH,
accountNumber: 0,
Expand All @@ -137,7 +137,7 @@ const expectedTradeQuoteFoxToEth: TradeQuote<KnownChainIds.EthereumMainnet> = {
},
sellAmountIncludingProtocolFeesCryptoBaseUnit: '1000000000000000000000',
buyAmountBeforeFeesCryptoBaseUnit: '51242479117266593',
sources: [{ name: SwapperName.CowSwap, proportion: '1' }],
source: SwapperName.CowSwap,
buyAsset: ETH,
sellAsset: FOX_MAINNET,
accountNumber: 0,
Expand All @@ -164,7 +164,7 @@ const expectedTradeQuoteUsdcToXdai: TradeQuote<KnownChainIds.GnosisMainnet> = {
},
sellAmountIncludingProtocolFeesCryptoBaseUnit: '20000000',
buyAmountBeforeFeesCryptoBaseUnit: '21006555357465608755',
sources: [{ name: SwapperName.CowSwap, proportion: '1' }],
source: SwapperName.CowSwap,
buyAsset: XDAI,
sellAsset: USDC_GNOSIS,
accountNumber: 0,
Expand All @@ -191,7 +191,7 @@ const expectedTradeQuoteSmallAmountWethToFox: TradeQuote<KnownChainIds.EthereumM
},
sellAmountIncludingProtocolFeesCryptoBaseUnit: '1000000000000',
buyAmountBeforeFeesCryptoBaseUnit: '165590332317788059940', // 165.59 FOX
sources: [{ name: SwapperName.CowSwap, proportion: '1' }],
source: SwapperName.CowSwap,
buyAsset: FOX_MAINNET,
sellAsset: WETH,
accountNumber: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import type { AxiosError } from 'axios'
import { getConfig } from 'config'
import { bn } from 'lib/bignumber/bignumber'
import type { GetTradeQuoteInput, SwapErrorRight, TradeQuote } from 'lib/swapper/api'
import { SwapperName } from 'lib/swapper/api'
import type { CowChainId, CowSwapQuoteResponse } from 'lib/swapper/swappers/CowSwapper/types'
import {
COW_SWAP_NATIVE_ASSET_MARKER_ADDRESS,
COW_SWAP_VAULT_RELAYER_ADDRESS,
DEFAULT_APP_DATA,
DEFAULT_SOURCE,
ORDER_KIND_SELL,
} from 'lib/swapper/swappers/CowSwapper/utils/constants'
import { cowService } from 'lib/swapper/swappers/CowSwapper/utils/cowService'
Expand Down Expand Up @@ -115,7 +115,7 @@ export async function getCowSwapTradeQuote(
},
sellAmountIncludingProtocolFeesCryptoBaseUnit: normalizedSellAmountCryptoBaseUnit,
buyAmountBeforeFeesCryptoBaseUnit,
sources: DEFAULT_SOURCE,
source: SwapperName.CowSwap,
buyAsset,
sellAsset,
accountNumber,
Expand Down
2 changes: 0 additions & 2 deletions src/lib/swapper/swappers/CowSwapper/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AddressZero } from '@ethersproject/constants'
import { KnownChainIds } from '@shapeshiftoss/types'
import { SwapperName } from 'lib/swapper/api'

import type { CowChainId } from '../types'

Expand All @@ -9,7 +8,6 @@ export const MIN_COWSWAP_USD_TRADE_VALUES_BY_CHAIN_ID: Record<CowChainId, string
[KnownChainIds.GnosisMainnet]: '0.01',
}

export const DEFAULT_SOURCE = [{ name: SwapperName.CowSwap, proportion: '1' }]
export const DEFAULT_ADDRESS = AddressZero
export const DEFAULT_APP_DATA = '0x68a7b5781dfe48bd5d7aeb11261c17517f5c587da682e4fade9b6a00a59b8970'
export const COW_SWAP_VAULT_RELAYER_ADDRESS = '0xc92e8bdf79f0507f65a392b0ab4667716bfe0110'
Expand Down
18 changes: 13 additions & 5 deletions src/lib/swapper/swappers/LifiSwapper/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ import { createDefaultStatusResponse } from 'lib/utils/evm'

import { getTradeQuote } from './getTradeQuote/getTradeQuote'
import { getLifiChainMap } from './utils/getLifiChainMap'
import { getLifiToolsMap } from './utils/getLifiToolsMap'
import { getUnsignedTx } from './utils/getUnsignedTx/getUnsignedTx'
import type { LifiTool } from './utils/types'

const tradeQuoteMetadata: Map<string, Route> = new Map()

let lifiChainMapPromise: Promise<Result<Map<ChainId, ChainKey>, SwapErrorRight>> | undefined
// cached metadata - would need persistent cache with expiry if moved server-side
let lifiChainMapPromise: Promise<Map<ChainId, ChainKey>> | undefined
let lifiToolsMapPromise: Promise<Map<string, LifiTool>> | undefined

export const lifiApi: Swapper2Api = {
getTradeQuote: async (
Expand All @@ -38,15 +42,19 @@ export const lifiApi: Swapper2Api = {
}),
)
}
if (lifiChainMapPromise === undefined) lifiChainMapPromise = getLifiChainMap()

const maybeLifiChainMap = await lifiChainMapPromise
if (lifiChainMapPromise === undefined) lifiChainMapPromise = getLifiChainMap()
if (lifiToolsMapPromise === undefined) lifiToolsMapPromise = getLifiToolsMap()

if (maybeLifiChainMap.isErr()) return Err(maybeLifiChainMap.unwrapErr())
const [lifiChainMap, lifiToolsMap] = await Promise.all([
lifiChainMapPromise,
lifiToolsMapPromise,
])

const tradeQuoteResult = await getTradeQuote(
input as GetEvmTradeQuoteInput,
maybeLifiChainMap.unwrap(),
lifiChainMap,
lifiToolsMap,
assets,
)
const { receiveAddress } = input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ import { Err, Ok } from '@sniptt/monads'
import { getDefaultSlippagePercentageForSwapper } from 'constants/constants'
import type { Asset } from 'lib/asset-service'
import { bn, bnOrZero, convertPrecision } from 'lib/bignumber/bignumber'
import type { GetEvmTradeQuoteInput, SwapErrorRight } from 'lib/swapper/api'
import type { GetEvmTradeQuoteInput, SwapErrorRight, SwapSource } from 'lib/swapper/api'
import { makeSwapErrorRight, SwapErrorType, SwapperName } from 'lib/swapper/api'
import { LIFI_INTEGRATOR_ID } from 'lib/swapper/swappers/LifiSwapper/utils/constants'
import { getIntermediaryTransactionOutputs } from 'lib/swapper/swappers/LifiSwapper/utils/getIntermediaryTransactionOutputs/getIntermediaryTransactionOutputs'
import { getLifi } from 'lib/swapper/swappers/LifiSwapper/utils/getLifi'
import { getLifiEvmAssetAddress } from 'lib/swapper/swappers/LifiSwapper/utils/getLifiEvmAssetAddress/getLifiEvmAssetAddress'
import { transformLifiStepFeeData } from 'lib/swapper/swappers/LifiSwapper/utils/transformLifiFeeData/transformLifiFeeData'
import type { LifiTradeQuote } from 'lib/swapper/swappers/LifiSwapper/utils/types'
import type { LifiTool, LifiTradeQuote } from 'lib/swapper/swappers/LifiSwapper/utils/types'

import { getNetworkFeeCryptoBaseUnit } from '../utils/getNetworkFeeCryptoBaseUnit/getNetworkFeeCryptoBaseUnit'

export async function getTradeQuote(
input: GetEvmTradeQuoteInput & { wallet?: HDWallet },
lifiChainMap: Map<ChainId, ChainKey>,
lifiToolsMap: Map<string, LifiTool>,
assets: Partial<Record<AssetId, Asset>>,
): Promise<Result<LifiTradeQuote[], SwapErrorRight>> {
const {
Expand Down Expand Up @@ -164,6 +165,11 @@ export async function getTradeQuote(
wallet,
})

const toolInfo = lifiToolsMap.get(lifiStep.tool)
const source: SwapSource = toolInfo
? `${SwapperName.LIFI}${toolInfo.name}`
: SwapperName.LIFI

return {
allowanceContract: lifiStep.estimate.approvalAddress,
accountNumber,
Expand All @@ -179,12 +185,7 @@ export async function getTradeQuote(
rate: estimateRate,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
sellAsset,
sources: [
{
name: `${selectedLifiRoute.steps[0].tool} (${SwapperName.LIFI})`,
proportion: '1',
},
],
source,
}
}),
)
Expand Down
23 changes: 3 additions & 20 deletions src/lib/swapper/swappers/LifiSwapper/utils/getLifiChainMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,21 @@ import type { ChainId as LifiChainId, ChainKey } from '@lifi/sdk'
import type { ChainId } from '@shapeshiftoss/caip'
import { fromChainId } from '@shapeshiftoss/caip'
import { evmChainIds } from '@shapeshiftoss/chain-adapters'
import type { Result } from '@sniptt/monads/build'
import { Err, Ok } from '@sniptt/monads/build'
import type { SwapErrorRight } from 'lib/swapper/api'
import { makeSwapErrorRight } from 'lib/swapper/api'

import { createLifiChainMap } from './createLifiChainMap/createLifiChainMap'
import { getLifi } from './getLifi'

export const getLifiChainMap = async (): Promise<
Result<Map<ChainId, ChainKey>, SwapErrorRight>
> => {
export const getLifiChainMap = async (): Promise<Map<ChainId, ChainKey>> => {
const supportedChainRefs = evmChainIds.map(
chainId => Number(fromChainId(chainId).chainReference) as LifiChainId,
)

// getMixPanel()?.track(MixPanelEvents.SwapperApiRequest, {
// swapper: SwapperName.LIFI,
// method: 'get',
// // Note, this may change if the Li.Fi SDK changes
// url: 'https://li.quest/v1/chains',
// })
const { chains } = await getLifi().getPossibilities({
include: ['chains'],
chains: supportedChainRefs,
})

if (chains === undefined)
return Err(
makeSwapErrorRight({
message: '[getLifiChainMap] no chains available',
}),
)
if (chains === undefined) throw Error('no chains available')

return Ok(createLifiChainMap(chains))
return createLifiChainMap(chains)
}
20 changes: 20 additions & 0 deletions src/lib/swapper/swappers/LifiSwapper/utils/getLifiToolsMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ToolsResponse } from '@lifi/types'

import { getLifi } from './getLifi'
import type { LifiTool } from './types'

export const getLifiToolsMap = async (): Promise<Map<string, LifiTool>> => {
const toolsResponse: ToolsResponse = await getLifi().getTools()

const result: Map<string, LifiTool> = new Map()

for (const { key, name, logoURI } of toolsResponse.exchanges) {
result.set(key, { key, name, logoURI })
}

for (const { key, name, logoURI } of toolsResponse.bridges) {
result.set(key, { key, name, logoURI })
}

return result
}
6 changes: 6 additions & 0 deletions src/lib/swapper/swappers/LifiSwapper/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ import type { TradeQuote } from 'lib/swapper/api'
export interface LifiTradeQuote extends TradeQuote<EvmChainId> {
selectedLifiRoute?: Route
}

export type LifiTool = {
key: string
name: string
logoURI: string
}
Loading

0 comments on commit e00ed08

Please sign in to comment.