show v3 quotes

This commit is contained in:
Moody Salem 2021-04-26 19:35:27 -05:00
parent bb17c57a84
commit ccbd5dfcf7
No known key found for this signature in database
GPG Key ID: 8CB5CD10385138DB
9 changed files with 86 additions and 74 deletions

@ -1,5 +1,6 @@
import { TradeType } from '@uniswap/sdk-core' import { TradeType } from '@uniswap/sdk-core'
import { Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import styled, { ThemeContext } from 'styled-components' import styled, { ThemeContext } from 'styled-components'
import { Field } from '../../state/swap/actions' import { Field } from '../../state/swap/actions'
@ -12,17 +13,7 @@ import { RowBetween, RowFixed } from '../Row'
import FormattedPriceImpact from './FormattedPriceImpact' import FormattedPriceImpact from './FormattedPriceImpact'
import SwapRoute from './SwapRoute' import SwapRoute from './SwapRoute'
const InfoLink = styled(ExternalLink)` function TradeSummary({ trade, allowedSlippage }: { trade: V2Trade | V3Trade; allowedSlippage: number }) {
width: 100%;
border: 1px solid ${({ theme }) => theme.bg3};
padding: 6px 6px;
border-radius: 8px;
text-align: center;
font-size: 14px;
color: ${({ theme }) => theme.text1};
`
function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippage: number }) {
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade) const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade)
const isExactIn = trade.tradeType === TradeType.EXACT_INPUT const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
@ -75,7 +66,7 @@ function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippag
} }
export interface AdvancedSwapDetailsProps { export interface AdvancedSwapDetailsProps {
trade?: Trade trade?: V2Trade | V3Trade
} }
export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) { export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
@ -83,7 +74,10 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
const [allowedSlippage] = useUserSlippageTolerance() const [allowedSlippage] = useUserSlippageTolerance()
const showRoute = Boolean(trade && trade.route.path.length > 2) const showRoute = Boolean(
(trade && trade instanceof V2Trade && trade.route.pairs.length > 2) ||
(trade instanceof V3Trade && trade.route.pools.length > 2)
)
return ( return (
<AutoColumn gap="0px"> <AutoColumn gap="0px">
@ -103,16 +97,6 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
</RowBetween> </RowBetween>
</> </>
)} )}
{!showRoute && (
<AutoColumn style={{ padding: '12px 16px 0 16px' }}>
<InfoLink
href={'https://info.uniswap.org/pair/' + trade.route.pairs[0].liquidityToken.address}
target="_blank"
>
View pair analytics
</InfoLink>
</AutoColumn>
)}
</> </>
)} )}
</AutoColumn> </AutoColumn>

