Add useSwapsEthToken hook and utilize wherever we need the ETH 'token' for swaps (#9537)

* Add useSwapsEthToken hook and utilize wherever we need the ETH 'token' for swaps
* Remove rawEthTokens param from useTokensToSearch calls

Co-authored-by: Mark Stacey <markjstacey@gmail.com>
Co-authored-by: Erik Marks <rekmarks@protonmail.com>
feature/default_network_editable
Dan J Miller 4 years ago committed by GitHub
parent d8818a47c4
commit 436b01387c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      ui/app/components/app/wallet-overview/eth-overview.js
  2. 4
      ui/app/ducks/swaps/swaps.js
  3. 50
      ui/app/hooks/useSwapsEthToken.js
  4. 15
      ui/app/hooks/useTokensToSearch.js
  5. 6
      ui/app/pages/swaps/build-quote/build-quote.js
  6. 15
      ui/app/pages/swaps/intro-popup/intro-popup.js
  7. 11
      ui/app/pages/swaps/view-quote/view-quote.js

@ -8,17 +8,16 @@ import Identicon from '../../ui/identicon'
import { I18nContext } from '../../../contexts/i18n' import { I18nContext } from '../../../contexts/i18n'
import { SEND_ROUTE, BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes' import { SEND_ROUTE, BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes'
import { useMetricEvent, useNewMetricEvent } from '../../../hooks/useMetricEvent' import { useMetricEvent, useNewMetricEvent } from '../../../hooks/useMetricEvent'
import { useSwapsEthToken } from '../../../hooks/useSwapsEthToken'
import Tooltip from '../../ui/tooltip' import Tooltip from '../../ui/tooltip'
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common' import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'
import { showModal } from '../../../store/actions' import { showModal } from '../../../store/actions'
import { isBalanceCached, getSelectedAccount, getShouldShowFiat, getCurrentNetworkId, getCurrentKeyring } from '../../../selectors/selectors' import { isBalanceCached, getSelectedAccount, getShouldShowFiat, getCurrentNetworkId, getCurrentKeyring } from '../../../selectors/selectors'
import { getValueFromWeiHex } from '../../../helpers/utils/conversions.util'
import SwapIcon from '../../ui/icon/swap-icon.component' import SwapIcon from '../../ui/icon/swap-icon.component'
import BuyIcon from '../../ui/icon/overview-buy-icon.component' import BuyIcon from '../../ui/icon/overview-buy-icon.component'
import SendIcon from '../../ui/icon/overview-send-icon.component' import SendIcon from '../../ui/icon/overview-send-icon.component'
import { getSwapsFeatureLiveness, setSwapsFromToken } from '../../../ducks/swaps/swaps' import { getSwapsFeatureLiveness, setSwapsFromToken } from '../../../ducks/swaps/swaps'
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../helpers/constants/swaps'
import IconButton from '../../ui/icon-button' import IconButton from '../../ui/icon-button'
import { MAINNET_NETWORK_ID } from '../../../../../app/scripts/controllers/network/enums' import { MAINNET_NETWORK_ID } from '../../../../../app/scripts/controllers/network/enums'
import WalletOverview from './wallet-overview' import WalletOverview from './wallet-overview'
@ -50,6 +49,7 @@ const EthOverview = ({ className }) => {
const networkId = useSelector(getCurrentNetworkId) const networkId = useSelector(getCurrentNetworkId)
const enteredSwapsEvent = useNewMetricEvent({ event: 'Swaps Opened', properties: { source: 'Main View', active_currency: 'ETH' }, category: 'swaps' }) const enteredSwapsEvent = useNewMetricEvent({ event: 'Swaps Opened', properties: { source: 'Main View', active_currency: 'ETH' }, category: 'swaps' })
const swapsEnabled = useSelector(getSwapsFeatureLiveness) const swapsEnabled = useSelector(getSwapsFeatureLiveness)
const swapsEthToken = useSwapsEthToken()
return ( return (
<WalletOverview <WalletOverview
@ -118,11 +118,7 @@ const EthOverview = ({ className }) => {
onClick={() => { onClick={() => {
if (networkId === MAINNET_NETWORK_ID) { if (networkId === MAINNET_NETWORK_ID) {
enteredSwapsEvent() enteredSwapsEvent()
dispatch(setSwapsFromToken({ dispatch(setSwapsFromToken(swapsEthToken))
...ETH_SWAPS_TOKEN_OBJECT,
balance,
string: getValueFromWeiHex({ value: balance, numberOfDecimals: 4, toDenomination: 'ETH' }),
}))
if (usingHardwareWallet) { if (usingHardwareWallet) {
global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE) global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE)
} else { } else {

@ -23,7 +23,7 @@ import {
import { AWAITING_SWAP_ROUTE, BUILD_QUOTE_ROUTE, LOADING_QUOTES_ROUTE, SWAPS_ERROR_ROUTE, SWAPS_MAINTENANCE_ROUTE } from '../../helpers/constants/routes' import { AWAITING_SWAP_ROUTE, BUILD_QUOTE_ROUTE, LOADING_QUOTES_ROUTE, SWAPS_ERROR_ROUTE, SWAPS_MAINTENANCE_ROUTE } from '../../helpers/constants/routes'
import { fetchSwapsFeatureLiveness } from '../../pages/swaps/swaps.util' import { fetchSwapsFeatureLiveness } from '../../pages/swaps/swaps.util'
import { calcGasTotal } from '../../pages/send/send.utils' import { calcGasTotal } from '../../pages/send/send.utils'
import { decimalToHex, getValueFromWeiHex, hexMax, decGWEIToHexWEI } from '../../helpers/utils/conversions.util' import { decimalToHex, getValueFromWeiHex, hexMax, decGWEIToHexWEI, hexToDecimal } from '../../helpers/utils/conversions.util'
import { calcTokenAmount } from '../../helpers/utils/token-util' import { calcTokenAmount } from '../../helpers/utils/token-util'
import { import {
getFastPriceEstimateInHexWEI, getFastPriceEstimateInHexWEI,
@ -274,7 +274,7 @@ export const fetchQuotesAndSetQuoteState = (history, inputValue, maxSlippage, me
{ {
...ETH_SWAPS_TOKEN_OBJECT, ...ETH_SWAPS_TOKEN_OBJECT,
string: getValueFromWeiHex({ value: selectedAccount.balance, numberOfDecimals: 4, toDenomination: 'ETH' }), string: getValueFromWeiHex({ value: selectedAccount.balance, numberOfDecimals: 4, toDenomination: 'ETH' }),
balance: selectedAccount.balance, balance: hexToDecimal(selectedAccount.balance),
} : } :
fetchParams?.metaData?.sourceTokenInfo fetchParams?.metaData?.sourceTokenInfo
const selectedFromToken = getFromToken(state) || fetchParamsFromToken || {} const selectedFromToken = getFromToken(state) || fetchParamsFromToken || {}

@ -0,0 +1,50 @@
import { useSelector } from 'react-redux'
import { getSelectedAccount } from '../selectors'
import { ETH_SWAPS_TOKEN_OBJECT } from '../helpers/constants/swaps'
import { getValueFromWeiHex, hexToDecimal } from '../helpers/utils/conversions.util'
/**
* @typedef {Object} SwapsEthToken
* @property {string} symbol - The symbol for ETH, namely "ETH"
* @property {string} name - The name of the ETH currency, "Ether"
* @property {string} address - A substitute address for the metaswap-api to
* recognize the ETH token
* @property {string} decimals - The number of ETH decimals, i.e. 18
* @property {string} balance - The user's ETH balance in decimal wei, with a
* precision of 4 decimal places
* @property {string} string - The user's ETH balance in decimal ETH
*/
/**
* Swaps related code uses token objects for various purposes. These objects
* always have the following properties: `symbol`, `name`, `address`, and
* `decimals`.
*
* When available for the current account, the objects can have `balance` and
* `string` properties.
* `balance` is the users token balance in decimal values, denominated in the
* minimal token units (according to its decimals).
* `string` is the token balance in a readable format, ready for rendering.
*
* Swaps treats ETH as a token, and we use the ETH_SWAPS_TOKEN_OBJECT constant
* to set the standard properties for the token. The useSwapsEthToken hook
* extends that object with `balance` and `balance` values of the same type as
* in regular ERC-20 token objects, per the above description.
*
* @returns {SwapsEthToken} The token object representation of the currently
* selected account's ETH balance, as expected by the Swaps API.
*/
export function useSwapsEthToken () {
const selectedAccount = useSelector(getSelectedAccount)
const { balance } = selectedAccount
return {
...ETH_SWAPS_TOKEN_OBJECT,
balance: hexToDecimal(balance),
string: getValueFromWeiHex({
value: balance,
numberOfDecimals: 4,
toDenomination: 'ETH',
}),
}
}

@ -3,12 +3,11 @@ import { useSelector } from 'react-redux'
import contractMap from 'eth-contract-metadata' import contractMap from 'eth-contract-metadata'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { isEqual, shuffle } from 'lodash' import { isEqual, shuffle } from 'lodash'
import { getValueFromWeiHex } from '../helpers/utils/conversions.util'
import { checksumAddress } from '../helpers/utils/util' import { checksumAddress } from '../helpers/utils/util'
import { getTokenFiatAmount } from '../helpers/utils/token-util' import { getTokenFiatAmount } from '../helpers/utils/token-util'
import { getTokenExchangeRates, getConversionRate, getCurrentCurrency } from '../selectors' import { getTokenExchangeRates, getConversionRate, getCurrentCurrency } from '../selectors'
import { getSwapsTokens } from '../ducks/swaps/swaps' import { getSwapsTokens } from '../ducks/swaps/swaps'
import { ETH_SWAPS_TOKEN_OBJECT } from '../helpers/constants/swaps' import { useSwapsEthToken } from './useSwapsEthToken'
import { useEqualityCheck } from './useEqualityCheck' import { useEqualityCheck } from './useEqualityCheck'
const tokenList = shuffle(Object.entries(contractMap) const tokenList = shuffle(Object.entries(contractMap)
@ -50,7 +49,13 @@ export function getRenderableTokenData (token, contractExchangeRates, conversion
} }
} }
export function useTokensToSearch ({ providedTokens, rawEthBalance, usersTokens = [], topTokens = {}, onlyEth, singleToken }) { export function useTokensToSearch ({
providedTokens,
usersTokens = [],
topTokens = {},
onlyEth,
singleToken,
}) {
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual) const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual)
const conversionRate = useSelector(getConversionRate) const conversionRate = useSelector(getConversionRate)
const currentCurrency = useSelector(getCurrentCurrency) const currentCurrency = useSelector(getCurrentCurrency)
@ -58,9 +63,9 @@ export function useTokensToSearch ({ providedTokens, rawEthBalance, usersTokens
const memoizedTopTokens = useEqualityCheck(topTokens) const memoizedTopTokens = useEqualityCheck(topTokens)
const memoizedUsersToken = useEqualityCheck(usersTokens) const memoizedUsersToken = useEqualityCheck(usersTokens)
const decEthBalance = getValueFromWeiHex({ value: rawEthBalance, numberOfDecimals: 4, toDenomination: 'ETH' }) const swapsEthToken = useSwapsEthToken()
const [ethToken] = useState(() => getRenderableTokenData( const [ethToken] = useState(() => getRenderableTokenData(
{ ...ETH_SWAPS_TOKEN_OBJECT, balance: rawEthBalance, string: decEthBalance }, swapsEthToken,
tokenConversionRates, tokenConversionRates,
conversionRate, conversionRate,
currentCurrency, currentCurrency,

@ -7,6 +7,7 @@ import { useHistory } from 'react-router-dom'
import { MetaMetricsContext } from '../../../contexts/metametrics.new' import { MetaMetricsContext } from '../../../contexts/metametrics.new'
import { useTokensToSearch } from '../../../hooks/useTokensToSearch' import { useTokensToSearch } from '../../../hooks/useTokensToSearch'
import { useEqualityCheck } from '../../../hooks/useEqualityCheck' import { useEqualityCheck } from '../../../hooks/useEqualityCheck'
import { useSwapsEthToken } from '../../../hooks/useSwapsEthToken'
import { I18nContext } from '../../../contexts/i18n' import { I18nContext } from '../../../contexts/i18n'
import DropdownInputPair from '../dropdown-input-pair' import DropdownInputPair from '../dropdown-input-pair'
import DropdownSearchList from '../dropdown-search-list' import DropdownSearchList from '../dropdown-search-list'
@ -60,8 +61,9 @@ export default function BuildQuote ({
const topAssets = useSelector(getTopAssets) const topAssets = useSelector(getTopAssets)
const fromToken = useSelector(getFromToken) const fromToken = useSelector(getFromToken)
const toToken = useSelector(getToToken) || destinationTokenInfo const toToken = useSelector(getToToken) || destinationTokenInfo
const swapsEthToken = useSwapsEthToken()
const fetchParamsFromToken = sourceTokenInfo?.symbol === 'ETH' const fetchParamsFromToken = sourceTokenInfo?.symbol === 'ETH'
? { ...ETH_SWAPS_TOKEN_OBJECT, string: getValueFromWeiHex({ value: ethBalance, numberOfDecimals: 4, toDenomination: 'ETH' }), balance: ethBalance } ? swapsEthToken
: sourceTokenInfo : sourceTokenInfo
const { loading, tokensWithBalances } = useTokenTracker(tokens) const { loading, tokensWithBalances } = useTokenTracker(tokens)
@ -77,14 +79,12 @@ export default function BuildQuote ({
const selectedFromToken = useTokensToSearch({ const selectedFromToken = useTokensToSearch({
providedTokens: fromToken || fetchParamsFromToken ? [fromToken || fetchParamsFromToken] : [], providedTokens: fromToken || fetchParamsFromToken ? [fromToken || fetchParamsFromToken] : [],
rawEthBalance: ethBalance,
usersTokens: memoizedUsersTokens, usersTokens: memoizedUsersTokens,
onlyEth: (fromToken || fetchParamsFromToken)?.symbol === 'ETH', onlyEth: (fromToken || fetchParamsFromToken)?.symbol === 'ETH',
singleToken: true, singleToken: true,
})[0] })[0]
const tokensToSearch = useTokensToSearch({ const tokensToSearch = useTokensToSearch({
rawEthBalance: ethBalance,
usersTokens: memoizedUsersTokens, usersTokens: memoizedUsersTokens,
topTokens: topAssets, topTokens: topAssets,
}) })

@ -1,27 +1,24 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { getValueFromWeiHex } from '../../../helpers/utils/conversions.util'
import { setSwapsFromToken } from '../../../ducks/swaps/swaps' import { setSwapsFromToken } from '../../../ducks/swaps/swaps'
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../helpers/constants/swaps'
import { I18nContext } from '../../../contexts/i18n' import { I18nContext } from '../../../contexts/i18n'
import { BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes' import { BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes'
import { useNewMetricEvent } from '../../../hooks/useMetricEvent' import { useNewMetricEvent } from '../../../hooks/useMetricEvent'
import { useSwapsEthToken } from '../../../hooks/useSwapsEthToken'
import Button from '../../../components/ui/button' import Button from '../../../components/ui/button'
import Popover from '../../../components/ui/popover' import Popover from '../../../components/ui/popover'
import { getSelectedAccount } from '../../../selectors/selectors'
export default function IntroPopup ({ onClose }) { export default function IntroPopup ({ onClose }) {
const dispatch = useDispatch(useDispatch) const dispatch = useDispatch(useDispatch)
const history = useHistory() const history = useHistory()
const t = useContext(I18nContext) const t = useContext(I18nContext)
const selectedAccount = useSelector(getSelectedAccount)
const { balance } = selectedAccount
const enteredSwapsEvent = useNewMetricEvent({ event: 'Swaps Opened', properties: { source: 'Intro popup', active_currency: 'ETH' }, category: 'swaps' }) const enteredSwapsEvent = useNewMetricEvent({ event: 'Swaps Opened', properties: { source: 'Intro popup', active_currency: 'ETH' }, category: 'swaps' })
const blogPostVisitedEvent = useNewMetricEvent({ event: 'Blog Post Visited ', category: 'swaps' }) const blogPostVisitedEvent = useNewMetricEvent({ event: 'Blog Post Visited ', category: 'swaps' })
const contractAuditVisitedEvent = useNewMetricEvent({ event: 'Contract Audit Visited', category: 'swaps' }) const contractAuditVisitedEvent = useNewMetricEvent({ event: 'Contract Audit Visited', category: 'swaps' })
const productOverviewDismissedEvent = useNewMetricEvent({ event: 'Product Overview Dismissed', category: 'swaps' }) const productOverviewDismissedEvent = useNewMetricEvent({ event: 'Product Overview Dismissed', category: 'swaps' })
const swapsEthToken = useSwapsEthToken()
return ( return (
<div className="intro-popup"> <div className="intro-popup">
@ -41,11 +38,7 @@ export default function IntroPopup ({ onClose }) {
onClick={() => { onClick={() => {
onClose() onClose()
enteredSwapsEvent() enteredSwapsEvent()
dispatch(setSwapsFromToken({ dispatch(setSwapsFromToken(swapsEthToken))
...ETH_SWAPS_TOKEN_OBJECT,
balance,
string: getValueFromWeiHex({ value: balance, numberOfDecimals: 4, toDenomination: 'ETH' }),
}))
history.push(BUILD_QUOTE_ROUTE) history.push(BUILD_QUOTE_ROUTE)
}} }}
> >

@ -8,6 +8,7 @@ import { I18nContext } from '../../../contexts/i18n'
import SelectQuotePopover from '../select-quote-popover' import SelectQuotePopover from '../select-quote-popover'
import { useEqualityCheck } from '../../../hooks/useEqualityCheck' import { useEqualityCheck } from '../../../hooks/useEqualityCheck'
import { useNewMetricEvent } from '../../../hooks/useMetricEvent' import { useNewMetricEvent } from '../../../hooks/useMetricEvent'
import { useSwapsEthToken } from '../../../hooks/useSwapsEthToken'
import { MetaMetricsContext } from '../../../contexts/metametrics.new' import { MetaMetricsContext } from '../../../contexts/metametrics.new'
import FeeCard from '../fee-card' import FeeCard from '../fee-card'
import { setCustomGasLimit } from '../../../ducks/gas/gas.duck' import { setCustomGasLimit } from '../../../ducks/gas/gas.duck'
@ -68,10 +69,7 @@ import { getCustomTxParamsData } from '../../confirm-approve/confirm-approve.uti
import ActionableMessage from '../actionable-message' import ActionableMessage from '../actionable-message'
import { quotesToRenderableData, getRenderableGasFeesForQuote } from '../swaps.util' import { quotesToRenderableData, getRenderableGasFeesForQuote } from '../swaps.util'
import { useTokenTracker } from '../../../hooks/useTokenTracker' import { useTokenTracker } from '../../../hooks/useTokenTracker'
import { import { QUOTES_EXPIRED_ERROR } from '../../../helpers/constants/swaps'
ETH_SWAPS_TOKEN_OBJECT,
QUOTES_EXPIRED_ERROR,
} from '../../../helpers/constants/swaps'
import CountdownTimer from '../countdown-timer' import CountdownTimer from '../countdown-timer'
import SwapsFooter from '../swaps-footer' import SwapsFooter from '../swaps-footer'
@ -143,8 +141,9 @@ export default function ViewQuote () {
const gasTotalInWeiHex = calcGasTotal(maxGasLimit, gasPrice) const gasTotalInWeiHex = calcGasTotal(maxGasLimit, gasPrice)
const { tokensWithBalances } = useTokenTracker(swapsTokens) const { tokensWithBalances } = useTokenTracker(swapsTokens)
const balanceToken = fetchParamsSourceToken === ETH_SWAPS_TOKEN_OBJECT.address const swapsEthToken = useSwapsEthToken()
? { ...ETH_SWAPS_TOKEN_OBJECT, balance: ethBalance } const balanceToken = fetchParamsSourceToken === swapsEthToken.address
? swapsEthToken
: tokensWithBalances.find(({ address }) => address === fetchParamsSourceToken) : tokensWithBalances.find(({ address }) => address === fetchParamsSourceToken)
const selectedFromToken = balanceToken || usedQuote.sourceTokenInfo const selectedFromToken = balanceToken || usedQuote.sourceTokenInfo

Loading…
Cancel
Save