Commit 27e20d72 authored by Jack Short's avatar Jack Short Committed by GitHub

feat: pay with any token routing (#5959)

* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth
parent 95eafbab
......@@ -41,6 +41,7 @@ export enum ActivityType {
Send = 'SEND',
Stake = 'STAKE',
Swap = 'SWAP',
Swapx = 'SWAPX',
Staking = 'Staking',
Unknown = 'UNKNOWN',
Unstake = 'UNSTAKE',
......@@ -587,6 +588,7 @@ export type Portfolio = {
export type PortfolioAssetActivitiesArgs = {
includeOffChain?: InputMaybe<Scalars['Boolean']>;
page?: InputMaybe<Scalars['Int']>;
pageSize?: InputMaybe<Scalars['Int']>;
};
......@@ -1048,7 +1050,7 @@ export type NftRouteQueryVariables = Exact<{
}>;
export type NftRouteQuery = { __typename?: 'Query', nftRoute?: { __typename?: 'NftRouteResponse', calldata: string, toAddress: string, route?: Array<{ __typename?: 'NftTrade', amount: number, contractAddress: string, id: string, marketplace: NftMarketplace, tokenId: string, tokenType: NftStandard, price: { __typename?: 'TokenAmount', currency: Currency, value: string }, quotePrice?: { __typename?: 'TokenAmount', currency: Currency, value: string } }>, sendAmount: { __typename?: 'TokenAmount', currency: Currency, value: string } } };
export type NftRouteQuery = { __typename?: 'Query', nftRoute?: { __typename?: 'NftRouteResponse', id: string, calldata: string, toAddress: string, route?: Array<{ __typename?: 'NftTrade', amount: number, contractAddress: string, id: string, marketplace: NftMarketplace, tokenId: string, tokenType: NftStandard, price: { __typename?: 'TokenAmount', id: string, currency: Currency, value: string }, quotePrice?: { __typename?: 'TokenAmount', id: string, currency: Currency, value: string } }>, sendAmount: { __typename?: 'TokenAmount', id: string, currency: Currency, value: string } } };
export const RecentlySearchedAssetsDocument = gql`
......@@ -1989,6 +1991,7 @@ export const NftRouteDocument = gql`
nftTrades: $nftTrades
tokenTrades: $tokenTrades
) {
id
calldata
route {
amount
......@@ -1996,10 +1999,12 @@ export const NftRouteDocument = gql`
id
marketplace
price {
id
currency
value
}
quotePrice {
id
currency
value
}
......@@ -2007,6 +2012,7 @@ export const NftRouteDocument = gql`
tokenType
}
sendAmount {
id
currency
value
}
......
import gql from 'graphql-tag'
import { NftTradeInput, TokenTradeInput, useNftRouteQuery } from '../__generated__/types-and-hooks'
gql`
query NftRoute(
$chain: Chain = ETHEREUM
......@@ -10,6 +8,7 @@ gql`
$tokenTrades: [TokenTradeInput!]
) {
nftRoute(chain: $chain, senderAddress: $senderAddress, nftTrades: $nftTrades, tokenTrades: $tokenTrades) {
id
calldata
route {
amount
......@@ -17,10 +16,12 @@ gql`
id
marketplace
price {
id
currency
value
}
quotePrice {
id
currency
value
}
......@@ -28,6 +29,7 @@ gql`
tokenType
}
sendAmount {
id
currency
value
}
......@@ -35,13 +37,3 @@ gql`
}
}
`
export function useNftRoute(senderAddress: string, nftTrades: NftTradeInput[], tokenTrades?: TokenTradeInput[]) {
return useNftRouteQuery({
variables: {
senderAddress,
nftTrades,
tokenTrades,
},
})
}
......@@ -27,7 +27,7 @@ interface AllowanceRequired {
approveAndPermit: () => Promise<void>
}
type Allowance =
export type Allowance =
| { state: AllowanceState.LOADING }
| {
state: AllowanceState.ALLOWED
......
......@@ -4,7 +4,7 @@ import { NFTEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { GqlRoutingVariant, useGqlRoutingFlag } from 'featureFlags/flags/gqlRouting'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { useNftRoute } from 'graphql/data/nft/Routing'
import { useNftRouteLazyQuery } from 'graphql/data/__generated__/types-and-hooks'
import { useIsNftDetailsPage, useIsNftPage, useIsNftProfilePage } from 'hooks/useIsNftPage'
import { BagFooter } from 'nft/components/bag/BagFooter'
import ListingModal from 'nft/components/bag/profile/ListingModal'
......@@ -21,14 +21,17 @@ import {
useSendTransaction,
useTransactionResponse,
} from 'nft/hooks'
import { useTokenInput } from 'nft/hooks/useTokenInput'
import { fetchRoute } from 'nft/queries'
import { BagItemStatus, BagStatus, ProfilePageStateType, RouteResponse, TxStateType } from 'nft/types'
import {
buildNftTradeInputFromBagItems,
buildSellObject,
formatAssetEventProperties,
recalculateBagUsingPooledAssets,
sortUpdatedAssets,
} from 'nft/utils'
import { buildRouteResponse } from 'nft/utils/nftRoute'
import { combineBuyItemsWithTxRoute } from 'nft/utils/txRoute/combineItemsWithTxRoute'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useQueryClient } from 'react-query'
......@@ -50,7 +53,7 @@ interface SeparatorProps {
show?: boolean
}
const BagContainer = styled.div<{ raiseZIndex: boolean }>`
const BagContainer = styled.div<{ raiseZIndex: boolean; isProfilePage: boolean }>`
position: fixed;
display: flex;
flex-direction: column;
......@@ -62,7 +65,8 @@ const BagContainer = styled.div<{ raiseZIndex: boolean }>`
border: 1px solid ${({ theme }) => theme.backgroundOutline};
border-radius: 16px;
box-shadow: ${({ theme }) => theme.shallowShadow};
z-index: ${({ raiseZIndex }) => (raiseZIndex ? Z_INDEX.modalOverTooltip : 3)};
z-index: ${({ raiseZIndex, isProfilePage }) =>
raiseZIndex ? (isProfilePage ? Z_INDEX.modalOverTooltip : Z_INDEX.modalBackdrop) : 3};
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
right: 0px;
......@@ -143,6 +147,7 @@ const Bag = () => {
const setTransactionState = useSendTransaction((state) => state.setState)
const transactionStateRef = useRef(transactionState)
const [setTransactionResponse] = useTransactionResponse((state) => [state.setTransactionResponse])
const tokenTradeInput = useTokenInput((state) => state.tokenTradeInput)
const queryClient = useQueryClient()
......@@ -197,7 +202,8 @@ const Bag = () => {
setBagExpanded({ bagExpanded: false, manualClose: true })
}, [setBagExpanded])
useNftRoute(usingGqlRouting ? account ?? '' : '', [])
const [fetchGqlRoute] = useNftRouteLazyQuery()
const fetchAssets = async () => {
const itemsToBuy = itemsInBag.filter((item) => item.status !== BagItemStatus.UNAVAILABLE).map((item) => item.asset)
const ethSellObject = buildSellObject(
......@@ -210,7 +216,72 @@ const Bag = () => {
!bagIsLocked && setLocked(true)
setBagStatus(BagStatus.FETCHING_ROUTE)
try {
const data = await queryClient.fetchQuery(['assetsRoute', ethSellObject, itemsToBuy, account], () =>
if (usingGqlRouting) {
fetchGqlRoute({
variables: {
senderAddress: usingGqlRouting && account ? account : '',
nftTrades: usingGqlRouting ? buildNftTradeInputFromBagItems(itemsInBag) : [],
tokenTrades: tokenTradeInput ? tokenTradeInput : undefined,
},
onCompleted: (data) => {
if (!data.nftRoute || !data.nftRoute.route) {
setBagStatus(BagStatus.ADDING_TO_BAG)
setLocked(false)
return
}
const { route, routeResponse } = buildRouteResponse(data.nftRoute, !!tokenTradeInput)
const updatedAssets = combineBuyItemsWithTxRoute(itemsToBuy, route)
const fetchedPriceChangedAssets = updatedAssets
.filter((asset) => asset.updatedPriceInfo)
.sort(sortUpdatedAssets)
const fetchedUnavailableAssets = updatedAssets.filter((asset) => asset.isUnavailable)
const fetchedUnchangedAssets = updatedAssets.filter(
(asset) => !asset.updatedPriceInfo && !asset.isUnavailable
)
const hasReviewedAssets = fetchedUnchangedAssets.length > 0
const hasAssetsInReview = fetchedPriceChangedAssets.length > 0
const hasUnavailableAssets = fetchedUnavailableAssets.length > 0
const hasAssets = hasReviewedAssets || hasAssetsInReview || hasUnavailableAssets
const shouldReview = hasAssetsInReview || hasUnavailableAssets
setItemsInBag([
...fetchedUnavailableAssets.map((unavailableAsset) => ({
asset: unavailableAsset,
status: BagItemStatus.UNAVAILABLE,
})),
...fetchedPriceChangedAssets.map((changedAsset) => ({
asset: changedAsset,
status: BagItemStatus.REVIEWING_PRICE_CHANGE,
})),
...fetchedUnchangedAssets.map((unchangedAsset) => ({
asset: unchangedAsset,
status: BagItemStatus.REVIEWED,
})),
])
let shouldLock = false
if (hasAssets) {
if (!shouldReview) {
purchaseAssets(routeResponse)
setBagStatus(BagStatus.CONFIRMING_IN_WALLET)
shouldLock = true
} else if (!hasAssetsInReview) setBagStatus(BagStatus.CONFIRM_REVIEW)
else {
setBagStatus(BagStatus.IN_REVIEW)
}
} else {
setBagStatus(BagStatus.ADDING_TO_BAG)
}
setLocked(shouldLock)
},
})
} else {
const routeData = await queryClient.fetchQuery(['assetsRoute', ethSellObject, itemsToBuy, account], () =>
fetchRoute({
toSell: [ethSellObject],
toBuy: itemsToBuy,
......@@ -218,9 +289,11 @@ const Bag = () => {
})
)
const updatedAssets = combineBuyItemsWithTxRoute(itemsToBuy, data.route)
const updatedAssets = combineBuyItemsWithTxRoute(itemsToBuy, routeData.route)
const fetchedPriceChangedAssets = updatedAssets.filter((asset) => asset.updatedPriceInfo).sort(sortUpdatedAssets)
const fetchedPriceChangedAssets = updatedAssets
.filter((asset) => asset.updatedPriceInfo)
.sort(sortUpdatedAssets)
const fetchedUnavailableAssets = updatedAssets.filter((asset) => asset.isUnavailable)
const fetchedUnchangedAssets = updatedAssets.filter((asset) => !asset.updatedPriceInfo && !asset.isUnavailable)
const hasReviewedAssets = fetchedUnchangedAssets.length > 0
......@@ -238,13 +311,16 @@ const Bag = () => {
asset: changedAsset,
status: BagItemStatus.REVIEWING_PRICE_CHANGE,
})),
...fetchedUnchangedAssets.map((unchangedAsset) => ({ asset: unchangedAsset, status: BagItemStatus.REVIEWED })),
...fetchedUnchangedAssets.map((unchangedAsset) => ({
asset: unchangedAsset,
status: BagItemStatus.REVIEWED,
})),
])
setLocked(false)
if (hasAssets) {
if (!shouldReview) {
purchaseAssets(data)
purchaseAssets(routeData)
setBagStatus(BagStatus.CONFIRMING_IN_WALLET)
} else if (!hasAssetsInReview) setBagStatus(BagStatus.CONFIRM_REVIEW)
else {
......@@ -253,6 +329,7 @@ const Bag = () => {
} else {
setBagStatus(BagStatus.ADDING_TO_BAG)
}
}
} catch (error) {
setBagStatus(BagStatus.ADDING_TO_BAG)
}
......@@ -313,7 +390,7 @@ const Bag = () => {
return (
<Portal>
<BagContainer data-testid="nft-bag" raiseZIndex={isMobile || isModalOpen}>
<BagContainer data-testid="nft-bag" raiseZIndex={isMobile || isModalOpen} isProfilePage={isProfilePage}>
{!(isProfilePage && profilePageState === ProfilePageStateType.LISTING) ? (
<>
<BagHeader
......
This diff is collapsed.
import { style } from '@vanilla-extract/css'
import { Z_INDEX } from 'theme/zIndex'
import { sprinkles } from '../../css/sprinkles.css'
......@@ -11,10 +12,10 @@ export const overlay = style([
position: 'fixed',
display: 'block',
background: 'black',
zIndex: 'modalBackdrop',
}),
{
opacity: 0.72,
overflow: 'hidden',
zIndex: Z_INDEX.modalBackdrop - 1,
},
])
import { Currency, CurrencyAmount, NativeCurrency, Percent, Token, TradeType } from '@uniswap/sdk-core'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade'
import { useMemo } from 'react'
import { InterfaceTrade, TradeState } from 'state/routing/types'
export default function useDerivedPayWithAnyTokenSwapInfo(
inputCurrency?: Currency,
parsedOutputAmount?: CurrencyAmount<NativeCurrency | Token>
): {
state: TradeState
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
maximumAmountIn: CurrencyAmount<Token> | undefined
allowedSlippage: Percent
} {
const { state, trade } = useBestTrade(TradeType.EXACT_OUTPUT, parsedOutputAmount, inputCurrency ?? undefined)
const allowedSlippage = useAutoSlippageTolerance(trade)
const maximumAmountIn = useMemo(() => {
const maximumAmountIn = trade?.maximumAmountIn(allowedSlippage)
return maximumAmountIn?.currency.isToken ? (maximumAmountIn as CurrencyAmount<Token>) : undefined
}, [allowedSlippage, trade])
return useMemo(() => {
return {
state,
trade,
maximumAmountIn,
allowedSlippage,
}
}, [allowedSlippage, maximumAmountIn, state, trade])
}
import { Currency, CurrencyAmount, NativeCurrency, Token, TradeType } from '@uniswap/sdk-core'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade'
import { useMemo } from 'react'
import { InterfaceTrade, TradeState } from 'state/routing/types'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { PermitInput, TokenTradeRoutesInput, TokenTradeType } from 'graphql/data/__generated__/types-and-hooks'
import { Allowance } from 'hooks/usePermit2Allowance'
import { buildAllTradeRouteInputs } from 'nft/utils/tokenRoutes'
import { useEffect } from 'react'
import { InterfaceTrade } from 'state/routing/types'
import { useTokenInput } from './useTokenInput'
export default function usePayWithAnyTokenSwap(
inputCurrency?: Currency,
parsedOutputAmount?: CurrencyAmount<NativeCurrency | Token>
): {
state: TradeState
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
maximumAmountIn: CurrencyAmount<Token> | undefined
} {
const { state, trade } = useBestTrade(TradeType.EXACT_OUTPUT, parsedOutputAmount, inputCurrency ?? undefined)
const allowedSlippage = useAutoSlippageTolerance(trade)
const maximumAmountIn = useMemo(() => {
const maximumAmountIn = trade?.maximumAmountIn(allowedSlippage)
return maximumAmountIn?.currency.isToken ? (maximumAmountIn as CurrencyAmount<Token>) : undefined
}, [allowedSlippage, trade])
return useMemo(() => {
return {
state,
trade,
maximumAmountIn,
trade?: InterfaceTrade<Currency, Currency, TradeType> | undefined,
allowance?: Allowance,
allowedSlippage?: Percent
) {
const setTokenTradeInput = useTokenInput((state) => state.setTokenTradeInput)
const hasRoutes = !!trade && trade.routes
const hasInputAmount = !!trade && !!trade.inputAmount && trade.inputAmount.currency.isToken
const hasAllowance = !!allowedSlippage && !!allowance
useEffect(() => {
if (!hasRoutes || !hasInputAmount || !hasAllowance) {
setTokenTradeInput(undefined)
return
}
}, [maximumAmountIn, state, trade])
const slippage = parseInt(allowedSlippage.multiply(100).toSignificant(2))
const { mixedTokenTradeRouteInputs, v2TokenTradeRouteInputs, v3TokenTradeRouteInputs } =
buildAllTradeRouteInputs(trade)
const routes: TokenTradeRoutesInput = {
mixedRoutes: mixedTokenTradeRouteInputs,
tradeType: TokenTradeType.ExactOutput,
v2Routes: v2TokenTradeRouteInputs,
v3Routes: v3TokenTradeRouteInputs,
}
const permitInput: PermitInput | undefined =
'permitSignature' in allowance && allowance.permitSignature
? {
details: {
amount: allowance.permitSignature.details.amount.toString(),
expiration: allowance.permitSignature.details.expiration.toString(),
nonce: allowance.permitSignature.details.nonce.toString(),
token: allowance.permitSignature.details.token,
},
sigDeadline: allowance.permitSignature.sigDeadline.toString(),
signature: allowance.permitSignature.signature,
spender: allowance.permitSignature.spender,
}
: undefined
setTokenTradeInput({
permit: permitInput,
routes,
slippageToleranceBasisPoints: slippage,
tokenAmount: {
amount: trade.inputAmount.quotient.toString(),
token: {
address: trade.inputAmount.currency.address,
chainId: trade.inputAmount.currency.chainId,
decimals: trade.inputAmount.currency.decimals,
isNative: trade.inputAmount.currency.isNative,
},
},
})
}, [allowance, allowedSlippage, hasAllowance, hasInputAmount, hasRoutes, setTokenTradeInput, trade])
}
......@@ -38,7 +38,7 @@ export const useSendTransaction = create<TxState>()(
try {
const txNoGasLimit = {
to: transactionData.to,
value: BigNumber.from(transactionData.valueToSend),
value: transactionData.valueToSend ? BigNumber.from(transactionData.valueToSend) : undefined,
data: transactionData.data,
}
......
import { Currency } from '@uniswap/sdk-core'
import { TokenTradeInput } from 'graphql/data/__generated__/types-and-hooks'
import create from 'zustand'
import { devtools } from 'zustand/middleware'
......@@ -6,14 +7,18 @@ interface TokenInputState {
inputCurrency: Currency | undefined
setInputCurrency: (currency: Currency | undefined) => void
clearInputCurrency: () => void
tokenTradeInput: TokenTradeInput | undefined
setTokenTradeInput: (tokenTradeInput: TokenTradeInput | undefined) => void
}
export const useTokenInput = create<TokenInputState>()(
devtools(
(set) => ({
inputCurrency: undefined,
tokenTradeInput: undefined,
setInputCurrency: (currency) => set(() => ({ inputCurrency: currency })),
clearInputCurrency: () => set(() => ({ inputCurrency: undefined })),
setTokenTradeInput: (tokenTradeInput) => set(() => ({ tokenTradeInput })),
}),
{ name: 'useTokenInput' }
)
......
......@@ -30,7 +30,7 @@ export type SellItem = {
export type BuyItem = {
id?: string
symbol?: string
name: string
name?: string
decimals: number
address: string
priceInfo: PriceInfo
......@@ -52,7 +52,7 @@ export type RoutingItem = {
}
export interface RouteResponse {
valueToSend: string
valueToSend?: string
route: RoutingItem[]
data: any
to: any
......
import { NftMarketplace, NftTradeInput, TokenAmountInput } from 'graphql/data/__generated__/types-and-hooks'
import { BagItem, BagItemStatus, UpdatedGenieAsset } from 'nft/types'
export const buildSellObject = (amount: string) => {
return {
address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
......@@ -14,3 +17,38 @@ export const buildSellObject = (amount: string) => {
tokenType: 'ERC20',
}
}
export const buildNftTradeInputFromBagItems = (itemsInBag: BagItem[]): NftTradeInput[] => {
const assetsToBuy = itemsInBag.filter((item) => item.status !== BagItemStatus.UNAVAILABLE).map((item) => item.asset)
return buildNftTradeInput(assetsToBuy)
}
const buildNftTradeInput = (assets: UpdatedGenieAsset[]): NftTradeInput[] => {
return assets.flatMap((asset) => {
const { id, address, marketplace, priceInfo, tokenId, tokenType } = asset
if (!id || !marketplace || !tokenType) return []
const ethAmountInput: TokenAmountInput = {
amount: priceInfo.ETHPrice,
token: {
address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
chainId: 1,
decimals: 18,
isNative: true,
},
}
return [
{
amount: 1,
contractAddress: address,
id,
marketplace: marketplace.toUpperCase() as NftMarketplace,
quotePrice: ethAmountInput,
tokenId,
tokenType,
},
]
})
}
import { NftRouteResponse, NftTrade } from 'graphql/data/__generated__/types-and-hooks'
import { Markets, RouteResponse, RoutingActions, RoutingItem, TokenType } from 'nft/types'
function buildRoutingItem(routingItem: NftTrade): RoutingItem {
return {
action: RoutingActions.Buy,
marketplace: routingItem.marketplace.toLowerCase(),
amountIn: routingItem.price.value,
assetIn: {
ETHPrice: routingItem.price.value,
baseAsset: routingItem.price.currency,
basePrice: routingItem.price.value,
baseDecimals: '18',
},
amountOut: routingItem.amount.toString(),
assetOut: {
id: routingItem.id,
decimals: 18,
address: routingItem.contractAddress,
priceInfo: {
ETHPrice: routingItem.price.value,
baseAsset: routingItem.price.currency,
basePrice: routingItem.price.value,
baseDecimals: '18',
},
tokenType: routingItem.tokenType as unknown as TokenType,
tokenId: routingItem.tokenId,
amount: routingItem.amount.toString(),
marketplace: routingItem.marketplace.toLowerCase() as Markets,
orderSource: 'api',
},
}
}
function buildRoutingItems(routingItems: NftTrade[]): RoutingItem[] {
return routingItems.map(buildRoutingItem)
}
export function buildRouteResponse(
routeResponse: NftRouteResponse,
useErc20Token: boolean
): { route: RoutingItem[]; routeResponse: RouteResponse } {
const route = routeResponse.route ? buildRoutingItems(routeResponse.route) : []
return {
route,
routeResponse: {
route,
valueToSend: useErc20Token ? undefined : routeResponse.sendAmount.value,
data: routeResponse.calldata,
to: routeResponse.toAddress,
},
}
}
import { IRoute, Protocol } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { Pool } from '@uniswap/v3-sdk'
import { TokenAmountInput, TokenTradeRouteInput, TradePoolInput } from 'graphql/data/__generated__/types-and-hooks'
import { InterfaceTrade } from 'state/routing/types'
interface SwapAmounts {
inputAmount: CurrencyAmount<Currency>
outputAmount: CurrencyAmount<Currency>
}
interface TradeTokenInputAmounts {
inputAmount: TokenAmountInput
outputAmount: TokenAmountInput
}
interface Swap {
route: IRoute<Currency, Currency, Pair | Pool>
inputAmount: CurrencyAmount<Currency>
outputAmount: CurrencyAmount<Currency>
}
function buildTradeRouteInputAmounts(swapAmounts: SwapAmounts): TradeTokenInputAmounts {
return {
inputAmount: {
amount: swapAmounts.inputAmount.quotient.toString(),
token: {
address: swapAmounts.inputAmount.currency.isToken
? swapAmounts.inputAmount.currency.address
: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
chainId: swapAmounts.inputAmount.currency.chainId,
decimals: swapAmounts.inputAmount.currency.decimals,
isNative: swapAmounts.inputAmount.currency.isNative,
},
},
outputAmount: {
amount: swapAmounts.outputAmount.quotient.toString(),
token: {
address: swapAmounts.outputAmount.currency.isToken
? swapAmounts.outputAmount.currency.address
: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
chainId: swapAmounts.outputAmount.currency.chainId,
decimals: swapAmounts.outputAmount.currency.decimals,
isNative: swapAmounts.outputAmount.currency.isNative,
},
},
}
}
function buildPool(pool: Pair | Pool): TradePoolInput {
const isPool = 'fee' in pool
return {
pair: !isPool
? {
tokenAmountA: {
amount: pool.reserve0.quotient.toString(),
token: {
address: pool.token0.address,
chainId: pool.token0.chainId,
decimals: pool.token0.decimals,
isNative: pool.token0.isNative,
},
},
tokenAmountB: {
amount: pool.reserve1.quotient.toString(),
token: {
address: pool.token1.address,
chainId: pool.token1.chainId,
decimals: pool.token1.decimals,
isNative: pool.token1.isNative,
},
},
}
: undefined,
pool: isPool
? {
fee: pool.fee,
liquidity: pool.liquidity.toString(),
sqrtRatioX96: pool.sqrtRatioX96.toString(),
tickCurrent: pool.tickCurrent.toString(),
tokenA: {
address: pool.token0.address,
chainId: pool.token0.chainId,
decimals: pool.token0.decimals,
isNative: pool.token0.isNative,
},
tokenB: {
address: pool.token1.address,
chainId: pool.token1.chainId,
decimals: pool.token1.decimals,
isNative: pool.token1.isNative,
},
}
: undefined,
}
}
function buildPools(pools: (Pair | Pool)[]): TradePoolInput[] {
return pools.map((pool) => buildPool(pool))
}
function buildTradeRouteInput(swap: Swap): TokenTradeRouteInput {
return {
...buildTradeRouteInputAmounts({ inputAmount: swap.inputAmount, outputAmount: swap.outputAmount }),
pools: buildPools(swap.route.pools),
}
}
export function buildAllTradeRouteInputs(trade: InterfaceTrade<Currency, Currency, TradeType>): {
mixedTokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
v2TokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
v3TokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
} {
const mixedTokenTradeRouteInputs: TokenTradeRouteInput[] = []
const v2TokenTradeRouteInputs: TokenTradeRouteInput[] = []
const v3TokenTradeRouteInputs: TokenTradeRouteInput[] = []
const swaps = trade.swaps
for (const swap of swaps) {
if (swap.route.protocol === Protocol.MIXED) {
mixedTokenTradeRouteInputs.push(buildTradeRouteInput(swap))
} else if (swap.route.protocol === Protocol.V2) {
v2TokenTradeRouteInputs.push(buildTradeRouteInput(swap))
} else {
v3TokenTradeRouteInputs.push(buildTradeRouteInput(swap))
}
}
return {
mixedTokenTradeRouteInputs: mixedTokenTradeRouteInputs.length > 0 ? mixedTokenTradeRouteInputs : undefined,
v2TokenTradeRouteInputs: v2TokenTradeRouteInputs.length > 0 ? v2TokenTradeRouteInputs : undefined,
v3TokenTradeRouteInputs: v3TokenTradeRouteInputs.length > 0 ? v3TokenTradeRouteInputs : undefined,
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment