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 {
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>
<BalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider>
</BalancesContextProvider>
</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