Commit 690a1212 authored by Moody Salem's avatar Moody Salem Committed by GitHub

Strict typescript (#792)

* Make typescript compilation strict in some new code

* Fix other errors, change how we set dark mode to always respond to url parameter

* Fix bug in block number
parent 3fe0a5a9
import { useEffect } from 'react'
import ReactGA from 'react-ga'
import { RouteComponentProps } from 'react-router-dom'
// fires a GA pageview every time the route changes
export default function GoogleAnalyticsReporter({ location: { pathname, search } }: RouteComponentProps) {
useEffect(() => {
ReactGA.pageview(`${pathname}${search}`)
}, [pathname, search])
return null
}
...@@ -42,11 +42,11 @@ function useMockV1Pair(token?: Token) { ...@@ -42,11 +42,11 @@ function useMockV1Pair(token?: Token) {
} }
export function useV1TradeLinkIfBetter( export function useV1TradeLinkIfBetter(
isExactIn: boolean, isExactIn?: boolean,
inputToken: Token, inputToken?: Token,
outputToken: Token, outputToken?: Token,
exactAmount: TokenAmount, exactAmount?: TokenAmount,
v2Trade: Trade, v2Trade?: Trade,
minimumDelta: Percent = new Percent('0') minimumDelta: Percent = new Percent('0')
): string { ): string {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
......
import React, { Suspense, useEffect } from 'react' import React, { Suspense } from 'react'
import styled from 'styled-components'
import ReactGA from 'react-ga'
import { BrowserRouter, Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom' import { BrowserRouter, Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom'
import styled from 'styled-components'
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
import Create from '../components/CreatePool'
import Footer from '../components/Footer'
import Header from '../components/Header' import Header from '../components/Header'
import Footer from '../components/Footer'
import NavigationTabs from '../components/NavigationTabs' import NavigationTabs from '../components/NavigationTabs'
import Web3ReactManager from '../components/Web3ReactManager' import Find from '../components/PoolFinder'
import Popups from '../components/Popups' import Popups from '../components/Popups'
import Web3ReactManager from '../components/Web3ReactManager'
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
import { isAddress } from '../utils' import { isAddress } from '../utils'
import Swap from './Swap'
import Send from './Send'
import Pool from './Pool' import Pool from './Pool'
import Add from './Pool/AddLiquidity' import Add from './Pool/AddLiquidity'
import Remove from './Pool/RemoveLiquidity' import Remove from './Pool/RemoveLiquidity'
import Find from '../components/PoolFinder' import Send from './Send'
import Create from '../components/CreatePool'
import Swap from './Swap'
const AppWrapper = styled.div` const AppWrapper = styled.div`
display: flex; display: flex;
...@@ -90,14 +91,6 @@ const StyledRed = styled.div` ...@@ -90,14 +91,6 @@ const StyledRed = styled.div`
} }
` `
// fires a GA pageview every time the route changes
function GoogleAnalyticsReporter({ location: { pathname, search } }: RouteComponentProps) {
useEffect(() => {
ReactGA.pageview(`${pathname}${search}`)
}, [pathname, search])
return null
}
// Redirects to swap but only replace the pathname // Redirects to swap but only replace the pathname
function RedirectPathToSwapOnly({ location }: RouteComponentProps) { function RedirectPathToSwapOnly({ location }: RouteComponentProps) {
return <Redirect to={{ ...location, pathname: '/swap' }} /> return <Redirect to={{ ...location, pathname: '/swap' }} />
...@@ -132,6 +125,7 @@ export default function App() { ...@@ -132,6 +125,7 @@ export default function App() {
<Suspense fallback={null}> <Suspense fallback={null}>
<BrowserRouter> <BrowserRouter>
<Route component={GoogleAnalyticsReporter} /> <Route component={GoogleAnalyticsReporter} />
<Route component={DarkModeQueryParamReader} />
<AppWrapper> <AppWrapper>
<HeaderWrapper> <HeaderWrapper>
<Header /> <Header />
......
...@@ -21,7 +21,7 @@ export type PopupContent = ...@@ -21,7 +21,7 @@ export type PopupContent =
} }
} }
export const updateBlockNumber = createAction<{ networkId: number; blockNumber: number | null }>('updateBlockNumber') export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('updateBlockNumber')
export const toggleWalletModal = createAction<void>('toggleWalletModal') export const toggleWalletModal = createAction<void>('toggleWalletModal')
export const addPopup = createAction<{ content: PopupContent }>('addPopup') export const addPopup = createAction<{ content: PopupContent }>('addPopup')
export const removePopup = createAction<{ key: string }>('removePopup') export const removePopup = createAction<{ key: string }>('removePopup')
...@@ -4,17 +4,17 @@ import { addPopup, PopupContent, removePopup, toggleWalletModal } from './action ...@@ -4,17 +4,17 @@ import { addPopup, PopupContent, removePopup, toggleWalletModal } from './action
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { AppState } from '../index' import { AppState } from '../index'
export function useBlockNumber() { export function useBlockNumber(): number | undefined {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
return useSelector((state: AppState) => state.application.blockNumber[chainId]) return useSelector((state: AppState) => state.application.blockNumber[chainId ?? -1])
} }
export function useWalletModalOpen() { export function useWalletModalOpen(): boolean {
return useSelector((state: AppState) => state.application.walletModalOpen) return useSelector((state: AppState) => state.application.walletModalOpen)
} }
export function useWalletModalToggle() { export function useWalletModalToggle(): () => void {
const dispatch = useDispatch() const dispatch = useDispatch()
return useCallback(() => dispatch(toggleWalletModal()), [dispatch]) return useCallback(() => dispatch(toggleWalletModal()), [dispatch])
} }
......
...@@ -18,8 +18,12 @@ const initialState: ApplicationState = { ...@@ -18,8 +18,12 @@ const initialState: ApplicationState = {
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
builder builder
.addCase(updateBlockNumber, (state, action) => { .addCase(updateBlockNumber, (state, action) => {
const { networkId, blockNumber } = action.payload const { chainId, blockNumber } = action.payload
state.blockNumber[networkId] = blockNumber if (typeof state.blockNumber[chainId] !== 'number') {
state.blockNumber[chainId] = blockNumber
} else {
state.blockNumber[chainId] = Math.max(blockNumber, state.blockNumber[chainId])
}
}) })
.addCase(toggleWalletModal, state => { .addCase(toggleWalletModal, state => {
state.walletModalOpen = !state.walletModalOpen state.walletModalOpen = !state.walletModalOpen
......
...@@ -9,31 +9,20 @@ export default function Updater() { ...@@ -9,31 +9,20 @@ export default function Updater() {
// update block number // update block number
useEffect(() => { useEffect(() => {
if (library) { if (!library || !chainId) return
let stale = false
const update = () => { const blockListener = (blockNumber: number) => {
library dispatch(updateBlockNumber({ chainId, blockNumber }))
.getBlockNumber() }
.then(blockNumber => {
if (!stale) {
dispatch(updateBlockNumber({ networkId: chainId, blockNumber }))
}
})
.catch(() => {
if (!stale) {
dispatch(updateBlockNumber({ networkId: chainId, blockNumber: null }))
}
})
}
update() library
library.on('block', update) .getBlockNumber()
.then(blockNumber => updateBlockNumber({ chainId, blockNumber }))
.catch(error => console.error(`Failed to get block number for chainId ${chainId}`, error))
return () => { library.on('block', blockListener)
stale = true return () => {
library.removeListener('block', update) library.removeListener('block', blockListener)
}
} }
}, [dispatch, chainId, library]) }, [dispatch, chainId, library])
......
...@@ -53,14 +53,20 @@ export function useSwapActionHandlers(): { ...@@ -53,14 +53,20 @@ export function useSwapActionHandlers(): {
// try to parse a user entered amount for a given token // try to parse a user entered amount for a given token
function tryParseAmount(value?: string, token?: Token): TokenAmount | undefined { function tryParseAmount(value?: string, token?: Token): TokenAmount | undefined {
if (!value || !token) return if (!value || !token) {
return
}
try { try {
const typedValueParsed = parseUnits(value, token.decimals).toString() const typedValueParsed = parseUnits(value, token.decimals).toString()
if (typedValueParsed !== '0') return new TokenAmount(token, JSBI.BigInt(typedValueParsed)) if (typedValueParsed !== '0') {
return new TokenAmount(token, JSBI.BigInt(typedValueParsed))
}
} catch (error) { } catch (error) {
// should fail if the user specifies too many decimal places of precision (or maybe exceed max uint?) // should fail if the user specifies too many decimal places of precision (or maybe exceed max uint?)
console.debug(`Failed to parse input amount: "${value}"`, error) console.debug(`Failed to parse input amount: "${value}"`, error)
} }
// necessary for all paths to return a value
return
} }
// from the current swap inputs, compute the best trade and return it. // from the current swap inputs, compute the best trade and return it.
...@@ -68,7 +74,7 @@ export function useDerivedSwapInfo(): { ...@@ -68,7 +74,7 @@ export function useDerivedSwapInfo(): {
tokens: { [field in Field]?: Token } tokens: { [field in Field]?: Token }
tokenBalances: { [field in Field]?: TokenAmount } tokenBalances: { [field in Field]?: TokenAmount }
parsedAmounts: { [field in Field]?: TokenAmount } parsedAmounts: { [field in Field]?: TokenAmount }
bestTrade?: Trade bestTrade: Trade | null
error?: string error?: string
v1TradeLinkIfBetter?: string v1TradeLinkIfBetter?: string
} { } {
...@@ -84,13 +90,13 @@ export function useDerivedSwapInfo(): { ...@@ -84,13 +90,13 @@ export function useDerivedSwapInfo(): {
const tokenIn = useTokenByAddressAndAutomaticallyAdd(tokenInAddress) const tokenIn = useTokenByAddressAndAutomaticallyAdd(tokenInAddress)
const tokenOut = useTokenByAddressAndAutomaticallyAdd(tokenOutAddress) const tokenOut = useTokenByAddressAndAutomaticallyAdd(tokenOutAddress)
const relevantTokenBalances = useTokenBalancesTreatWETHAsETH(account, [tokenIn, tokenOut]) const relevantTokenBalances = useTokenBalancesTreatWETHAsETH(account ?? undefined, [tokenIn, tokenOut])
const isExactIn: boolean = independentField === Field.INPUT const isExactIn: boolean = independentField === Field.INPUT
const amount = tryParseAmount(typedValue, isExactIn ? tokenIn : tokenOut) const amount = tryParseAmount(typedValue, isExactIn ? tokenIn : tokenOut)
const bestTradeExactIn = useTradeExactIn(isExactIn ? amount : null, tokenOut) const bestTradeExactIn = useTradeExactIn(isExactIn ? amount : undefined, tokenOut)
const bestTradeExactOut = useTradeExactOut(tokenIn, !isExactIn ? amount : null) const bestTradeExactOut = useTradeExactOut(tokenIn, !isExactIn ? amount : undefined)
const bestTrade = isExactIn ? bestTradeExactIn : bestTradeExactOut const bestTrade = isExactIn ? bestTradeExactIn : bestTradeExactOut
...@@ -100,11 +106,11 @@ export function useDerivedSwapInfo(): { ...@@ -100,11 +106,11 @@ export function useDerivedSwapInfo(): {
} }
const tokenBalances = { const tokenBalances = {
[Field.INPUT]: relevantTokenBalances?.[tokenIn?.address], [Field.INPUT]: relevantTokenBalances?.[tokenIn?.address ?? ''],
[Field.OUTPUT]: relevantTokenBalances?.[tokenOut?.address] [Field.OUTPUT]: relevantTokenBalances?.[tokenOut?.address ?? '']
} }
const tokens = { const tokens: { [field in Field]?: Token } = {
[Field.INPUT]: tokenIn, [Field.INPUT]: tokenIn,
[Field.OUTPUT]: tokenOut [Field.OUTPUT]: tokenOut
} }
...@@ -115,7 +121,7 @@ export function useDerivedSwapInfo(): { ...@@ -115,7 +121,7 @@ export function useDerivedSwapInfo(): {
tokens[Field.INPUT], tokens[Field.INPUT],
tokens[Field.OUTPUT], tokens[Field.OUTPUT],
isExactIn ? parsedAmounts[Field.INPUT] : parsedAmounts[Field.OUTPUT], isExactIn ? parsedAmounts[Field.INPUT] : parsedAmounts[Field.OUTPUT],
bestTrade, bestTrade ?? undefined,
V1_TRADE_LINK_THRESHOLD V1_TRADE_LINK_THRESHOLD
) )
...@@ -132,12 +138,9 @@ export function useDerivedSwapInfo(): { ...@@ -132,12 +138,9 @@ export function useDerivedSwapInfo(): {
error = error ?? 'Enter an amount' error = error ?? 'Enter an amount'
} }
if ( const [balanceIn, amountIn] = [tokenBalances[Field.INPUT], parsedAmounts[Field.INPUT]]
tokenBalances[Field.INPUT] && if (balanceIn && amountIn && balanceIn.lessThan(amountIn)) {
parsedAmounts[Field.INPUT] && error = 'Insufficient ' + amountIn.token.symbol + ' balance'
tokenBalances[Field.INPUT].lessThan(parsedAmounts[Field.INPUT])
) {
error = 'Insufficient ' + tokens[Field.INPUT]?.symbol + ' balance'
} }
return { return {
...@@ -156,6 +159,7 @@ export function useDefaultsFromURL(search?: string) { ...@@ -156,6 +159,7 @@ export function useDefaultsFromURL(search?: string) {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
useEffect(() => { useEffect(() => {
if (!chainId) return
dispatch(setDefaultsFromURL({ chainId, queryString: search })) dispatch(setDefaultsFromURL({ chainId, queryString: search }))
}, [dispatch, search, chainId]) }, [dispatch, search, chainId])
} }
import { ChainId, WETH } from '@uniswap/sdk' import { ChainId, WETH } from '@uniswap/sdk'
import { createStore } from 'redux' import { createStore, Store } from 'redux'
import { Field, setDefaultsFromURL } from './actions' import { Field, setDefaultsFromURL } from './actions'
import reducer from './reducer' import reducer, { SwapState } from './reducer'
describe('swap reducer', () => { describe('swap reducer', () => {
let store let store: Store<SwapState>
beforeEach(() => { beforeEach(() => {
store = createStore(reducer, { store = createStore(reducer, {
......
import { parse } from 'qs' import { parse } from 'qs'
import { createReducer } from '@reduxjs/toolkit' import { createReducer } from '@reduxjs/toolkit'
import { WETH } from '@uniswap/sdk' import { ChainId, WETH } from '@uniswap/sdk'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { Field, selectToken, setDefaultsFromURL, switchTokens, typeInput } from './actions' import { Field, selectToken, setDefaultsFromURL, switchTokens, typeInput } from './actions'
...@@ -30,11 +30,11 @@ function parseCurrencyFromURLParameter(urlParam: any, chainId: number): string { ...@@ -30,11 +30,11 @@ function parseCurrencyFromURLParameter(urlParam: any, chainId: number): string {
if (typeof urlParam === 'string') { if (typeof urlParam === 'string') {
const valid = isAddress(urlParam) const valid = isAddress(urlParam)
if (valid) return valid if (valid) return valid
if (urlParam.toLowerCase() === 'eth') return WETH[chainId]?.address ?? '' if (urlParam.toLowerCase() === 'eth') return WETH[chainId as ChainId]?.address ?? ''
if (valid === false) return WETH[chainId]?.address ?? '' if (valid === false) return WETH[chainId as ChainId]?.address ?? ''
} }
return WETH[chainId]?.address return WETH[chainId as ChainId]?.address
} }
function parseTokenAmountURLParameter(urlParam: any): string { function parseTokenAmountURLParameter(urlParam: any): string {
...@@ -49,7 +49,7 @@ export default createReducer<SwapState>(initialState, builder => ...@@ -49,7 +49,7 @@ export default createReducer<SwapState>(initialState, builder =>
builder builder
.addCase(setDefaultsFromURL, (state, { payload: { queryString, chainId } }) => { .addCase(setDefaultsFromURL, (state, { payload: { queryString, chainId } }) => {
if (queryString && queryString.length > 1) { if (queryString && queryString.length > 1) {
const parsedQs = parse(queryString.substr(1), { parseArrays: false }) const parsedQs = parse(queryString, { parseArrays: false, ignoreQueryPrefix: true })
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency, chainId) let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency, chainId)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency, chainId) let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency, chainId)
...@@ -76,7 +76,7 @@ export default createReducer<SwapState>(initialState, builder => ...@@ -76,7 +76,7 @@ export default createReducer<SwapState>(initialState, builder =>
return { return {
...initialState, ...initialState,
[Field.INPUT]: { [Field.INPUT]: {
address: WETH[chainId]?.address address: WETH[chainId as ChainId]?.address ?? ''
} }
} }
}) })
......
...@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux' ...@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { useWeb3React } from '../../hooks' import { useWeb3React } from '../../hooks'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { addTransaction } from './actions' import { addTransaction } from './actions'
import { TransactionDetails } from './reducer' import { TransactionDetails, TransactionState } from './reducer'
// helper that can take a ethers library transaction response and add it to the list of transactions // helper that can take a ethers library transaction response and add it to the list of transactions
export function useTransactionAdder(): ( export function useTransactionAdder(): (
...@@ -20,6 +20,9 @@ export function useTransactionAdder(): ( ...@@ -20,6 +20,9 @@ export function useTransactionAdder(): (
response: TransactionResponse, response: TransactionResponse,
{ summary, approvalOfToken }: { summary?: string; approvalOfToken?: string } = {} { summary, approvalOfToken }: { summary?: string; approvalOfToken?: string } = {}
) => { ) => {
if (!account) return
if (!chainId) return
const { hash } = response const { hash } = response
if (!hash) { if (!hash) {
throw Error('No transaction hash found.') throw Error('No transaction hash found.')
...@@ -34,9 +37,9 @@ export function useTransactionAdder(): ( ...@@ -34,9 +37,9 @@ export function useTransactionAdder(): (
export function useAllTransactions(): { [txHash: string]: TransactionDetails } { export function useAllTransactions(): { [txHash: string]: TransactionDetails } {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const state = useSelector<AppState>(state => state.transactions) const state = useSelector<AppState, TransactionState>(state => state.transactions)
return state[chainId] ?? {} return state[chainId ?? -1] ?? {}
} }
// returns whether a token has a pending approval transaction // returns whether a token has a pending approval transaction
......
...@@ -11,65 +11,68 @@ export default function Updater() { ...@@ -11,65 +11,68 @@ export default function Updater() {
const lastBlockNumber = useBlockNumber() const lastBlockNumber = useBlockNumber()
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const transactions = useSelector<AppState>(state => state.transactions) const transactions = useSelector<AppState, AppState['transactions']>(state => state.transactions)
const allTransactions = transactions[chainId] ?? {} const allTransactions = transactions[chainId ?? -1] ?? {}
// show popup on confirm // show popup on confirm
const addPopup = useAddPopup() const addPopup = useAddPopup()
useEffect(() => { useEffect(() => {
if (typeof chainId === 'number' && library) { if (!chainId) return
Object.keys(allTransactions) if (!library) return
.filter(hash => !allTransactions[hash].receipt) if (!lastBlockNumber) return
.filter(
hash => Object.keys(allTransactions)
!allTransactions[hash].blockNumberChecked || allTransactions[hash].blockNumberChecked < lastBlockNumber .filter(hash => !allTransactions[hash].receipt)
) .filter(hash => {
.forEach(hash => { const lastChecked = allTransactions[hash].blockNumberChecked
library
.getTransactionReceipt(hash) return !lastChecked || lastChecked < lastBlockNumber
.then(receipt => { })
if (!receipt) { .forEach(hash => {
dispatch(checkTransaction({ chainId, hash, blockNumber: lastBlockNumber })) library
} else { .getTransactionReceipt(hash)
dispatch( .then(receipt => {
finalizeTransaction({ if (!receipt) {
chainId, dispatch(checkTransaction({ chainId, hash, blockNumber: lastBlockNumber }))
} else {
dispatch(
finalizeTransaction({
chainId,
hash,
receipt: {
blockHash: receipt.blockHash,
blockNumber: receipt.blockNumber,
contractAddress: receipt.contractAddress,
from: receipt.from,
status: receipt.status,
to: receipt.to,
transactionHash: receipt.transactionHash,
transactionIndex: receipt.transactionIndex
}
})
)
// add success or failure popup
if (receipt.status === 1) {
addPopup({
txn: {
hash, hash,
receipt: { success: true,
blockHash: receipt.blockHash, summary: allTransactions[hash]?.summary
blockNumber: receipt.blockNumber, }
contractAddress: receipt.contractAddress, })
from: receipt.from, } else {
status: receipt.status, addPopup({
to: receipt.to, txn: { hash, success: false, summary: allTransactions[hash]?.summary }
transactionHash: receipt.transactionHash, })
transactionIndex: receipt.transactionIndex
}
})
)
// add success or failure popup
if (receipt.status === 1) {
addPopup({
txn: {
hash,
success: true,
summary: allTransactions[hash]?.summary
}
})
} else {
addPopup({
txn: { hash, success: false, summary: allTransactions[hash]?.summary }
})
}
} }
}) }
.catch(error => { })
console.error(`failed to check transaction hash: ${hash}`, error) .catch(error => {
}) console.error(`failed to check transaction hash: ${hash}`, error)
}) })
} })
}, [chainId, library, allTransactions, lastBlockNumber, dispatch, addPopup]) }, [chainId, library, allTransactions, lastBlockNumber, dispatch, addPopup])
return null return null
......
{
"extends": "../../tsconfig.strict.json",
"include": ["**/*"]
}
\ No newline at end of file
...@@ -4,8 +4,8 @@ export interface SerializedToken { ...@@ -4,8 +4,8 @@ export interface SerializedToken {
chainId: number chainId: number
address: string address: string
decimals: number decimals: number
symbol: string symbol?: string
name: string name?: string
} }
export interface SerializedPair { export interface SerializedPair {
......
...@@ -35,7 +35,10 @@ function deserializeToken(serializedToken: SerializedToken): Token { ...@@ -35,7 +35,10 @@ function deserializeToken(serializedToken: SerializedToken): Token {
} }
export function useIsDarkMode(): boolean { export function useIsDarkMode(): boolean {
const { userDarkMode, matchesDarkMode } = useSelector<AppState, { userDarkMode: boolean; matchesDarkMode: boolean }>( const { userDarkMode, matchesDarkMode } = useSelector<
AppState,
{ userDarkMode: boolean | null; matchesDarkMode: boolean }
>(
({ user: { matchesDarkMode, userDarkMode } }) => ({ ({ user: { matchesDarkMode, userDarkMode } }) => ({
userDarkMode, userDarkMode,
matchesDarkMode matchesDarkMode
...@@ -61,7 +64,8 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n ...@@ -61,7 +64,8 @@ export function useFetchTokenByAddress(): (address: string) => Promise<Token | n
const { library, chainId } = useWeb3React() const { library, chainId } = useWeb3React()
return useCallback( return useCallback(
async (address: string) => { async (address: string): Promise<Token | null> => {
if (!library || !chainId) return null
const [decimals, symbol, name] = await Promise.all([ const [decimals, symbol, name] = await Promise.all([
getTokenDecimals(address, library).catch(() => null), getTokenDecimals(address, library).catch(() => null),
getTokenSymbol(address, library).catch(() => 'UNKNOWN'), getTokenSymbol(address, library).catch(() => 'UNKNOWN'),
...@@ -103,7 +107,7 @@ export function useUserAddedTokens(): Token[] { ...@@ -103,7 +107,7 @@ export function useUserAddedTokens(): Token[] {
const serializedTokensMap = useSelector<AppState, AppState['user']['tokens']>(({ user: { tokens } }) => tokens) const serializedTokensMap = useSelector<AppState, AppState['user']['tokens']>(({ user: { tokens } }) => tokens)
return useMemo(() => { return useMemo(() => {
return Object.values(serializedTokensMap[chainId] ?? {}).map(deserializeToken) return Object.values(serializedTokensMap[chainId as ChainId] ?? {}).map(deserializeToken)
}, [serializedTokensMap, chainId]) }, [serializedTokensMap, chainId])
} }
...@@ -156,17 +160,17 @@ export function useAllDummyPairs(): Pair[] { ...@@ -156,17 +160,17 @@ export function useAllDummyPairs(): Pair[] {
return new Pair(new TokenAmount(base, ZERO), new TokenAmount(token, ZERO)) return new Pair(new TokenAmount(base, ZERO), new TokenAmount(token, ZERO))
} }
}) })
.filter(pair => !!pair) .filter(pair => !!pair) as Pair[]
) )
}), }),
[tokens, chainId] [tokens, chainId]
) )
const savedSerializedPairs = useSelector<AppState>(({ user: { pairs } }) => pairs) const savedSerializedPairs = useSelector<AppState, AppState['user']['pairs']>(({ user: { pairs } }) => pairs)
const userPairs = useMemo( const userPairs = useMemo(
() => () =>
Object.values<SerializedPair>(savedSerializedPairs[chainId] ?? {}).map( Object.values<SerializedPair>(savedSerializedPairs[chainId ?? -1] ?? {}).map(
pair => pair =>
new Pair( new Pair(
new TokenAmount(deserializeToken(pair.token0), ZERO), new TokenAmount(deserializeToken(pair.token0), ZERO),
......
...@@ -51,12 +51,13 @@ const initialState: UserState = { ...@@ -51,12 +51,13 @@ const initialState: UserState = {
timestamp: currentTimestamp() timestamp: currentTimestamp()
} }
const GIT_COMMIT_HASH: string | undefined = process.env.REACT_APP_GIT_COMMIT_HASH
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
builder builder
.addCase(updateVersion, state => { .addCase(updateVersion, state => {
if (state.lastVersion !== process.env.REACT_APP_GIT_COMMIT_HASH) { if (GIT_COMMIT_HASH && state.lastVersion !== GIT_COMMIT_HASH) {
state.lastVersion = process.env.REACT_APP_GIT_COMMIT_HASH state.lastVersion = GIT_COMMIT_HASH
// other stuff
} }
state.timestamp = currentTimestamp() state.timestamp = currentTimestamp()
}) })
......
import { getAddress } from '@ethersproject/address' import { getAddress } from '@ethersproject/address'
import { JSBI, Token, TokenAmount, WETH } from '@uniswap/sdk' import { ChainId, JSBI, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { useAllTokens } from '../../hooks/Tokens' import { useAllTokens } from '../../hooks/Tokens'
...@@ -26,7 +26,7 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [ ...@@ -26,7 +26,7 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [
() => () =>
uncheckedAddresses uncheckedAddresses
? uncheckedAddresses ? uncheckedAddresses
.filter(isAddress) .filter((a): a is string => isAddress(a) !== false)
.map(getAddress) .map(getAddress)
.sort() .sort()
: [], : [],
...@@ -38,12 +38,15 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [ ...@@ -38,12 +38,15 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [
if (addresses.length === 0) return if (addresses.length === 0) return
dispatch(startListeningForBalance({ addresses })) dispatch(startListeningForBalance({ addresses }))
return () => dispatch(stopListeningForBalance({ addresses })) return () => {
dispatch(stopListeningForBalance({ addresses }))
}
}, [addresses, dispatch]) }, [addresses, dispatch])
const rawBalanceMap = useSelector<AppState>(({ wallet: { balances } }) => balances) const rawBalanceMap = useSelector<AppState, AppState['wallet']['balances']>(({ wallet: { balances } }) => balances)
return useMemo(() => { return useMemo(() => {
if (!chainId) return {}
return addresses.reduce<{ [address: string]: JSBI }>((map, address) => { return addresses.reduce<{ [address: string]: JSBI }>((map, address) => {
const key = balanceKey({ address, chainId }) const key = balanceKey({ address, chainId })
const { value } = rawBalanceMap[key] ?? {} const { value } = rawBalanceMap[key] ?? {}
...@@ -65,7 +68,10 @@ export function useTokenBalances( ...@@ -65,7 +68,10 @@ export function useTokenBalances(
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const validTokens: Token[] = useMemo(() => tokens?.filter(t => isAddress(t?.address)) ?? [], [tokens]) const validTokens: Token[] = useMemo(
() => tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false) ?? [],
[tokens]
)
const tokenAddresses: string[] = useMemo(() => validTokens.map(t => t.address).sort(), [validTokens]) const tokenAddresses: string[] = useMemo(() => validTokens.map(t => t.address).sort(), [validTokens])
// keep the listeners up to date // keep the listeners up to date
...@@ -75,13 +81,15 @@ export function useTokenBalances( ...@@ -75,13 +81,15 @@ export function useTokenBalances(
const combos: TokenBalanceListenerKey[] = tokenAddresses.map(tokenAddress => ({ address, tokenAddress })) const combos: TokenBalanceListenerKey[] = tokenAddresses.map(tokenAddress => ({ address, tokenAddress }))
dispatch(startListeningForTokenBalances(combos)) dispatch(startListeningForTokenBalances(combos))
return () => dispatch(stopListeningForTokenBalances(combos)) return () => {
dispatch(stopListeningForTokenBalances(combos))
}
}, [address, tokenAddresses, dispatch]) }, [address, tokenAddresses, dispatch])
const rawBalanceMap = useSelector<AppState>(({ wallet: { balances } }) => balances) const rawBalanceMap = useSelector<AppState, AppState['wallet']['balances']>(({ wallet: { balances } }) => balances)
return useMemo(() => { return useMemo(() => {
if (!address || validTokens.length === 0) { if (!address || validTokens.length === 0 || !chainId) {
return {} return {}
} }
return ( return (
...@@ -110,7 +118,8 @@ export function useTokenBalancesTreatWETHAsETH( ...@@ -110,7 +118,8 @@ export function useTokenBalancesTreatWETHAsETH(
} }
let includesWETH = false let includesWETH = false
const tokensWithoutWETH = tokens.filter(t => { const tokensWithoutWETH = tokens.filter(t => {
const isWETH = t?.equals(WETH[chainId]) ?? false if (!chainId) return true
const isWETH = t?.equals(WETH[chainId as ChainId]) ?? false
if (isWETH) includesWETH = true if (isWETH) includesWETH = true
return !isWETH return !isWETH
}) })
...@@ -121,8 +130,9 @@ export function useTokenBalancesTreatWETHAsETH( ...@@ -121,8 +130,9 @@ export function useTokenBalancesTreatWETHAsETH(
const ETHBalance = useETHBalances(includesWETH ? [address] : []) const ETHBalance = useETHBalances(includesWETH ? [address] : [])
return useMemo(() => { return useMemo(() => {
if (!chainId || !address) return {}
if (includesWETH) { if (includesWETH) {
const weth = WETH[chainId] const weth = WETH[chainId as ChainId]
const ethBalance = ETHBalance[address] const ethBalance = ETHBalance[address]
return { return {
...balancesWithoutWETH, ...balancesWithoutWETH,
...@@ -136,13 +146,16 @@ export function useTokenBalancesTreatWETHAsETH( ...@@ -136,13 +146,16 @@ export function useTokenBalancesTreatWETHAsETH(
// get the balance for a single token/account combo // get the balance for a single token/account combo
export function useTokenBalance(account?: string, token?: Token): TokenAmount | undefined { export function useTokenBalance(account?: string, token?: Token): TokenAmount | undefined {
return useTokenBalances(account, [token])?.[token?.address] const tokenBalances = useTokenBalances(account, [token])
if (!token) return
return tokenBalances[token.address]
} }
// mimics the behavior of useAddressBalance // mimics the behavior of useAddressBalance
export function useTokenBalanceTreatingWETHasETH(account?: string, token?: Token): TokenAmount | undefined { export function useTokenBalanceTreatingWETHasETH(account?: string, token?: Token): TokenAmount | undefined {
const balances = useTokenBalancesTreatWETHAsETH(account, [token]) const balances = useTokenBalancesTreatWETHAsETH(account, [token])
return token && token.address && balances?.[token.address] if (!token) return
return balances?.[token.address]
} }
// mimics useAllBalances // mimics useAllBalances
...@@ -152,6 +165,6 @@ export function useAllTokenBalancesTreatingWETHasETH(): { ...@@ -152,6 +165,6 @@ export function useAllTokenBalancesTreatingWETHasETH(): {
const { account } = useWeb3React() const { account } = useWeb3React()
const allTokens = useAllTokens() const allTokens = useAllTokens()
const allTokensArray = useMemo(() => Object.values(allTokens ?? {}), [allTokens]) const allTokensArray = useMemo(() => Object.values(allTokens ?? {}), [allTokens])
const balances = useTokenBalancesTreatWETHAsETH(account, allTokensArray) const balances = useTokenBalancesTreatWETHAsETH(account ?? undefined, allTokensArray)
return account ? { [account]: balances } : {} return account ? { [account]: balances } : {}
} }
...@@ -18,13 +18,13 @@ export default function Updater() { ...@@ -18,13 +18,13 @@ export default function Updater() {
const { chainId, library } = useWeb3React() const { chainId, library } = useWeb3React()
const lastBlockNumber = useBlockNumber() const lastBlockNumber = useBlockNumber()
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const ethBalanceListeners = useSelector<AppState>(state => { const ethBalanceListeners = useSelector<AppState, AppState['wallet']['balanceListeners']>(state => {
return state.wallet.balanceListeners return state.wallet.balanceListeners
}) })
const tokenBalanceListeners = useSelector<AppState>(state => { const tokenBalanceListeners = useSelector<AppState, AppState['wallet']['tokenBalanceListeners']>(state => {
return state.wallet.tokenBalanceListeners return state.wallet.tokenBalanceListeners
}) })
const allBalances = useSelector<AppState>(state => state.wallet.balances) const allBalances = useSelector<AppState, AppState['wallet']['balances']>(state => state.wallet.balances)
const activeETHListeners: string[] = useMemo(() => { const activeETHListeners: string[] = useMemo(() => {
return Object.keys(ethBalanceListeners).filter(address => ethBalanceListeners[address] > 0) // redundant check return Object.keys(ethBalanceListeners).filter(address => ethBalanceListeners[address] > 0) // redundant check
...@@ -41,18 +41,22 @@ export default function Updater() { ...@@ -41,18 +41,22 @@ export default function Updater() {
}, [tokenBalanceListeners]) }, [tokenBalanceListeners])
const ethBalancesNeedUpdate: string[] = useMemo(() => { const ethBalancesNeedUpdate: string[] = useMemo(() => {
if (!chainId || !lastBlockNumber) return []
return activeETHListeners.filter(address => { return activeETHListeners.filter(address => {
const data = allBalances[balanceKey({ chainId, address })] const data = allBalances[balanceKey({ chainId, address })]
return !data || data.blockNumber < lastBlockNumber if (!data || !data.blockNumber) return true
return data.blockNumber < lastBlockNumber
}) })
}, [activeETHListeners, allBalances, chainId, lastBlockNumber]) }, [activeETHListeners, allBalances, chainId, lastBlockNumber])
const tokenBalancesNeedUpdate: { [address: string]: string[] } = useMemo(() => { const tokenBalancesNeedUpdate: { [address: string]: string[] } = useMemo(() => {
if (!chainId || !lastBlockNumber) return {}
return Object.keys(activeTokenBalanceListeners).reduce<{ [address: string]: string[] }>((map, address) => { return Object.keys(activeTokenBalanceListeners).reduce<{ [address: string]: string[] }>((map, address) => {
const needsUpdate = const needsUpdate =
activeTokenBalanceListeners[address]?.filter(tokenAddress => { activeTokenBalanceListeners[address]?.filter(tokenAddress => {
const data = allBalances[balanceKey({ chainId, tokenAddress, address })] const data = allBalances[balanceKey({ chainId, tokenAddress, address })]
return !data || data.blockNumber < lastBlockNumber if (!data || !data.blockNumber) return true
return data.blockNumber < lastBlockNumber
}) ?? [] }) ?? []
if (needsUpdate.length > 0) { if (needsUpdate.length > 0) {
map[address] = needsUpdate map[address] = needsUpdate
...@@ -62,8 +66,7 @@ export default function Updater() { ...@@ -62,8 +66,7 @@ export default function Updater() {
}, [activeTokenBalanceListeners, allBalances, chainId, lastBlockNumber]) }, [activeTokenBalanceListeners, allBalances, chainId, lastBlockNumber])
useEffect(() => { useEffect(() => {
if (!library) return if (!library || !chainId || !lastBlockNumber || ethBalancesNeedUpdate.length === 0) return
if (ethBalancesNeedUpdate.length === 0) return
getEtherBalances(library, ethBalancesNeedUpdate) getEtherBalances(library, ethBalancesNeedUpdate)
.then(balanceMap => { .then(balanceMap => {
dispatch( dispatch(
...@@ -80,7 +83,7 @@ export default function Updater() { ...@@ -80,7 +83,7 @@ export default function Updater() {
}, [library, ethBalancesNeedUpdate, dispatch, lastBlockNumber, chainId]) }, [library, ethBalancesNeedUpdate, dispatch, lastBlockNumber, chainId])
useEffect(() => { useEffect(() => {
if (!library) return if (!library || !chainId || !lastBlockNumber) return
Object.keys(tokenBalancesNeedUpdate).forEach(address => { Object.keys(tokenBalancesNeedUpdate).forEach(address => {
if (tokenBalancesNeedUpdate[address].length === 0) return if (tokenBalancesNeedUpdate[address].length === 0) return
getTokensBalance(library, address, tokenBalancesNeedUpdate[address]) getTokensBalance(library, address, tokenBalancesNeedUpdate[address])
......
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { RouteComponentProps } from 'react-router-dom'
import { parse } from 'qs'
import { AppDispatch } from '../state'
import { updateUserDarkMode } from '../state/user/actions'
export default function DarkModeQueryParamReader({ location: { search } }: RouteComponentProps) {
const dispatch = useDispatch<AppDispatch>()
useEffect(() => {
if (!search) return
if (search.length < 2) return
const parsed = parse(search, {
parseArrays: false,
ignoreQueryPrefix: true
})
const theme = parsed.theme
if (typeof theme !== 'string') return
if (theme.toLowerCase() === 'light') {
dispatch(updateUserDarkMode({ userDarkMode: false }))
} else if (theme.toLowerCase() === 'dark') {
dispatch(updateUserDarkMode({ userDarkMode: true }))
}
}, [dispatch, search])
return null
}
import React, { useEffect, useMemo } from 'react' import React, { useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import styled, { import styled, {
ThemeProvider as StyledComponentsThemeProvider, ThemeProvider as StyledComponentsThemeProvider,
createGlobalStyle, createGlobalStyle,
css, css,
DefaultTheme DefaultTheme
} from 'styled-components' } from 'styled-components'
import { AppDispatch, AppState } from '../state'
import { updateUserDarkMode } from '../state/user/actions'
import { getQueryParam } from '../utils'
import { useIsDarkMode } from '../state/user/hooks' import { useIsDarkMode } from '../state/user/hooks'
import { Text, TextProps } from 'rebass' import { Text, TextProps } from 'rebass'
import { Colors } from './styled' import { Colors } from './styled'
...@@ -24,8 +20,8 @@ const MEDIA_WIDTHS = { ...@@ -24,8 +20,8 @@ const MEDIA_WIDTHS = {
const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(MEDIA_WIDTHS).reduce( const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(MEDIA_WIDTHS).reduce(
(accumulator, size) => { (accumulator, size) => {
accumulator[size] = (a, b, c) => css` ;(accumulator as any)[size] = (a: any, b: any, c: any) => css`
@media (max-width: ${MEDIA_WIDTHS[size]}px) { @media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) {
${css(a, b, c)} ${css(a, b, c)}
} }
` `
...@@ -117,32 +113,15 @@ export function theme(darkMode: boolean): DefaultTheme { ...@@ -117,32 +113,15 @@ export function theme(darkMode: boolean): DefaultTheme {
} }
export default function ThemeProvider({ children }: { children: React.ReactNode }) { export default function ThemeProvider({ children }: { children: React.ReactNode }) {
const dispatch = useDispatch<AppDispatch>()
const userDarkMode = useSelector<AppState, boolean | null>(state => state.user.userDarkMode)
const darkMode = useIsDarkMode() const darkMode = useIsDarkMode()
const themeURL = getQueryParam(window.location, 'theme')
const urlContainsDarkMode: boolean | null = themeURL
? themeURL.toUpperCase() === 'DARK'
? true
: themeURL.toUpperCase() === 'LIGHT'
? false
: null
: null
useEffect(() => {
if (urlContainsDarkMode !== null && userDarkMode === null) {
dispatch(updateUserDarkMode({ userDarkMode: urlContainsDarkMode }))
}
}, [dispatch, userDarkMode, urlContainsDarkMode])
const themeObject = useMemo(() => theme(darkMode), [darkMode]) const themeObject = useMemo(() => theme(darkMode), [darkMode])
return <StyledComponentsThemeProvider theme={themeObject}>{children}</StyledComponentsThemeProvider> return <StyledComponentsThemeProvider theme={themeObject}>{children}</StyledComponentsThemeProvider>
} }
const TextWrapper = styled(Text)<{ color: keyof Colors }>` const TextWrapper = styled(Text)<{ color: keyof Colors }>`
color: ${({ color, theme }) => theme[color]}; color: ${({ color, theme }) => (theme as any)[color]};
` `
export const TYPE = { export const TYPE = {
......
{
"extends": "../../tsconfig.strict.json",
"include": ["**/*"]
}
\ No newline at end of file
...@@ -22,11 +22,6 @@ export function isAddress(value: any): string | false { ...@@ -22,11 +22,6 @@ export function isAddress(value: any): string | false {
} }
} }
export enum ERROR_CODES {
TOKEN_SYMBOL = 1,
TOKEN_DECIMALS = 2
}
const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = { const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = {
1: '', 1: '',
3: 'ropsten.', 3: 'ropsten.',
...@@ -49,11 +44,6 @@ export function getEtherscanLink(chainId: ChainId, data: string, type: 'transact ...@@ -49,11 +44,6 @@ export function getEtherscanLink(chainId: ChainId, data: string, type: 'transact
} }
} }
export function getQueryParam(windowLocation: Location, name: string): string | undefined {
const q = windowLocation.search.match(new RegExp('[?&]' + name + '=([^&#?]*)'))
return q && q[1]
}
// shorten the checksummed version of the input address to have 0x + 4 characters at start and end // shorten the checksummed version of the input address to have 0x + 4 characters at start and end
export function shortenAddress(address: string, chars = 4): string { export function shortenAddress(address: string, chars = 4): string {
const parsed = isAddress(address) const parsed = isAddress(address)
...@@ -103,17 +93,17 @@ export function getContract(address: string, ABI: any, library: Web3Provider, ac ...@@ -103,17 +93,17 @@ export function getContract(address: string, ABI: any, library: Web3Provider, ac
} }
// account is optional // account is optional
export function getRouterContract(chainId, library, account) { export function getRouterContract(chainId: number, library: Web3Provider, account?: string) {
return getContract(ROUTER_ADDRESS, IUniswapV2Router01ABI, library, account) return getContract(ROUTER_ADDRESS, IUniswapV2Router01ABI, library, account)
} }
// account is optional // account is optional
export function getExchangeContract(pairAddress, library, account) { export function getExchangeContract(pairAddress: string, library: Web3Provider, account?: string) {
return getContract(pairAddress, IUniswapV2PairABI, library, account) return getContract(pairAddress, IUniswapV2PairABI, library, account)
} }
// get token name // get token name
export async function getTokenName(tokenAddress, library) { export async function getTokenName(tokenAddress: string, library: Web3Provider) {
if (!isAddress(tokenAddress)) { if (!isAddress(tokenAddress)) {
throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`) throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`)
} }
...@@ -125,14 +115,10 @@ export async function getTokenName(tokenAddress, library) { ...@@ -125,14 +115,10 @@ export async function getTokenName(tokenAddress, library) {
.name() .name()
.then(parseBytes32String) .then(parseBytes32String)
) )
.catch(error => {
error.code = ERROR_CODES.TOKEN_SYMBOL
throw error
})
} }
// get token symbol // get token symbol
export async function getTokenSymbol(tokenAddress, library) { export async function getTokenSymbol(tokenAddress: string, library: Web3Provider) {
if (!isAddress(tokenAddress)) { if (!isAddress(tokenAddress)) {
throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`) throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`)
} }
...@@ -143,24 +129,15 @@ export async function getTokenSymbol(tokenAddress, library) { ...@@ -143,24 +129,15 @@ export async function getTokenSymbol(tokenAddress, library) {
const contractBytes32 = getContract(tokenAddress, ERC20_BYTES32_ABI, library) const contractBytes32 = getContract(tokenAddress, ERC20_BYTES32_ABI, library)
return contractBytes32.symbol().then(parseBytes32String) return contractBytes32.symbol().then(parseBytes32String)
}) })
.catch(error => {
error.code = ERROR_CODES.TOKEN_SYMBOL
throw error
})
} }
// get token decimals // get token decimals
export async function getTokenDecimals(tokenAddress, library) { export async function getTokenDecimals(tokenAddress: string, library: Web3Provider) {
if (!isAddress(tokenAddress)) { if (!isAddress(tokenAddress)) {
throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`) throw Error(`Invalid 'tokenAddress' parameter '${tokenAddress}'.`)
} }
return getContract(tokenAddress, ERC20_ABI, library) return getContract(tokenAddress, ERC20_ABI, library).decimals()
.decimals()
.catch(error => {
error.code = ERROR_CODES.TOKEN_DECIMALS
throw error
})
} }
export function escapeRegExp(string: string): string { export function escapeRegExp(string: string): string {
......
...@@ -6,6 +6,7 @@ import { basisPointsToPercent } from './index' ...@@ -6,6 +6,7 @@ import { basisPointsToPercent } from './index'
const BASE_FEE = new Percent(JSBI.BigInt(30), JSBI.BigInt(10000)) const BASE_FEE = new Percent(JSBI.BigInt(30), JSBI.BigInt(10000))
const ONE_HUNDRED_PERCENT = new Percent(JSBI.BigInt(10000), JSBI.BigInt(10000)) const ONE_HUNDRED_PERCENT = new Percent(JSBI.BigInt(10000), JSBI.BigInt(10000))
const INPUT_FRACTION_AFTER_FEE = ONE_HUNDRED_PERCENT.subtract(BASE_FEE) 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?: Trade trade?: Trade
...@@ -22,7 +23,7 @@ export function computeTradePriceBreakdown( ...@@ -22,7 +23,7 @@ export function computeTradePriceBreakdown(
) )
// remove lp fees from price impact // remove lp fees from price impact
const priceImpactWithoutFeeFraction = trade?.slippage?.subtract(realizedLPFee) const priceImpactWithoutFeeFraction = trade && realizedLPFee ? trade.slippage.subtract(realizedLPFee) : undefined
// the x*y=k impact // the x*y=k impact
const priceImpactWithoutFeePercent = priceImpactWithoutFeeFraction const priceImpactWithoutFeePercent = priceImpactWithoutFeeFraction
...@@ -31,7 +32,9 @@ export function computeTradePriceBreakdown( ...@@ -31,7 +32,9 @@ export function computeTradePriceBreakdown(
// the amount of the input that accrues to LPs // the amount of the input that accrues to LPs
const realizedLPFeeAmount = const realizedLPFeeAmount =
realizedLPFee && new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient) realizedLPFee &&
trade &&
new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
return { priceImpactWithoutFee: priceImpactWithoutFeePercent, realizedLPFee: realizedLPFeeAmount } return { priceImpactWithoutFee: priceImpactWithoutFeePercent, realizedLPFee: realizedLPFeeAmount }
} }
......
{
"extends": "../../tsconfig.strict.json",
"include": ["**/*"]
}
\ No newline at end of file
{
"extends": "./tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"alwaysStrict": true,
"strictNullChecks": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitUseStrict": true
}
}
\ No newline at end of file
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