Commit 38625d24 authored by Ian Lapham's avatar Ian Lapham Committed by Noah Zinsmeister

Balance and USD Refactor (#544)

* update injected loading state

* basic setup

* add contexts

* minor changes

* refactor balances and USd price

* small tweaks

* clean up balances context code

don't store usd price except in hook

refactor getUSDPrice to make clear it's sync

* add small memo
parent 22e859be
...@@ -24,7 +24,7 @@ import { ReactComponent as Close } from '../../assets/images/x.svg' ...@@ -24,7 +24,7 @@ import { ReactComponent as Close } from '../../assets/images/x.svg'
import { transparentize } from 'polished' import { transparentize } from 'polished'
import { Spinner } from '../../theme' import { Spinner } from '../../theme'
import Circle from '../../assets/images/circle-grey.svg' import Circle from '../../assets/images/circle-grey.svg'
import { useUSDPrice } from '../../contexts/Application' import { useETHPriceInUSD, useAllBalances } from '../../contexts/Balances'
const GAS_MARGIN = ethers.utils.bigNumberify(1000) const GAS_MARGIN = ethers.utils.bigNumberify(1000)
...@@ -439,7 +439,7 @@ export default function CurrencyInputPanel({ ...@@ -439,7 +439,7 @@ export default function CurrencyInputPanel({
) )
} }
function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) { function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect }) {
const { t } = useTranslation() const { t } = useTranslation()
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
...@@ -450,19 +450,24 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) ...@@ -450,19 +450,24 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances })
const { account } = useWeb3React() const { account } = useWeb3React()
// BigNumber.js instance // BigNumber.js instance
const ethPrice = useUSDPrice() const ethPrice = useETHPriceInUSD()
// all balances for both account and exchanges
let allBalances = useAllBalances()
const _usdAmounts = Object.keys(allTokens).map(k => { const _usdAmounts = Object.keys(allTokens).map(k => {
if ( if (ethPrice && allBalances[account] && allBalances[account][k]) {
ethPrice && let ethRate = 1 // default for ETH
allBalances && let exchangeDetails = allBalances[allTokens[k].exchangeAddress]
allBalances[k] && if (exchangeDetails && exchangeDetails[k] && exchangeDetails['ETH']) {
allBalances[k].ethRate && const tokenBalance = new BigNumber(exchangeDetails[k].value.toString())
!allBalances[k].ethRate.isNaN() && const ethBalance = new BigNumber(exchangeDetails['ETH'].value.toString())
allBalances[k].balance ethRate = ethBalance.div(tokenBalance)
) { }
const USDRate = ethPrice.times(allBalances[k].ethRate) const USDRate = ethPrice
const balanceBigNumber = new BigNumber(allBalances[k].balance.toString()) .times(ethRate)
.times(new BigNumber(10).pow(allTokens[k].decimals).div(new BigNumber(10).pow(18)))
const balanceBigNumber = new BigNumber(allBalances[account][k].value.toString())
const usdBalance = balanceBigNumber.times(USDRate).div(new BigNumber(10).pow(allTokens[k].decimals)) const usdBalance = balanceBigNumber.times(USDRate).div(new BigNumber(10).pow(allTokens[k].decimals))
return usdBalance return usdBalance
} else { } else {
...@@ -506,11 +511,11 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) ...@@ -506,11 +511,11 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances })
let balance let balance
let usdBalance let usdBalance
// only update if we have data // only update if we have data
if (k === 'ETH' && allBalances && allBalances[k]) { if (k === 'ETH' && allBalances[account] && allBalances[account][k]) {
balance = formatEthBalance(allBalances[k].balance) balance = formatEthBalance(allBalances[account][k].value)
usdBalance = usdAmounts[k] usdBalance = usdAmounts[k]
} else if (allBalances && allBalances[k]) { } else if (allBalances[account] && allBalances[account][k]) {
balance = formatTokenBalance(allBalances[k].balance, allTokens[k].decimals) balance = formatTokenBalance(allBalances[account][k].value, allTokens[k].decimals)
usdBalance = usdAmounts[k] usdBalance = usdAmounts[k]
} }
return { return {
...@@ -521,7 +526,7 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) ...@@ -521,7 +526,7 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances })
usdBalance: usdBalance usdBalance: usdBalance
} }
}) })
}, [allBalances, allTokens, usdAmounts]) }, [allBalances, allTokens, usdAmounts, account])
const filteredTokenList = useMemo(() => { const filteredTokenList = useMemo(() => {
return tokenList.filter(tokenEntry => { return tokenList.filter(tokenEntry => {
...@@ -580,7 +585,13 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) ...@@ -580,7 +585,13 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances })
'-' '-'
)} )}
<TokenRowUsd> <TokenRowUsd>
{usdBalance ? (usdBalance.lt(0.01) ? '<$0.01' : '$' + formatToUsd(usdBalance)) : ''} {usdBalance
? usdBalance.isZero()
? ''
: usdBalance.lt(0.01)
? '<$0.01'
: '$' + formatToUsd(usdBalance)
: ''}
</TokenRowUsd> </TokenRowUsd>
</TokenRowRight> </TokenRowRight>
</TokenModalRow> </TokenModalRow>
......
...@@ -17,7 +17,6 @@ import { useExchangeContract } from '../../hooks' ...@@ -17,7 +17,6 @@ import { useExchangeContract } from '../../hooks'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances' import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { useAddressAllowance } from '../../contexts/Allowances' import { useAddressAllowance } from '../../contexts/Allowances'
import { useWalletModalToggle } from '../../contexts/Application' import { useWalletModalToggle } from '../../contexts/Application'
...@@ -645,15 +644,12 @@ export default function ExchangePage({ initialCurrency, sending = false, params ...@@ -645,15 +644,12 @@ export default function ExchangePage({ initialCurrency, sending = false, params
const [customSlippageError, setcustomSlippageError] = useState('') const [customSlippageError, setcustomSlippageError] = useState('')
const allBalances = useFetchAllBalances()
const toggleWalletModal = useWalletModalToggle() const toggleWalletModal = useWalletModalToggle()
return ( return (
<> <>
<CurrencyInputPanel <CurrencyInputPanel
title={t('input')} title={t('input')}
allBalances={allBalances}
description={inputValueFormatted && independentField === OUTPUT ? estimatedText : ''} description={inputValueFormatted && independentField === OUTPUT ? estimatedText : ''}
extraText={inputBalanceFormatted && formatBalance(inputBalanceFormatted)} extraText={inputBalanceFormatted && formatBalance(inputBalanceFormatted)}
extraTextClickHander={() => { extraTextClickHander={() => {
...@@ -702,7 +698,6 @@ export default function ExchangePage({ initialCurrency, sending = false, params ...@@ -702,7 +698,6 @@ export default function ExchangePage({ initialCurrency, sending = false, params
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('output')} title={t('output')}
allBalances={allBalances}
description={outputValueFormatted && independentField === INPUT ? estimatedText : ''} description={outputValueFormatted && independentField === INPUT ? estimatedText : ''}
extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)} extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)}
onCurrencySelected={outputCurrency => { onCurrencySelected={outputCurrency => {
......
import React, { createContext, useContext, useReducer, useMemo, useCallback } from 'react'
import { ethers } from 'ethers'
import { getTokenReserves, getMarketDetails, BigNumber } from '@uniswap/sdk'
import { useWeb3React } from '../hooks'
import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils'
import { useAllTokenDetails } from './Tokens'
const ZERO = ethers.utils.bigNumberify(0)
const ONE = new BigNumber(1)
const UPDATE = 'UPDATE'
const AllBalancesContext = createContext()
function useAllBalancesContext() {
return useContext(AllBalancesContext)
}
function reducer(state, { type, payload }) {
switch (type) {
case UPDATE: {
const { allBalanceData, networkId, address } = payload
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
[address]: {
...(safeAccess(state, [networkId, address]) || {}),
allBalanceData
}
}
}
}
default: {
throw Error(`Unexpected action type in AllBalancesContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, {})
const update = useCallback((allBalanceData, networkId, address) => {
dispatch({ type: UPDATE, payload: { allBalanceData, networkId, address } })
}, [])
return (
<AllBalancesContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</AllBalancesContext.Provider>
)
}
export function useFetchAllBalances() {
const { library, chainId, account } = useWeb3React()
const allTokens = useAllTokenDetails()
const [state, { update }] = useAllBalancesContext()
const { allBalanceData } = safeAccess(state, [chainId, account]) || {}
const getData = async () => {
if (!!library && !!account) {
const newBalances = {}
await Promise.all(
Object.keys(allTokens).map(async k => {
let balance = null
let ethRate = null
if (isAddress(k) || k === 'ETH') {
if (k === 'ETH') {
balance = await getEtherBalance(account, library).catch(() => null)
ethRate = ONE
} else {
balance = await getTokenBalance(k, account, library).catch(() => null)
// only get values for tokens with positive balances
if (!!balance && balance.gt(ZERO)) {
const tokenReserves = await getTokenReserves(k, library).catch(() => null)
if (!!tokenReserves) {
const marketDetails = getMarketDetails(tokenReserves)
if (marketDetails.marketRate && marketDetails.marketRate.rate) {
ethRate = marketDetails.marketRate.rate
}
}
}
}
return (newBalances[k] = { balance, ethRate })
}
})
)
update(newBalances, chainId, account)
}
}
useMemo(getData, [account])
return allBalanceData
}
...@@ -2,14 +2,12 @@ import React, { createContext, useContext, useReducer, useMemo, useCallback, use ...@@ -2,14 +2,12 @@ import React, { createContext, useContext, useReducer, useMemo, useCallback, use
import { useWeb3React } from '../hooks' import { useWeb3React } from '../hooks'
import { safeAccess } from '../utils' import { safeAccess } from '../utils'
import { getUSDPrice } from '../utils/price'
const BLOCK_NUMBER = 'BLOCK_NUMBER' const BLOCK_NUMBER = 'BLOCK_NUMBER'
const USD_PRICE = 'USD_PRICE' const USD_PRICE = 'USD_PRICE'
const WALLET_MODAL_OPEN = 'WALLET_MODAL_OPEN' const WALLET_MODAL_OPEN = 'WALLET_MODAL_OPEN'
const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER' const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER'
const UPDATE_USD_PRICE = 'UPDATE_USD_PRICE'
const TOGGLE_WALLET_MODAL = 'TOGGLE_WALLET_MODAL' const TOGGLE_WALLET_MODAL = 'TOGGLE_WALLET_MODAL'
const ApplicationContext = createContext() const ApplicationContext = createContext()
...@@ -30,16 +28,7 @@ function reducer(state, { type, payload }) { ...@@ -30,16 +28,7 @@ function reducer(state, { type, payload }) {
} }
} }
} }
case UPDATE_USD_PRICE: {
const { networkId, USDPrice } = payload
return {
...state,
[USD_PRICE]: {
...(safeAccess(state, [USD_PRICE]) || {}),
[networkId]: USDPrice
}
}
}
case TOGGLE_WALLET_MODAL: { case TOGGLE_WALLET_MODAL: {
return { ...state, [WALLET_MODAL_OPEN]: !state[WALLET_MODAL_OPEN] } return { ...state, [WALLET_MODAL_OPEN]: !state[WALLET_MODAL_OPEN] }
} }
...@@ -60,20 +49,15 @@ export default function Provider({ children }) { ...@@ -60,20 +49,15 @@ export default function Provider({ children }) {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } }) dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
}, []) }, [])
const updateUSDPrice = useCallback((networkId, USDPrice) => {
dispatch({ type: UPDATE_USD_PRICE, payload: { networkId, USDPrice } })
}, [])
const toggleWalletModal = useCallback(() => { const toggleWalletModal = useCallback(() => {
dispatch({ type: TOGGLE_WALLET_MODAL }) dispatch({ type: TOGGLE_WALLET_MODAL })
}, []) }, [])
return ( return (
<ApplicationContext.Provider <ApplicationContext.Provider
value={useMemo(() => [state, { updateBlockNumber, updateUSDPrice, toggleWalletModal }], [ value={useMemo(() => [state, { updateBlockNumber, toggleWalletModal }], [
state, state,
updateBlockNumber, updateBlockNumber,
updateUSDPrice,
toggleWalletModal toggleWalletModal
])} ])}
> >
...@@ -85,27 +69,7 @@ export default function Provider({ children }) { ...@@ -85,27 +69,7 @@ export default function Provider({ children }) {
export function Updater() { export function Updater() {
const { library, chainId } = useWeb3React() const { library, chainId } = useWeb3React()
const globalBlockNumber = useBlockNumber() const [, { updateBlockNumber }] = useApplicationContext()
const [, { updateBlockNumber, updateUSDPrice }] = useApplicationContext()
// update usd price
useEffect(() => {
if (library && chainId === 1) {
let stale = false
getUSDPrice(library)
.then(([price]) => {
if (!stale) {
updateUSDPrice(chainId, price)
}
})
.catch(() => {
if (!stale) {
updateUSDPrice(chainId, null)
}
})
}
}, [globalBlockNumber, library, chainId, updateUSDPrice])
// update block number // update block number
useEffect(() => { useEffect(() => {
...@@ -148,14 +112,6 @@ export function useBlockNumber() { ...@@ -148,14 +112,6 @@ export function useBlockNumber() {
return safeAccess(state, [BLOCK_NUMBER, chainId]) return safeAccess(state, [BLOCK_NUMBER, chainId])
} }
export function useUSDPrice() {
const { chainId } = useWeb3React()
const [state] = useApplicationContext()
return safeAccess(state, [USD_PRICE, chainId])
}
export function useWalletModalOpen() { export function useWalletModalOpen() {
const [state] = useApplicationContext() const [state] = useApplicationContext()
......
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect, useRef, useState } from 'react'
import { BigNumber } from '@uniswap/sdk'
import { useWeb3React } from '../hooks' import { useWeb3React } from '../hooks'
import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils' import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils'
import { useBlockNumber } from './Application' import { useBlockNumber } from './Application'
import { useTokenDetails } from './Tokens' import { useTokenDetails, useAllTokenDetails } from './Tokens'
import { getUSDPrice } from '../utils/price'
const UPDATE = 'UPDATE' const UPDATE = 'UPDATE'
const UPDATE_ALL_FOR_ACCOUNT = 'UPDATE_ALL_FOR_ACCOUNT'
const UPDATE_ALL_FOR_EXCHANGES = 'UPDATE_ALL_FOR_EXCHANGES'
const BalancesContext = createContext() const BalancesContext = createContext()
...@@ -31,6 +35,40 @@ function reducer(state, { type, payload }) { ...@@ -31,6 +35,40 @@ function reducer(state, { type, payload }) {
} }
} }
} }
case UPDATE_ALL_FOR_ACCOUNT: {
const { networkId, address, tokenAddresses, values } = payload
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
[address]: {
...tokenAddresses.reduce((accumulator, currentValue, i) => {
accumulator[currentValue] = { value: values[i] }
return accumulator
}, {}),
...(safeAccess(state, [networkId, address]) || {})
}
}
}
}
case UPDATE_ALL_FOR_EXCHANGES: {
const { networkId, exchangeAddresses, tokenAddresses, values } = payload
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
...exchangeAddresses.reduce((accumulator, currentValue, i) => {
accumulator[currentValue] = {
...safeAccess(state, [networkId, currentValue]),
[tokenAddresses[i]]: {
value: values[i]
}
}
return accumulator
}, {})
}
}
}
default: { default: {
throw Error(`Unexpected action type in BalancesContext reducer: '${type}'.`) throw Error(`Unexpected action type in BalancesContext reducer: '${type}'.`)
} }
...@@ -44,13 +82,111 @@ export default function Provider({ children }) { ...@@ -44,13 +82,111 @@ export default function Provider({ children }) {
dispatch({ type: UPDATE, payload: { networkId, address, tokenAddress, value, blockNumber } }) dispatch({ type: UPDATE, payload: { networkId, address, tokenAddress, value, blockNumber } })
}, []) }, [])
const updateAllForAccount = useCallback((networkId, address, tokenAddresses, values) => {
dispatch({ type: UPDATE_ALL_FOR_ACCOUNT, payload: { networkId, address, tokenAddresses, values } })
}, [])
const updateAllForExchanges = useCallback((networkId, exchangeAddresses, tokenAddresses, values) => {
dispatch({ type: UPDATE_ALL_FOR_EXCHANGES, payload: { networkId, exchangeAddresses, tokenAddresses, values } })
}, [])
return ( return (
<BalancesContext.Provider value={useMemo(() => [state, { update }], [state, update])}> <BalancesContext.Provider
value={useMemo(() => [state, { update, updateAllForAccount, updateAllForExchanges }], [
state,
update,
updateAllForAccount,
updateAllForExchanges
])}
>
{children} {children}
</BalancesContext.Provider> </BalancesContext.Provider>
) )
} }
const STAGGER_TIME = 2500
export function Updater() {
const { library, chainId, account } = useWeb3React()
const allTokens = useAllTokenDetails()
const [state, { updateAllForAccount, updateAllForExchanges }] = useBalancesContext()
const stateRef = useRef(state)
stateRef.current = state
useEffect(() => {
const getData = async () => {
if (chainId && library && account) {
// get 1 eth + all token balances for the account
Promise.all(
Object.keys(allTokens).map(async tokenAddress => {
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
const { value: existingValue } = safeAccess(stateRef.current, [chainId, account, tokenAddress]) || {}
return (
existingValue ||
(await (tokenAddress === 'ETH'
? getEtherBalance(account, library).catch(() => null)
: getTokenBalance(tokenAddress, account, library).catch(() => null)))
)
})
).then(balances => {
updateAllForAccount(chainId, account, Object.keys(allTokens), balances)
})
const allTokensWithAnExchange = Object.keys(allTokens).filter(tokenAddress => tokenAddress !== 'ETH')
// get all eth balances for all exchanges
Promise.all(
allTokensWithAnExchange.map(async tokenAddress => {
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
const exchangeAddress = allTokens[tokenAddress].exchangeAddress
const { value: existingValue } = safeAccess(stateRef.current, [chainId, exchangeAddress, 'ETH']) || {}
return existingValue || (await getEtherBalance(exchangeAddress, library).catch(() => null))
})
).then(ethBalances => {
updateAllForExchanges(
chainId,
allTokensWithAnExchange.map(tokenAddress => allTokens[tokenAddress].exchangeAddress),
Array(allTokensWithAnExchange.length).fill('ETH'),
ethBalances
)
})
// get all token balances for all exchanges
Promise.all(
allTokensWithAnExchange.map(async tokenAddress => {
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
const exchangeAddress = allTokens[tokenAddress].exchangeAddress
const { value: existingValue } =
safeAccess(stateRef.current, [chainId, exchangeAddress, tokenAddress]) || {}
return existingValue || (await getTokenBalance(tokenAddress, exchangeAddress, library).catch(() => null))
})
).then(tokenBalances => {
updateAllForExchanges(
chainId,
allTokensWithAnExchange.map(tokenAddress => allTokens[tokenAddress].exchangeAddress),
allTokensWithAnExchange.map(tokenAddress => tokenAddress),
tokenBalances
)
})
}
}
getData()
}, [chainId, library, account, allTokens, updateAllForAccount, updateAllForExchanges])
return null
}
export function useAllBalances() {
const { chainId } = useWeb3React()
const [state] = useBalancesContext()
const balances = safeAccess(state, [chainId]) || {}
return balances
}
export function useAddressBalance(address, tokenAddress) { export function useAddressBalance(address, tokenAddress) {
const { library, chainId } = useWeb3React() const { library, chainId } = useWeb3React()
...@@ -96,3 +232,79 @@ export function useExchangeReserves(tokenAddress) { ...@@ -96,3 +232,79 @@ export function useExchangeReserves(tokenAddress) {
return { reserveETH, reserveToken } return { reserveETH, reserveToken }
} }
const buildReserveObject = (chainId, tokenAddress, ethReserveAmount, tokenReserveAmount, decimals) => ({
token: {
chainId,
address: tokenAddress,
decimals
},
ethReserve: {
token: {
chainId,
decimals: 18
},
amount: ethReserveAmount
},
tokenReserve: {
token: {
chainId,
address: tokenAddress,
decimals
},
amount: tokenReserveAmount
}
})
const daiTokenAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'
const daiExchangeAddress = '0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667'
const usdcTokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
const usdcExchangeAddress = '0x97deC872013f6B5fB443861090ad931542878126'
const tusdTokenAddress = '0x0000000000085d4780B73119b644AE5ecd22b376'
const tusdExchangeAddress = '0x5048b9d01097498Fd72F3F14bC9Bc74A5aAc8fA7'
export function useETHPriceInUSD() {
const { chainId } = useWeb3React()
let daiReserveETH = useAddressBalance(daiExchangeAddress, 'ETH')
let daiReserveToken = useAddressBalance(daiExchangeAddress, daiTokenAddress)
let usdcReserveETH = useAddressBalance(usdcExchangeAddress, 'ETH')
let usdcReserveToken = useAddressBalance(usdcExchangeAddress, usdcTokenAddress)
let tusdReserveETH = useAddressBalance(tusdExchangeAddress, 'ETH')
let tusdReserveToken = useAddressBalance(tusdExchangeAddress, tusdTokenAddress)
const [price, setPrice] = useState()
useEffect(() => {
if (daiReserveETH && daiReserveToken && usdcReserveETH && usdcReserveToken && tusdReserveETH && tusdReserveToken) {
const daiReservesObject = buildReserveObject(
chainId,
daiTokenAddress,
new BigNumber(daiReserveETH.toString()),
new BigNumber(daiReserveToken.toString()),
18
)
const tusdReservesObject = buildReserveObject(
chainId,
tusdTokenAddress,
new BigNumber(tusdReserveETH.toString()),
new BigNumber(tusdReserveToken.toString()),
18
)
const usdcReservesObject = buildReserveObject(
chainId,
usdcTokenAddress,
new BigNumber(usdcReserveETH.toString()),
new BigNumber(usdcReserveToken.toString()),
6
)
const stablecoinReserves = [daiReservesObject, usdcReservesObject, tusdReservesObject]
try {
setPrice(getUSDPrice(stablecoinReserves))
} catch {
setPrice(null)
}
}
}, [daiReserveETH, daiReserveToken, usdcReserveETH, usdcReserveToken, tusdReserveETH, tusdReserveToken, chainId])
return price
}
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { ethers } from 'ethers'
import { useWeb3React } from '../hooks' import { useWeb3React } from '../hooks'
import { import {
...@@ -615,23 +614,10 @@ export function useTokenDetails(tokenAddress) { ...@@ -615,23 +614,10 @@ export function useTokenDetails(tokenAddress) {
return { name, symbol, decimals, exchangeAddress } return { name, symbol, decimals, exchangeAddress }
} }
export function useAllTokenDetails(requireExchange = true) { export function useAllTokenDetails() {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const [state] = useTokensContext() const [state] = useTokensContext()
const tokenDetails = { ...ETH, ...(safeAccess(state, [chainId]) || {}) }
return requireExchange return useMemo(() => ({ ...ETH, ...(safeAccess(state, [chainId]) || {}) }), [state, chainId])
? Object.keys(tokenDetails)
.filter(
tokenAddress =>
tokenAddress === 'ETH' ||
(safeAccess(tokenDetails, [tokenAddress, EXCHANGE_ADDRESS]) &&
safeAccess(tokenDetails, [tokenAddress, EXCHANGE_ADDRESS]) !== ethers.constants.AddressZero)
)
.reduce((accumulator, tokenAddress) => {
accumulator[tokenAddress] = tokenDetails[tokenAddress]
return accumulator
}, {})
: tokenDetails
} }
...@@ -8,10 +8,9 @@ import { NetworkContextName } from './constants' ...@@ -8,10 +8,9 @@ import { NetworkContextName } from './constants'
import LocalStorageContextProvider, { Updater as LocalStorageContextUpdater } from './contexts/LocalStorage' import LocalStorageContextProvider, { Updater as LocalStorageContextUpdater } from './contexts/LocalStorage'
import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from './contexts/Application' import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from './contexts/Application'
import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions' import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions'
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
import TokensContextProvider from './contexts/Tokens' import TokensContextProvider from './contexts/Tokens'
import BalancesContextProvider from './contexts/Balances'
import AllowancesContextProvider from './contexts/Allowances' import AllowancesContextProvider from './contexts/Allowances'
import AllBalancesContextProvider from './contexts/AllBalances'
import App from './pages/App' import App from './pages/App'
import ThemeProvider, { GlobalStyle } from './theme' import ThemeProvider, { GlobalStyle } from './theme'
import './i18n' import './i18n'
...@@ -38,9 +37,7 @@ function ContextProviders({ children }) { ...@@ -38,9 +37,7 @@ function ContextProviders({ children }) {
<TransactionContextProvider> <TransactionContextProvider>
<TokensContextProvider> <TokensContextProvider>
<BalancesContextProvider> <BalancesContextProvider>
<AllBalancesContextProvider> <AllowancesContextProvider>{children}</AllowancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider>
</AllBalancesContextProvider>
</BalancesContextProvider> </BalancesContextProvider>
</TokensContextProvider> </TokensContextProvider>
</TransactionContextProvider> </TransactionContextProvider>
...@@ -55,6 +52,7 @@ function Updaters() { ...@@ -55,6 +52,7 @@ function Updaters() {
<LocalStorageContextUpdater /> <LocalStorageContextUpdater />
<ApplicationContextUpdater /> <ApplicationContextUpdater />
<TransactionContextUpdater /> <TransactionContextUpdater />
<BalancesContextUpdater />
</> </>
) )
} }
......
...@@ -15,7 +15,6 @@ import { brokenTokens } from '../../constants' ...@@ -15,7 +15,6 @@ import { brokenTokens } from '../../constants'
import { amountFormatter, calculateGasMargin } from '../../utils' import { amountFormatter, calculateGasMargin } from '../../utils'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances' import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
import { useAddressAllowance } from '../../contexts/Allowances' import { useAddressAllowance } from '../../contexts/Allowances'
...@@ -567,8 +566,6 @@ export default function AddLiquidity({ params }) { ...@@ -567,8 +566,6 @@ export default function AddLiquidity({ params }) {
const isActive = active && account const isActive = active && account
const isValid = (inputError === null || outputError === null) && !showUnlock && !brokenTokenWarning const isValid = (inputError === null || outputError === null) && !showUnlock && !brokenTokenWarning
const allBalances = useFetchAllBalances()
return ( return (
<> <>
{isNewExchange ? ( {isNewExchange ? (
...@@ -585,7 +582,6 @@ export default function AddLiquidity({ params }) { ...@@ -585,7 +582,6 @@ export default function AddLiquidity({ params }) {
<CurrencyInputPanel <CurrencyInputPanel
title={t('deposit')} title={t('deposit')}
allBalances={allBalances}
extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))} extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))}
onValueChange={inputValue => { onValueChange={inputValue => {
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } }) dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } })
...@@ -613,7 +609,6 @@ export default function AddLiquidity({ params }) { ...@@ -613,7 +609,6 @@ export default function AddLiquidity({ params }) {
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('deposit')} title={t('deposit')}
allBalances={allBalances}
description={isNewExchange ? '' : outputValue ? `(${t('estimated')})` : ''} description={isNewExchange ? '' : outputValue ? `(${t('estimated')})` : ''}
extraText={ extraText={
outputBalance && decimals && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4))) outputBalance && decimals && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4)))
......
...@@ -14,7 +14,6 @@ import ArrowDown from '../../assets/svg/SVGArrowDown' ...@@ -14,7 +14,6 @@ import ArrowDown from '../../assets/svg/SVGArrowDown'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useAddressBalance } from '../../contexts/Balances' import { useAddressBalance } from '../../contexts/Balances'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { calculateGasMargin, amountFormatter } from '../../utils' import { calculateGasMargin, amountFormatter } from '../../utils'
// denominated in bips // denominated in bips
...@@ -334,13 +333,10 @@ export default function RemoveLiquidity({ params }) { ...@@ -334,13 +333,10 @@ export default function RemoveLiquidity({ params }) {
const marketRate = getMarketRate(exchangeETHBalance, exchangeTokenBalance, decimals) const marketRate = getMarketRate(exchangeETHBalance, exchangeTokenBalance, decimals)
const allBalances = useFetchAllBalances()
return ( return (
<> <>
<CurrencyInputPanel <CurrencyInputPanel
title={t('poolTokens')} title={t('poolTokens')}
allBalances={allBalances}
extraText={poolTokenBalance && formatBalance(amountFormatter(poolTokenBalance, 18, 4))} extraText={poolTokenBalance && formatBalance(amountFormatter(poolTokenBalance, 18, 4))}
extraTextClickHander={() => { extraTextClickHander={() => {
if (poolTokenBalance) { if (poolTokenBalance) {
...@@ -363,7 +359,6 @@ export default function RemoveLiquidity({ params }) { ...@@ -363,7 +359,6 @@ export default function RemoveLiquidity({ params }) {
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('output')} title={t('output')}
allBalances={allBalances}
description={!!(ethWithdrawn && tokenWithdrawn) ? `(${t('estimated')})` : ''} description={!!(ethWithdrawn && tokenWithdrawn) ? `(${t('estimated')})` : ''}
key="remove-liquidity-input" key="remove-liquidity-input"
renderInput={() => renderInput={() =>
......
import { getTokenReserves, getMarketDetails } from '@uniswap/sdk' import { getMarketDetails } from '@uniswap/sdk'
import { getMedian, getMean } from './math' import { getMedian, getMean } from './math'
const DAI = 'DAI' const DAI = 'DAI'
...@@ -7,37 +7,29 @@ const TUSD = 'TUSD' ...@@ -7,37 +7,29 @@ const TUSD = 'TUSD'
const USD_STABLECOINS = [DAI, USDC, TUSD] const USD_STABLECOINS = [DAI, USDC, TUSD]
const USD_STABLECOIN_ADDRESSES = [
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
'0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E'
]
function forEachStablecoin(runner) { function forEachStablecoin(runner) {
return USD_STABLECOINS.map((stablecoin, index) => runner(index, stablecoin)) return USD_STABLECOINS.map((stablecoin, index) => runner(index, stablecoin))
} }
export async function getUSDPrice(library) { export function getUSDPrice(reserves) {
return Promise.all(forEachStablecoin(i => getTokenReserves(USD_STABLECOIN_ADDRESSES[i], library))).then(reserves => { const marketDetails = forEachStablecoin(i => getMarketDetails(reserves[i], undefined))
const ethReserves = forEachStablecoin(i => reserves[i].ethReserve.amount) const ethPrices = forEachStablecoin(i => marketDetails[i].marketRate.rateInverted)
const marketDetails = forEachStablecoin(i => getMarketDetails(reserves[i], undefined))
const [median] = getMedian(ethPrices)
const ethPrices = forEachStablecoin(i => marketDetails[i].marketRate.rateInverted) const [mean] = getMean(ethPrices)
const [weightedMean] = getMean(
const [median, medianWeights] = getMedian(ethPrices) ethPrices,
const [mean, meanWeights] = getMean(ethPrices) forEachStablecoin(i => reserves[i].ethReserve.amount)
const [weightedMean, weightedMeanWeights] = getMean(ethPrices, ethReserves) )
const ethPrice = getMean([median, mean, weightedMean])[0] // const _stablecoinWeights = [
const _stablecoinWeights = [ // getMean([medianWeights[0], meanWeights[0], weightedMeanWeights[0]])[0],
getMean([medianWeights[0], meanWeights[0], weightedMeanWeights[0]])[0], // getMean([medianWeights[1], meanWeights[1], weightedMeanWeights[1]])[0],
getMean([medianWeights[1], meanWeights[1], weightedMeanWeights[1]])[0], // getMean([medianWeights[2], meanWeights[2], weightedMeanWeights[2]])[0]
getMean([medianWeights[2], meanWeights[2], weightedMeanWeights[2]])[0] // ]
] // const stablecoinWeights = forEachStablecoin((i, stablecoin) => ({
const stablecoinWeights = forEachStablecoin((i, stablecoin) => ({ // [stablecoin]: _stablecoinWeights[i]
[stablecoin]: _stablecoinWeights[i] // })).reduce((accumulator, currentValue) => ({ ...accumulator, ...currentValue }), {})
})).reduce((accumulator, currentValue) => ({ ...accumulator, ...currentValue }), {})
return getMean([median, mean, weightedMean])[0]
return [ethPrice, stablecoinWeights]
})
} }
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