@ -1,5 +1,6 @@
import { currencyEquals } from '@uniswap/sdk-core' import { currencyEquals } from '@uniswap/sdk-core'
import { Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useCallback, useMemo } from 'react' import React, { useCallback, useMemo } from 'react'
import TransactionConfirmationModal, { import TransactionConfirmationModal, {
ConfirmationModalContent, ConfirmationModalContent,
@ -10,10 +11,10 @@ import SwapModalHeader from './SwapModalHeader'
/** /**
* Returns true if the trade requires a confirmation of details before we can submit it * Returns true if the trade requires a confirmation of details before we can submit it
* @param tradeA trade A * @param args either a pair of V2 trades or a pair of V3 trades
* @param tradeB trade B
*/ */
function tradeMeaningfullyDiffers(tradeA: Trade, tradeB: Trade): boolean { function tradeMeaningfullyDiffers(...args: [V2Trade, V2Trade] | [V3Trade, V3Trade]): boolean {
const [tradeA, tradeB] = args
return ( return (
tradeA.tradeType !== tradeB.tradeType || tradeA.tradeType !== tradeB.tradeType ||
!currencyEquals(tradeA.inputAmount.currency, tradeB.inputAmount.currency) || !currencyEquals(tradeA.inputAmount.currency, tradeB.inputAmount.currency) ||
@ -37,8 +38,8 @@ export default function ConfirmSwapModal({
txHash, txHash,
}: { }: {
isOpen: boolean isOpen: boolean
trade: Trade | undefined trade: V2Trade | V3Trade | undefined
originalTrade: Trade | undefined originalTrade: V2Trade | V3Trade | undefined
attemptingTxn: boolean attemptingTxn: boolean
txHash: string | undefined txHash: string | undefined
recipient: string | null recipient: string | null
@ -49,7 +50,15 @@ export default function ConfirmSwapModal({
onDismiss: () => void onDismiss: () => void
}) { }) {
const showAcceptChanges = useMemo( const showAcceptChanges = useMemo(
() => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)), () =>
Boolean(
(trade instanceof V2Trade &&
originalTrade instanceof V2Trade &&
tradeMeaningfullyDiffers(trade, originalTrade)) ||
(trade instanceof V3Trade &&
originalTrade instanceof V3Trade &&
tradeMeaningfullyDiffers(trade, originalTrade))
),
[originalTrade, trade] [originalTrade, trade]
) )

@ -1,4 +1,5 @@
import { Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { TradeType } from '@uniswap/sdk-core' import { TradeType } from '@uniswap/sdk-core'
import React, { useContext, useMemo, useState } from 'react' import React, { useContext, useMemo, useState } from 'react'
import { Repeat } from 'react-feather' import { Repeat } from 'react-feather'
@ -26,7 +27,7 @@ export default function SwapModalFooter({
swapErrorMessage, swapErrorMessage,
disabledConfirm, disabledConfirm,
}: { }: {
trade: Trade trade: V2Trade | V3Trade
allowedSlippage: number allowedSlippage: number
onConfirm: () => void onConfirm: () => void
swapErrorMessage: string | undefined swapErrorMessage: string | undefined

@ -1,5 +1,6 @@
import { TradeType } from '@uniswap/sdk-core' import { TradeType } from '@uniswap/sdk-core'
import { Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useContext, useMemo } from 'react' import React, { useContext, useMemo } from 'react'
import { ArrowDown, AlertTriangle } from 'react-feather' import { ArrowDown, AlertTriangle } from 'react-feather'
import { Text } from 'rebass' import { Text } from 'rebass'
@ -21,7 +22,7 @@ export default function SwapModalHeader({
showAcceptChanges, showAcceptChanges,
onAcceptChanges, onAcceptChanges,
}: { }: {
trade: Trade trade: V2Trade | V3Trade
allowedSlippage: number allowedSlippage: number
recipient: string | null recipient: string | null
showAcceptChanges: boolean showAcceptChanges: boolean

@ -1,4 +1,5 @@
import { Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { Fragment, memo, useContext } from 'react' import React, { Fragment, memo, useContext } from 'react'
import { ChevronRight } from 'react-feather' import { ChevronRight } from 'react-feather'
import { Flex } from 'rebass' import { Flex } from 'rebass'
@ -6,11 +7,12 @@ import { ThemeContext } from 'styled-components'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { unwrappedToken } from 'utils/wrappedCurrency' import { unwrappedToken } from 'utils/wrappedCurrency'
export default memo(function SwapRoute({ trade }: { trade: Trade }) { export default memo(function SwapRoute({ trade }: { trade: V2Trade | V3Trade }) {
const tokenPath = trade instanceof V2Trade ? trade.route.path : trade.route.tokenPath
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
return ( return (
<Flex flexWrap="wrap" width="100%" justifyContent="flex-end" alignItems="center"> <Flex flexWrap="wrap" width="100%" justifyContent="flex-end" alignItems="center">
{trade.route.path.map((token, i, path) => { {tokenPath.map((token, i, path) => {
const isLastItem: boolean = i === path.length - 1 const isLastItem: boolean = i === path.length - 1
const currency = unwrappedToken(token) const currency = unwrappedToken(token)
return ( return (

@ -1,5 +1,7 @@
import JSBI from 'jsbi'
import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { JSBI, Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { ArrowDown } from 'react-feather' import { ArrowDown } from 'react-feather'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
@ -22,6 +24,7 @@ import ProgressSteps from '../../components/ProgressSteps'
import SwapHeader from '../../components/swap/SwapHeader' import SwapHeader from '../../components/swap/SwapHeader'
import { INITIAL_ALLOWED_SLIPPAGE } from '../../constants' import { INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
import useToggledVersion, { Version } from '../../hooks/useToggledVersion'
import { getTradeVersion } from '../../utils/getTradeVersion' import { getTradeVersion } from '../../utils/getTradeVersion'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useCurrency, useAllTokens } from '../../hooks/Tokens' import { useCurrency, useAllTokens } from '../../hooks/Tokens'
@ -90,6 +93,7 @@ export default function Swap({ history }: RouteComponentProps) {
const { independentField, typedValue, recipient } = useSwapState() const { independentField, typedValue, recipient } = useSwapState()
const { const {
v2Trade, v2Trade,
v3Trade,
currencyBalances, currencyBalances,
parsedAmount, parsedAmount,
currencies, currencies,
@ -104,13 +108,13 @@ export default function Swap({ history }: RouteComponentProps) {
) )
const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE
const { address: recipientAddress } = useENSAddress(recipient) const { address: recipientAddress } = useENSAddress(recipient)
// const toggledVersion = useToggledVersion() const toggledVersion = useToggledVersion()
const trade = showWrap const trade = showWrap
? undefined ? undefined
: v2Trade /*{ : {
[Version.v2]: v2Trade, [Version.v2]: v2Trade,
[Version.v3]: v3Trade, [Version.v3]: v3Trade,
}[toggledVersion]*/ }[toggledVersion]
const parsedAmounts = showWrap const parsedAmounts = showWrap
? { ? {
@ -148,7 +152,7 @@ export default function Swap({ history }: RouteComponentProps) {
// modal and loading // modal and loading
const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{ const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{
showConfirm: boolean showConfirm: boolean
tradeToConfirm: Trade | undefined tradeToConfirm: V2Trade | V3Trade | undefined
attemptingTxn: boolean attemptingTxn: boolean
swapErrorMessage: string | undefined swapErrorMessage: string | undefined
txHash: string | undefined txHash: string | undefined
@ -190,7 +194,11 @@ export default function Swap({ history }: RouteComponentProps) {
const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput)) const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput))
// the callback to execute the swap // the callback to execute the swap
const { callback: swapCallback, error: swapCallbackError } = useV2SwapCallback(trade, allowedSlippage, recipient) const { callback: swapCallback, error: swapCallbackError } = useV2SwapCallback(
trade instanceof V2Trade ? trade : undefined,
allowedSlippage,
recipient
)
const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade) const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade)

@ -89,8 +89,8 @@ export default createReducer(initialState, (builder) =>
calls.forEach((call) => { calls.forEach((call) => {
const callKey = toCallKey(call) const callKey = toCallKey(call)
const current = state.callResults[chainId][callKey] const current = state.callResults[chainId][callKey]
if (!current) return // only should be dispatched if we are already fetching if (!current || typeof current.fetchingBlockNumber !== 'number') return // only should be dispatched if we are already fetching
if (current.fetchingBlockNumber === fetchingBlockNumber) { if (current.fetchingBlockNumber <= fetchingBlockNumber) {
delete current.fetchingBlockNumber delete current.fetchingBlockNumber
current.data = null current.data = null
current.blockNumber = fetchingBlockNumber current.blockNumber = fetchingBlockNumber

@ -30,7 +30,7 @@ async function fetchChunk(
results: { success: boolean; returnData: string }[] results: { success: boolean; returnData: string }[]
blockNumber: number blockNumber: number
}> { }> {
console.debug('Fetching chunk', multicall2Contract, chunk, minBlockNumber) console.debug('Fetching chunk', chunk, minBlockNumber)
let resultsBlockNumber: number let resultsBlockNumber: number
let results: { success: boolean; returnData: string }[] let results: { success: boolean; returnData: string }[]
try { try {
@ -41,7 +41,7 @@ async function fetchChunk(
resultsBlockNumber = blockNumber.toNumber() resultsBlockNumber = blockNumber.toNumber()
results = returnData results = returnData
} catch (error) { } catch (error) {
console.debug('Failed to fetch chunk inside retry', error) console.debug('Failed to fetch chunk', error)
throw error throw error
} }
if (resultsBlockNumber < minBlockNumber) { if (resultsBlockNumber < minBlockNumber) {
@ -205,12 +205,12 @@ export default function Updater(): null {
// dispatch any errored calls // dispatch any errored calls
if (erroredCalls.length > 0) { if (erroredCalls.length > 0) {
console.debug('Errored calls', erroredCalls) console.debug('Calls errored in fetch', erroredCalls)
dispatch( dispatch(
errorFetchingMulticallResults({ errorFetchingMulticallResults({
calls: erroredCalls, calls: erroredCalls,
chainId, chainId,
fetchingBlockNumber: latestBlockNumber, fetchingBlockNumber: fetchBlockNumber,
}) })
) )
} }

@ -14,36 +14,43 @@ const INPUT_FRACTION_AFTER_FEE = ONE_HUNDRED_PERCENT.subtract(BASE_FEE)
// computes price breakdown for the trade // computes price breakdown for the trade
export function computeTradePriceBreakdown( export function computeTradePriceBreakdown(
trade?: V2Trade | null trade?: V2Trade | V3Trade | null
): { priceImpactWithoutFee: Percent | undefined; realizedLPFee: CurrencyAmount | undefined | null } { ): { priceImpactWithoutFee: Percent | undefined; realizedLPFee: CurrencyAmount | undefined | null } {
// for each hop in our trade, take away the x*y=k price impact from 0.3% fees if (trade instanceof V2Trade) {
// e.g. for 3 tokens/2 hops: 1 - ((1 - .03) * (1-.03)) // for each hop in our trade, take away the x*y=k price impact from 0.3% fees
const realizedLPFee = !trade // e.g. for 3 tokens/2 hops: 1 - ((1 - .03) * (1-.03))
? undefined const realizedLPFee = !trade
: ONE_HUNDRED_PERCENT.subtract( ? undefined
trade.route.pairs.reduce<Fraction>( : ONE_HUNDRED_PERCENT.subtract(
(currentFee: Fraction): Fraction => currentFee.multiply(INPUT_FRACTION_AFTER_FEE), trade.route.pairs.reduce<Fraction>(
ONE_HUNDRED_PERCENT (currentFee: Fraction): Fraction => currentFee.multiply(INPUT_FRACTION_AFTER_FEE),
ONE_HUNDRED_PERCENT
)
) )
)
// remove lp fees from price impact // remove lp fees from price impact
const priceImpactWithoutFeeFraction = trade && realizedLPFee ? trade.priceImpact.subtract(realizedLPFee) : undefined const priceImpactWithoutFeeFraction = trade && realizedLPFee ? trade.priceImpact.subtract(realizedLPFee) : undefined
// the x*y=k impact // the x*y=k impact
const priceImpactWithoutFeePercent = priceImpactWithoutFeeFraction const priceImpactWithoutFeePercent = priceImpactWithoutFeeFraction
? new Percent(priceImpactWithoutFeeFraction?.numerator, priceImpactWithoutFeeFraction?.denominator) ? new Percent(priceImpactWithoutFeeFraction?.numerator, priceImpactWithoutFeeFraction?.denominator)
: undefined : undefined
// the amount of the input that accrues to LPs // the amount of the input that accrues to LPs
const realizedLPFeeAmount = const realizedLPFeeAmount =
realizedLPFee && realizedLPFee &&
trade && trade &&
(trade.inputAmount instanceof TokenAmount (trade.inputAmount instanceof TokenAmount
? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient) ? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
: CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient)) : CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient))
return { priceImpactWithoutFee: priceImpactWithoutFeePercent, realizedLPFee: realizedLPFeeAmount } return { priceImpactWithoutFee: priceImpactWithoutFeePercent, realizedLPFee: realizedLPFeeAmount }
} else {
return {
priceImpactWithoutFee: undefined,
realizedLPFee: undefined,
}
}
} }
// computes the minimum amount out and maximum amount in for a trade given a user specified allowed slippage in bips // computes the minimum amount out and maximum amount in for a trade given a user specified allowed slippage in bips