Commit 9b2fe0bd authored by Moody Salem's avatar Moody Salem Committed by GitHub

Use best trade to get the best route (#731)

parent cb00e0ba
import React, { useState, useReducer, useCallback, useEffect } from 'react'
import React, { useState, useCallback, useEffect } from 'react'
import { parseEther, parseUnits } from '@ethersproject/units'
import { Fraction, JSBI, Percent, TokenAmount, TradeType, WETH } from '@uniswap/sdk'
import { ArrowDown, ChevronDown, ChevronUp, Repeat } from 'react-feather'
import { withRouter } from 'react-router-dom'
import { parseUnits, parseEther } from '@ethersproject/units'
import { BigNumber } from '@ethersproject/bignumber'
import { Zero, MaxUint256 } from '@ethersproject/constants'
import { Contract } from '@ethersproject/contracts'
import { WETH, TradeType, Pair, Trade, TokenAmount, JSBI, Percent, Fraction } from '@uniswap/sdk'
import { Field, initializeSwapState, reducer, SwapAction } from './swap-store'
import { Field, SwapAction, useSwapStateReducer } from './swap-store'
import { Text } from 'rebass'
import Card, { BlueCard, GreyCard, YellowCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { ROUTER_ADDRESS } from '../../constants'
import { useAddressAllowance } from '../../contexts/Allowances'
import { useUserAdvanced } from '../../contexts/Application'
import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
import { useDarkModeManager, useLocalStorageTokens } from '../../contexts/LocalStorage'
import { usePair } from '../../contexts/Pairs'
import { useAllTokens, useToken } from '../../contexts/Tokens'
import { usePendingApproval, useTransactionAdder } from '../../contexts/Transactions'
import { useTokenContract, useWeb3React } from '../../hooks'
import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades'
import { Hover, theme, TYPE } from '../../theme'
import { Link } from '../../theme/components'
import { calculateGasMargin, getEtherscanLink, getProviderOrSigner, getRouterContract, isWETH } from '../../utils'
import Copy from '../AccountDetails/Copy'
import AddressInputPanel from '../AddressInputPanel'
import { ButtonError, ButtonLight, ButtonPrimary } from '../Button'
import ConfirmationModal from '../ConfirmationModal'
import CurrencyInputPanel from '../CurrencyInputPanel'
import QuestionHelper from '../Question'
import SlippageTabs from '../SlippageTabs'
import TokenLogo from '../TokenLogo'
import {
AdvancedDropwdown,
ArrowWrapper,
......@@ -22,36 +47,8 @@ import {
TruncatedText,
Wrapper
} from './styleds'
import Copy from '../AccountDetails/Copy'
import TokenLogo from '../TokenLogo'
import SlippageTabs from '../SlippageTabs'
import QuestionHelper from '../Question'
import AddressInputPanel from '../AddressInputPanel'
import ConfirmationModal from '../ConfirmationModal'
import CurrencyInputPanel from '../CurrencyInputPanel'
// import BalanceCard from '../BalanceCard'
import { Link } from '../../theme/components'
import { Text } from 'rebass'
import { theme, TYPE, Hover } from '../../theme'
import { AutoColumn, ColumnCenter } from '../../components/Column'
import { RowBetween, RowFixed, AutoRow } from '../../components/Row'
import { ArrowDown, ChevronDown, ChevronUp, Repeat } from 'react-feather'
import { ButtonPrimary, ButtonError, ButtonLight } from '../Button'
import Card, { GreyCard, BlueCard, YellowCard } from '../../components/Card'
import { usePair } from '../../contexts/Pairs'
import { useRoute } from '../../contexts/Routes'
import { useAddressAllowance } from '../../contexts/Allowances'
import { useToken, useAllTokens } from '../../contexts/Tokens'
import { useWeb3React, useTokenContract } from '../../hooks'
import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
import { useUserAdvanced } from '../../contexts/Application'
import { ROUTER_ADDRESS } from '../../constants'
import { getRouterContract, calculateGasMargin, getProviderOrSigner, getEtherscanLink, isWETH } from '../../utils'
import { useLocalStorageTokens } from '../../contexts/LocalStorage'
import { useDarkModeManager } from '../../contexts/LocalStorage'
// import BalanceCard from '../BalanceCard'
function hex(value: JSBI) {
return BigNumber.from(value.toString())
......@@ -79,7 +76,6 @@ const ALLOWED_SLIPPAGE_HIGH = 500
function ExchangePage({ sendingInput = false, history, params }) {
// text translation
// const { t } = useTranslation()
const { chainId, account, library } = useWeb3React()
// adding notifications on txns
......@@ -92,33 +88,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
const [ENS, setENS] = useState<string>('')
// trade details, check query params for initial state
const [state, dispatch] = useReducer(
reducer,
{
independentField: params.outputTokenAddress && !params.inputTokenAddress ? Field.OUTPUT : Field.INPUT,
inputTokenAddress: params.inputTokenAddress ? params.inputTokenAddress : WETH[chainId].address,
outputTokenAddress: params.outputTokenAddress ? params.outputTokenAddress : '',
typedValue:
params.inputTokenAddress && !params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
: ''
: !params.inputTokenAddress && params.outputTokenAddress
? params.outputTokenAmount
? params.outputTokenAmount
: ''
: params.inputTokenAddress && params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
: ''
: ''
? ''
: ''
? ''
: ''
},
initializeSwapState
)
const [state, dispatch] = useSwapStateReducer(params)
const { independentField, typedValue, ...fieldData } = state
const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
......@@ -161,13 +131,6 @@ function ExchangePage({ sendingInput = false, history, params }) {
// check on pending approvals for token amounts
const pendingApprovalInput = usePendingApproval(tokens[Field.INPUT]?.address)
// entities for swap
const pair: Pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
const route = useRoute(tokens[Field.INPUT], tokens[Field.OUTPUT])
// check for invalid selection
const noRoute: boolean = !route && !!tokens[Field.INPUT] && !!tokens[Field.OUTPUT]
// modal and loading
const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [showAdvanced, setShowAdvanced] = useState<boolean>(false)
......@@ -202,14 +165,16 @@ function ExchangePage({ sendingInput = false, history, params }) {
}
}
// get trade
let trade: Trade
try {
trade =
!!route && !!parsedAmounts[independentField]
? new Trade(route, parsedAmounts[independentField], tradeType)
: undefined
} catch (error) {}
const pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
let bestTradeExactIn = useTradeExactIn(tradeType === TradeType.EXACT_INPUT ? parsedAmounts[independentField] : null, tokens[Field.OUTPUT])
let bestTradeExactOut = useTradeExactOut(tokens[Field.INPUT], tradeType === TradeType.EXACT_OUTPUT ? parsedAmounts[independentField] : null)
const trade = tradeType === TradeType.EXACT_INPUT ? bestTradeExactIn : bestTradeExactOut
const route = trade?.route
const userHasSpecifiedInputOutput = !!parsedAmounts[independentField] &&
parsedAmounts[independentField].greaterThan(JSBI.BigInt(0)) &&
!!tokens[Field.INPUT] && !!tokens[Field.OUTPUT]
const noRoute = !route
const slippageFromTrade: Percent = trade && trade.slippage
......@@ -241,18 +206,18 @@ function ExchangePage({ sendingInput = false, history, params }) {
type: SwapAction.SELECT_TOKEN,
payload: { field, address }
})
}, [])
}, [dispatch])
const onSwapTokens = useCallback(() => {
dispatch({
type: SwapAction.SWITCH_TOKENS,
payload: undefined
})
}, [])
}, [dispatch])
const onUserInput = useCallback((field: Field, typedValue: string) => {
dispatch({ type: SwapAction.TYPE, payload: { field, typedValue } })
}, [])
}, [dispatch])
const onMaxInput = useCallback((typedValue: string) => {
dispatch({
......@@ -262,7 +227,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
typedValue
}
})
}, [])
}, [dispatch])
const onMaxOutput = useCallback((typedValue: string) => {
dispatch({
......@@ -272,7 +237,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
typedValue
}
})
}, [])
}, [dispatch])
// reset field if sending with with swap is cancled
useEffect(() => {
......@@ -295,7 +260,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
? userBalances[Field.INPUT].subtract(MIN_ETHER)
: userBalances[Field.INPUT]
: undefined
} catch {}
} catch {
}
const atMaxAmountInput: boolean =
!!maxAmountInput && !!parsedAmounts[Field.INPUT]
......@@ -340,7 +306,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
return null
}
const slippageAdjustedAmounts: { [field: number]: TokenAmount } = {
const slippageAdjustedAmounts: { [field in Field]: TokenAmount } = {
[Field.INPUT]:
Field.INPUT === independentField
? parsedAmounts[Field.INPUT]
......@@ -582,7 +548,6 @@ function ExchangePage({ sendingInput = false, history, params }) {
if (
parsedAmounts[Field.INPUT] &&
pair &&
route &&
JSBI.greaterThan(parsedAmounts[Field.INPUT].raw, route.pairs[0].reserveOf(tokens[Field.INPUT]).raw)
) {
......@@ -593,7 +558,6 @@ function ExchangePage({ sendingInput = false, history, params }) {
if (
!ignoreOutput &&
parsedAmounts[Field.OUTPUT] &&
pair &&
route &&
JSBI.greaterThan(
parsedAmounts[Field.OUTPUT].raw,
......@@ -631,11 +595,10 @@ function ExchangePage({ sendingInput = false, history, params }) {
dependentField,
ignoreOutput,
independentField,
pair,
parsedAmounts,
recipientError,
route,
tokens,
route,
trade,
userBalances
])
......@@ -669,7 +632,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
<Text fontSize={36} fontWeight={500}>
{parsedAmounts[Field.INPUT]?.toSignificant(6)} {tokens[Field.INPUT]?.symbol}
</Text>
<TokenLogo address={tokens[Field.INPUT]?.address} size={'30px'} />
<TokenLogo address={tokens[Field.INPUT]?.address} size={'30px'}/>
</RowBetween>
<TYPE.darkGray fontSize={20}>To</TYPE.darkGray>
{ENS ? (
......@@ -681,7 +644,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
{recipient?.slice(0, 8)}...{recipient?.slice(34, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient} />
<Copy toCopy={recipient}/>
</AutoRow>
</AutoColumn>
) : (
......@@ -691,7 +654,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
{recipient?.slice(0, 6)}...{recipient?.slice(36, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient} />
<Copy toCopy={recipient}/>
</AutoRow>
)}
</AutoColumn>
......@@ -703,7 +666,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
<AutoColumn gap="lg" style={{ marginTop: '40px' }}>
<AutoColumn gap="sm">
<AutoRow gap="10px">
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'30px'} />
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'30px'}/>
<Text fontSize={36} fontWeight={500}>
{slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} {tokens[Field.OUTPUT]?.symbol}
</Text>
......@@ -731,14 +694,14 @@ function ExchangePage({ sendingInput = false, history, params }) {
{/* {!!slippageAdjustedAmounts[Field.INPUT] && slippageAdjustedAmounts[Field.INPUT].toSignificant(6)} */}
</TruncatedText>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.INPUT]?.address} size={'24px'} />
<TokenLogo address={tokens[Field.INPUT]?.address} size={'24px'}/>
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.INPUT]?.symbol || ''}
</Text>
</RowFixed>
</RowBetween>
<RowFixed>
<ArrowDown size="16" color={theme(isDark).text2} />
<ArrowDown size="16" color={theme(isDark).text2}/>
</RowFixed>
<RowBetween align="flex-end">
<TruncatedText fontSize={24} fontWeight={500} color={warningHigh ? theme(isDark).red1 : ''}>
......@@ -747,7 +710,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
{/* {!!slippageAdjustedAmounts[Field.OUTPUT] && slippageAdjustedAmounts[Field.OUTPUT].toSignificant(6)} */}
</TruncatedText>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'24px'} />
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'24px'}/>
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.OUTPUT]?.symbol || ''}
</Text>
......@@ -805,19 +768,19 @@ function ExchangePage({ sendingInput = false, history, params }) {
color={theme(isDark).text2}
style={{ justifyContent: 'center', alignItems: 'center', display: 'flex' }}
>
{pair && showInverted
? route.midPrice.invert().toSignificant(6) +
{trade && showInverted
? (trade?.executionPrice?.invert()?.toSignificant(6) ?? '') +
' ' +
tokens[Field.INPUT]?.symbol +
' / ' +
tokens[Field.OUTPUT]?.symbol
: route.midPrice.toSignificant(6) +
: (trade?.executionPrice?.toSignificant(6) ?? '') +
' ' +
tokens[Field.OUTPUT]?.symbol +
' / ' +
tokens[Field.INPUT]?.symbol}
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
<Repeat size={14} />
<Repeat size={14}/>
</StyledBalanceMaxMini>
</Text>
</RowBetween>
......@@ -827,7 +790,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
<TYPE.black fontSize={14} fontWeight={400}>
{independentField === Field.INPUT ? (sending ? 'Min sent' : 'Minimum received') : 'Maximum sold'}
</TYPE.black>
<QuestionHelper text="A boundary is set so you are protected from large price movements after you submit your trade." />
<QuestionHelper
text="A boundary is set so you are protected from large price movements after you submit your trade."/>
</RowFixed>
<RowFixed>
<TYPE.black fontSize={14}>
......@@ -857,7 +821,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
<TYPE.black color={theme(isDark).text1} fontSize={14} fontWeight={400}>
Price impact
</TYPE.black>
<QuestionHelper text="The difference between the market price and your price due to trade size." />
<QuestionHelper text="The difference between the market price and your price due to trade size."/>
</RowFixed>
<ErrorText
fontWeight={500}
......@@ -878,7 +842,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
<TYPE.black fontSize={14} fontWeight={400}>
Liquidity Provider Fee
</TYPE.black>
<QuestionHelper text="A portion of each trade (0.3%) goes to liquidity providers to incentivize liquidity on the protocol." />
<QuestionHelper
text="A portion of each trade (0.3%) goes to liquidity providers to incentivize liquidity on the protocol."/>
</RowFixed>
<TYPE.black fontSize={14}>
{feeTimesInputFormatted
......@@ -900,12 +865,12 @@ function ExchangePage({ sendingInput = false, history, params }) {
}
}
const PriceBar = function() {
const PriceBar = function () {
return (
<AutoRow justify="space-between">
<AutoColumn justify="center">
<Text fontWeight={500} fontSize={16} color={theme(isDark).text2}>
{pair ? `${route.midPrice.toSignificant(6)} ` : '-'}
{trade ? `${trade.executionPrice.toSignificant(6)} ` : '-'}
</Text>
<Text fontWeight={500} fontSize={16} color={theme(isDark).text3} pt={1}>
{tokens[Field.OUTPUT]?.symbol} / {tokens[Field.INPUT]?.symbol}
......@@ -913,7 +878,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
</AutoColumn>
<AutoColumn justify="center">
<Text fontWeight={500} fontSize={16} color={theme(isDark).text2}>
{pair ? `${route.midPrice.invert().toSignificant(6)} ` : '-'}
{trade ? `${trade.executionPrice.invert().toSignificant(6)} ` : '-'}
</Text>
<Text fontWeight={500} fontSize={16} color={theme(isDark).text3} pt={1}>
{tokens[Field.INPUT]?.symbol} / {tokens[Field.OUTPUT]?.symbol}
......@@ -1002,7 +967,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
Max
</MaxButton>
)}
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)} />
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)}/>
<CurrencyInputPanel
field={Field.INPUT}
value={formattedAmounts[Field.INPUT]}
......@@ -1048,7 +1013,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
<ColumnCenter>
<RowBetween padding="0 12px">
<ArrowWrapper onClick={onSwapTokens}>
<ArrowDown size="16" color={theme(isDark).text2} onClick={onSwapTokens} />
<ArrowDown size="16" color={theme(isDark).text2} onClick={onSwapTokens}/>
</ArrowWrapper>
<StyledBalanceMaxMini onClick={() => setSendingWithSwap(false)} style={{ marginRight: '0px' }}>
<TYPE.blue>Remove Swap</TYPE.blue>
......@@ -1086,7 +1051,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
/>
{sendingWithSwap && (
<RowBetween padding="0 12px">
<ArrowDown size="16" />
<ArrowDown size="16"/>
</RowBetween>
)}
</>
......@@ -1118,7 +1083,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
{!noRoute && tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
<Card padding={advanced ? '.25rem 1.25rem 0 .75rem' : '.25rem .7rem .25rem 1.25rem'} borderRadius={'20px'}>
{advanced ? (
<PriceBar />
<PriceBar/>
) : (
<AutoColumn gap="4px">
{' '}
......@@ -1132,23 +1097,23 @@ function ExchangePage({ sendingInput = false, history, params }) {
color={theme(isDark).text2}
style={{ justifyContent: 'center', alignItems: 'center', display: 'flex' }}
>
{pair && showInverted
? route.midPrice.invert().toSignificant(6) +
{trade && showInverted
? (trade?.executionPrice?.invert()?.toSignificant(6) ?? '') +
' ' +
tokens[Field.INPUT]?.symbol +
' per ' +
tokens[Field.OUTPUT]?.symbol
: route.midPrice.toSignificant(6) +
: (trade?.executionPrice?.toSignificant(6) ?? '') +
' ' +
tokens[Field.OUTPUT]?.symbol +
' per ' +
tokens[Field.INPUT]?.symbol}
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
<Repeat size={14} />
<Repeat size={14}/>
</StyledBalanceMaxMini>
</Text>
</RowBetween>
{pair && (warningHigh || warningMedium) && (
{trade && (warningHigh || warningMedium) && (
<RowBetween>
<TYPE.main
style={{ justifyContent: 'center', alignItems: 'center', display: 'flex' }}
......@@ -1164,7 +1129,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
: priceSlippage.toFixed(4) + '%'
: '-'}{' '}
</ErrorText>
<QuestionHelper text="The difference between the market price and your quoted price due to trade size." />
<QuestionHelper
text="The difference between the market price and your quoted price due to trade size."/>
</RowFixed>
</RowBetween>
)}
......@@ -1174,7 +1140,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
)}
</AutoColumn>
<BottomGrouping>
{noRoute ? (
{noRoute && userHasSpecifiedInputOutput ? (
<GreyCard style={{ textAlign: 'center' }}>
<TYPE.main>No path found.</TYPE.main>
......@@ -1240,7 +1206,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
<Text fontSize={16} fontWeight={500} style={{ userSelect: 'none' }}>
Show Advanced
</Text>
<ChevronDown color={theme(isDark).text2} />
<ChevronDown color={theme(isDark).text2}/>
</RowBetween>
</Hover>
)}
......@@ -1251,10 +1217,10 @@ function ExchangePage({ sendingInput = false, history, params }) {
<Text fontSize={16} color={theme(isDark).text2} fontWeight={500} style={{ userSelect: 'none' }}>
Hide Advanced
</Text>
<ChevronUp color={theme(isDark).text2} />
<ChevronUp color={theme(isDark).text2}/>
</RowBetween>
</Hover>
<SectionBreak />
<SectionBreak/>
<AutoColumn style={{ padding: '0 20px' }}>
<RowBetween>
<RowFixed>
......@@ -1307,7 +1273,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
<TYPE.black fontSize={14} fontWeight={400} color={theme(isDark).text1}>
Price Impact
</TYPE.black>
<QuestionHelper text="The difference between the market price and your quoted price due to trade size." />
<QuestionHelper
text="The difference between the market price and your quoted price due to trade size."/>
</RowFixed>
<ErrorText
fontWeight={500}
......@@ -1328,7 +1295,8 @@ function ExchangePage({ sendingInput = false, history, params }) {
<TYPE.black fontSize={14} fontWeight={400} color={theme(isDark).text1}>
Liquidity Provider Fee
</TYPE.black>
<QuestionHelper text="A portion of each trade (0.03%) goes to liquidity providers to incentivize liquidity on the protocol." />
<QuestionHelper
text="A portion of each trade (0.03%) goes to liquidity providers to incentivize liquidity on the protocol."/>
</RowFixed>
<TYPE.black fontSize={14} color={theme(isDark).text1}>
{feeTimesInputFormatted
......@@ -1337,12 +1305,13 @@ function ExchangePage({ sendingInput = false, history, params }) {
</TYPE.black>
</RowBetween>
</AutoColumn>
<SectionBreak />
<SectionBreak/>
<RowFixed padding={'0 20px'}>
<TYPE.black fontWeight={400} fontSize={14} color={theme(isDark).text1}>
Set front running resistance
</TYPE.black>
<QuestionHelper text="Your transaction will revert if the price changes more than this amount after you submit your trade." />
<QuestionHelper
text="Your transaction will revert if the price changes more than this amount after you submit your trade."/>
</RowFixed>
<SlippageTabs
rawSlippage={allowedSlippage}
......
import { WETH } from '@uniswap/sdk'
import { useReducer } from 'react'
import { useWeb3React } from '../../hooks'
import { QueryParams } from '../../utils'
export enum Field {
INPUT,
OUTPUT
......@@ -93,3 +98,34 @@ export function reducer(
}
}
}
export function useSwapStateReducer(params: QueryParams) {
const { chainId } = useWeb3React()
return useReducer(
reducer,
{
independentField: params.outputTokenAddress && !params.inputTokenAddress ? Field.OUTPUT : Field.INPUT,
inputTokenAddress: params.inputTokenAddress ? params.inputTokenAddress : WETH[chainId].address,
outputTokenAddress: params.outputTokenAddress ? params.outputTokenAddress : '',
typedValue:
params.inputTokenAddress && !params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
: ''
: !params.inputTokenAddress && params.outputTokenAddress
? params.outputTokenAmount
? params.outputTokenAmount
: ''
: params.inputTokenAddress && params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
: ''
: ''
? ''
: ''
? ''
: ''
},
initializeSwapState
)
}
......@@ -16,13 +16,29 @@ const ADD_POPUP = 'ADD_POPUP'
const USER_ADVANCED = 'USER_ADVANCED'
const TOGGLE_USER_ADVANCED = 'TOGGLE_USER_ADVANCED'
const ApplicationContext = createContext()
interface ApplicationState {
BLOCK_NUMBER: {},
USD_PRICE: {},
POPUP_LIST: Array<{ key: number; show: boolean; content: React.ReactElement }>,
POPUP_KEY: number,
WALLET_MODAL_OPEN: boolean,
USER_ADVANCED: boolean
}
const ApplicationContext = createContext<[ApplicationState, { [updater: string]: (...args: any[]) => void }]>([{
[BLOCK_NUMBER]: {},
[USD_PRICE]: {},
[POPUP_LIST]: [],
[POPUP_KEY]: 0,
[WALLET_MODAL_OPEN]: false,
[USER_ADVANCED]: false
}, {}])
function useApplicationContext() {
return useContext(ApplicationContext)
}
function reducer(state, { type, payload }) {
function reducer(state: ApplicationState, { type, payload }): ApplicationState {
switch (type) {
case UPDATE_BLOCK_NUMBER: {
const { networkId, blockNumber } = payload
......@@ -66,19 +82,19 @@ export default function Provider({ children }) {
const updateBlockNumber = useCallback((networkId, blockNumber) => {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
}, [])
}, [dispatch])
const toggleWalletModal = useCallback(() => {
dispatch({ type: TOGGLE_WALLET_MODAL })
}, [])
dispatch({ type: TOGGLE_WALLET_MODAL, payload: null })
}, [dispatch])
const toggleUserAdvanced = useCallback(() => {
dispatch({ type: TOGGLE_USER_ADVANCED })
}, [])
dispatch({ type: TOGGLE_USER_ADVANCED, payload: null })
}, [dispatch])
const setPopups = useCallback(newList => {
dispatch({ type: ADD_POPUP, payload: { newList } })
}, [])
}, [dispatch])
return (
<ApplicationContext.Provider
......@@ -105,7 +121,7 @@ export function Updater() {
if (library) {
let stale = false
function update() {
const update = () => {
library
.getBlockNumber()
.then(blockNumber => {
......@@ -164,13 +180,13 @@ export function useToggleUserAdvanced() {
return toggleUserAdvanced
}
export function usePopups() {
export function usePopups(): [ApplicationState['POPUP_LIST'], (content: React.ReactElement) => void, (key: number) => void] {
const [state, { setPopups }] = useApplicationContext()
const index = state[POPUP_KEY]
const currentPopups = state[POPUP_LIST]
function addPopup(content) {
function addPopup(content: React.ReactElement): void {
const newItem = {
show: true,
key: index,
......@@ -180,7 +196,7 @@ export function usePopups() {
setPopups(currentPopups)
}
function removePopup(key) {
function removePopup(key: number): void {
currentPopups.map(item => {
if (key === item.key) {
item.show = false
......
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { WETH, Token, Route, JSBI } from '@uniswap/sdk'
import { useWeb3React } from '../hooks'
import { usePair } from '../contexts/Pairs'
import { isWETH } from '../utils'
const UPDATE = 'UPDATE'
interface RouteState {
[chainId: number]: {
[tokenAddress: string]: {
[tokenAddress: string]: {
route: Route
}
}
}
}
const RouteContext = createContext<[RouteState, { [k: string]: (...args: any) => void }]>([{}, {}])
function useRouteContext() {
return useContext(RouteContext)
}
function reducer(state: RouteState, { type, payload }) {
switch (type) {
case UPDATE: {
const { tokens, route, chainId } = payload
return {
...state,
[chainId]: {
...state[chainId],
[tokens[0]]: {
...state[chainId]?.[tokens[0]],
[tokens[1]]: {
route
}
}
}
}
}
default: {
throw Error(`Unexpected action type in ExchangesContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, {})
const update = useCallback((tokens, route, chainId) => {
dispatch({ type: UPDATE, payload: { tokens, route, chainId } })
}, [])
return (
<RouteContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</RouteContext.Provider>
)
}
/**
* @param tokenA input to token to be sold
* @param tokenB output token to be bought
*
* This hook finds either a direct pair between tokenA and tokenB or,
* a one-hop route that goes through token<->WETH pairs
*
* if neither exists returns null
*/
export function useRoute(tokenA: Token, tokenB: Token) {
const [state, { update }] = useRouteContext()
const { chainId } = useWeb3React()
let route: Route = state?.[chainId]?.[tokenA?.address]?.[tokenB?.address]?.route
// check for direct pair between tokens
const defaultPair = usePair(tokenA, tokenB)
// get token<->WETH pairs
const aToETH = usePair(tokenA && !isWETH(tokenA) ? tokenA : null, WETH[chainId])
const bToETH = usePair(tokenB && !isWETH(tokenB) ? tokenB : null, WETH[chainId])
// needs to route through WETH
const requiresHop =
defaultPair &&
JSBI.equal(defaultPair?.reserve0?.raw, JSBI.BigInt(0)) &&
JSBI.equal(defaultPair?.reserve1?.raw, JSBI.BigInt(0))
useEffect(() => {
if (!route && tokenA && tokenB) {
if (!requiresHop && defaultPair) {
update([tokenA.address, tokenB.address], new Route([defaultPair], tokenA), chainId)
}
if (
requiresHop &&
aToETH &&
bToETH &&
// check there is liquidity in both token<->ETH pairs
JSBI.notEqual(JSBI.BigInt(0), aToETH.reserve0.raw) &&
JSBI.notEqual(JSBI.BigInt(0), bToETH.reserve0.raw)
) {
const routeThroughETH = new Route([aToETH, bToETH], tokenA)
update([tokenA.address, tokenB.address], routeThroughETH, chainId)
}
}
}, [route, requiresHop, update, chainId, tokenA, tokenB, defaultPair, aToETH, bToETH])
return useMemo(() => route, [route])
}
......@@ -16,7 +16,11 @@ const ADD = 'ADD'
const CHECK = 'CHECK'
const FINALIZE = 'FINALIZE'
const TransactionsContext = createContext()
interface TransactionState {
}
const TransactionsContext = createContext<[TransactionState, { [updater: string]: (...args: any[]) => void }]>([{}, {}])
export function useTransactionsContext() {
return useContext(TransactionsContext)
......@@ -134,10 +138,12 @@ export function Updater() {
finalize(chainId, hash, receipt)
// add success or failure popup
if (receipt.status === 1) {
addPopup(<TxnPopup hash={hash} success={true} summary={allTransactions[hash]?.response?.summary} />)
addPopup(<TxnPopup popKey={1} hash={hash} success={true}
summary={allTransactions[hash]?.response?.summary}/>)
} else {
addPopup(
<TxnPopup hash={hash} success={false} summary={allTransactions[hash]?.response?.summary} />
<TxnPopup popKey={2} hash={hash} success={false}
summary={allTransactions[hash]?.response?.summary}/>
)
}
}
......
import { useMemo } from 'react'
import { WETH, Token, TokenAmount, Trade } from '@uniswap/sdk'
import { useWeb3React } from './index'
import { usePair } from '../contexts/Pairs'
import { isWETH } from '../utils'
/**
* Returns the best trade for the exact amount of tokens in to the given token out
*/
export function useTradeExactIn(amountIn?: TokenAmount, tokenOut?: Token): Trade | null {
const { chainId } = useWeb3React()
// check for direct pair between tokens
const pairBetween = usePair(amountIn?.token, tokenOut)
// get token<->WETH pairs
const aToETH = usePair(amountIn && !isWETH(amountIn.token) ? amountIn.token : null, WETH[chainId])
const bToETH = usePair(tokenOut && !isWETH(tokenOut) ? tokenOut : null, WETH[chainId])
return useMemo(() => {
const allPairs = [pairBetween, aToETH, bToETH].filter(p => !!p)
if (amountIn && allPairs.length > 0 && tokenOut) {
return Trade.bestTradeExactIn(allPairs, amountIn, tokenOut)[0] ?? null
}
return null
}, [aToETH, bToETH, pairBetween, amountIn, tokenOut])
}
/**
* Returns the best trade for the token in to the exact amount of token out
*/
export function useTradeExactOut(tokenIn?: Token, amountOut?: TokenAmount): Trade | null {
const { chainId } = useWeb3React()
// check for direct pair between tokens
const pairBetween = usePair(amountOut?.token, tokenIn)
// get token<->WETH pairs
const aToETH = usePair(amountOut && !isWETH(amountOut.token) ? amountOut.token : null, WETH[chainId])
const bToETH = usePair(tokenIn && !isWETH(tokenIn) ? tokenIn : null, WETH[chainId])
return useMemo(() => {
const allPairs = [pairBetween, aToETH, bToETH].filter(p => !!p)
if (amountOut && allPairs.length > 0 && tokenIn) {
return Trade.bestTradeExactOut(allPairs, tokenIn, amountOut)[0] ?? null
}
return null
}, [pairBetween, aToETH, bToETH, amountOut, tokenIn])
}
......@@ -12,7 +12,6 @@ import TransactionContextProvider, { Updater as TransactionContextUpdater } from
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
import ExchangesContextProvider from './contexts/Pairs'
import AllowancesContextProvider from './contexts/Allowances'
import RoutesContextProvider from './contexts/Routes'
import App from './pages/App'
import ThemeProvider, { GlobalStyle } from './theme'
import './i18n'
......@@ -42,11 +41,9 @@ function ContextProviders({ children }) {
<ApplicationContextProvider>
<TransactionContextProvider>
<ExchangesContextProvider>
<RoutesContextProvider>
<BalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider>
</BalancesContextProvider>
</RoutesContextProvider>
</ExchangesContextProvider>
</TransactionContextProvider>
</ApplicationContextProvider>
......
......@@ -69,7 +69,7 @@ function parseUrlTokenAmount(paramName: string): string {
return value
}
interface QueryParams {
export interface QueryParams {
readonly inputTokenAddress: string
readonly outputTokenAddress: string
readonly inputTokenAmount: string
......
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