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

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

parent cb00e0ba
This diff is collapsed.
import { WETH } from '@uniswap/sdk'
import { useReducer } from 'react'
import { useWeb3React } from '../../hooks'
import { QueryParams } from '../../utils'
export enum Field { export enum Field {
INPUT, INPUT,
OUTPUT OUTPUT
...@@ -93,3 +98,34 @@ export function reducer( ...@@ -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' ...@@ -16,13 +16,29 @@ const ADD_POPUP = 'ADD_POPUP'
const USER_ADVANCED = 'USER_ADVANCED' const USER_ADVANCED = 'USER_ADVANCED'
const TOGGLE_USER_ADVANCED = 'TOGGLE_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() { function useApplicationContext() {
return useContext(ApplicationContext) return useContext(ApplicationContext)
} }
function reducer(state, { type, payload }) { function reducer(state: ApplicationState, { type, payload }): ApplicationState {
switch (type) { switch (type) {
case UPDATE_BLOCK_NUMBER: { case UPDATE_BLOCK_NUMBER: {
const { networkId, blockNumber } = payload const { networkId, blockNumber } = payload
...@@ -66,19 +82,19 @@ export default function Provider({ children }) { ...@@ -66,19 +82,19 @@ export default function Provider({ children }) {
const updateBlockNumber = useCallback((networkId, blockNumber) => { const updateBlockNumber = useCallback((networkId, blockNumber) => {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } }) dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
}, []) }, [dispatch])
const toggleWalletModal = useCallback(() => { const toggleWalletModal = useCallback(() => {
dispatch({ type: TOGGLE_WALLET_MODAL }) dispatch({ type: TOGGLE_WALLET_MODAL, payload: null })
}, []) }, [dispatch])
const toggleUserAdvanced = useCallback(() => { const toggleUserAdvanced = useCallback(() => {
dispatch({ type: TOGGLE_USER_ADVANCED }) dispatch({ type: TOGGLE_USER_ADVANCED, payload: null })
}, []) }, [dispatch])
const setPopups = useCallback(newList => { const setPopups = useCallback(newList => {
dispatch({ type: ADD_POPUP, payload: { newList } }) dispatch({ type: ADD_POPUP, payload: { newList } })
}, []) }, [dispatch])
return ( return (
<ApplicationContext.Provider <ApplicationContext.Provider
...@@ -105,7 +121,7 @@ export function Updater() { ...@@ -105,7 +121,7 @@ export function Updater() {
if (library) { if (library) {
let stale = false let stale = false
function update() { const update = () => {
library library
.getBlockNumber() .getBlockNumber()
.then(blockNumber => { .then(blockNumber => {
...@@ -164,13 +180,13 @@ export function useToggleUserAdvanced() { ...@@ -164,13 +180,13 @@ export function useToggleUserAdvanced() {
return toggleUserAdvanced return toggleUserAdvanced
} }
export function usePopups() { export function usePopups(): [ApplicationState['POPUP_LIST'], (content: React.ReactElement) => void, (key: number) => void] {
const [state, { setPopups }] = useApplicationContext() const [state, { setPopups }] = useApplicationContext()
const index = state[POPUP_KEY] const index = state[POPUP_KEY]
const currentPopups = state[POPUP_LIST] const currentPopups = state[POPUP_LIST]
function addPopup(content) { function addPopup(content: React.ReactElement): void {
const newItem = { const newItem = {
show: true, show: true,
key: index, key: index,
...@@ -180,7 +196,7 @@ export function usePopups() { ...@@ -180,7 +196,7 @@ export function usePopups() {
setPopups(currentPopups) setPopups(currentPopups)
} }
function removePopup(key) { function removePopup(key: number): void {
currentPopups.map(item => { currentPopups.map(item => {
if (key === item.key) { if (key === item.key) {
item.show = false 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' ...@@ -16,7 +16,11 @@ const ADD = 'ADD'
const CHECK = 'CHECK' const CHECK = 'CHECK'
const FINALIZE = 'FINALIZE' const FINALIZE = 'FINALIZE'
const TransactionsContext = createContext() interface TransactionState {
}
const TransactionsContext = createContext<[TransactionState, { [updater: string]: (...args: any[]) => void }]>([{}, {}])
export function useTransactionsContext() { export function useTransactionsContext() {
return useContext(TransactionsContext) return useContext(TransactionsContext)
...@@ -134,10 +138,12 @@ export function Updater() { ...@@ -134,10 +138,12 @@ export function Updater() {
finalize(chainId, hash, receipt) finalize(chainId, hash, receipt)
// add success or failure popup // add success or failure popup
if (receipt.status === 1) { 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 { } else {
addPopup( 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 ...@@ -12,7 +12,6 @@ import TransactionContextProvider, { Updater as TransactionContextUpdater } from
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances' import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
import ExchangesContextProvider from './contexts/Pairs' import ExchangesContextProvider from './contexts/Pairs'
import AllowancesContextProvider from './contexts/Allowances' import AllowancesContextProvider from './contexts/Allowances'
import RoutesContextProvider from './contexts/Routes'
import App from './pages/App' import App from './pages/App'
import ThemeProvider, { GlobalStyle } from './theme' import ThemeProvider, { GlobalStyle } from './theme'
import './i18n' import './i18n'
...@@ -42,11 +41,9 @@ function ContextProviders({ children }) { ...@@ -42,11 +41,9 @@ function ContextProviders({ children }) {
<ApplicationContextProvider> <ApplicationContextProvider>
<TransactionContextProvider> <TransactionContextProvider>
<ExchangesContextProvider> <ExchangesContextProvider>
<RoutesContextProvider>
<BalancesContextProvider> <BalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider> <AllowancesContextProvider>{children}</AllowancesContextProvider>
</BalancesContextProvider> </BalancesContextProvider>
</RoutesContextProvider>
</ExchangesContextProvider> </ExchangesContextProvider>
</TransactionContextProvider> </TransactionContextProvider>
</ApplicationContextProvider> </ApplicationContextProvider>
......
...@@ -69,7 +69,7 @@ function parseUrlTokenAmount(paramName: string): string { ...@@ -69,7 +69,7 @@ function parseUrlTokenAmount(paramName: string): string {
return value return value
} }
interface QueryParams { export interface QueryParams {
readonly inputTokenAddress: string readonly inputTokenAddress: string
readonly outputTokenAddress: string readonly outputTokenAddress: string
readonly inputTokenAmount: 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