diff --git a/package.json b/package.json index f91a7a6591..a68601c992 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,21 @@ "@reach/dialog": "^0.2.8", "@reach/tooltip": "^0.2.0", "@uniswap/sdk": "^1.0.0-beta.4", + "@web3-react/core": "^6.0.0-beta.15", + "@web3-react/injected-connector": "^6.0.0-beta.17", + "@web3-react/network-connector": "^6.0.0-beta.15", + "@web3-react/walletconnect-connector": "^6.0.0-beta.18", + "@web3-react/walletlink-connector": "^6.0.0-beta.15", "copy-to-clipboard": "^3.2.0", "escape-string-regexp": "^2.0.0", - "history": "^4.9.0", "ethers": "^4.0.36", + "history": "^4.9.0", "i18next": "^15.0.9", "i18next-browser-languagedetector": "^3.0.1", "i18next-xhr-backend": "^2.0.1", "jazzicon": "^1.5.0", "polished": "^3.3.2", + "qrcode.react": "^0.9.3", "react": "^16.8.6", "react-device-detect": "^1.6.2", "react-dom": "^16.8.6", @@ -25,10 +31,10 @@ "react-i18next": "^10.7.0", "react-router-dom": "^5.0.0", "react-scripts": "^3.0.1", - "react-spring": "^8.0.20", + "react-spring": "^8.0.27", "react-switch": "^5.0.1", - "styled-components": "^4.2.0", - "web3-react": "5.0.5" + "react-use-gesture": "^6.0.14", + "styled-components": "^4.2.0" }, "scripts": { "start": "react-scripts start", diff --git a/src/InjectedConnector.js b/src/InjectedConnector.js deleted file mode 100644 index 88745cbb23..0000000000 --- a/src/InjectedConnector.js +++ /dev/null @@ -1,100 +0,0 @@ -import { Connectors } from 'web3-react' -const { Connector, ErrorCodeMixin } = Connectors - -const InjectedConnectorErrorCodes = ['ETHEREUM_ACCESS_DENIED', 'NO_WEB3', 'UNLOCK_REQUIRED'] -export default class InjectedConnector extends ErrorCodeMixin(Connector, InjectedConnectorErrorCodes) { - constructor(args = {}) { - super(args) - this.runOnDeactivation = [] - - this.networkChangedHandler = this.networkChangedHandler.bind(this) - this.accountsChangedHandler = this.accountsChangedHandler.bind(this) - - const { ethereum } = window - if (ethereum && ethereum.isMetaMask) { - ethereum.autoRefreshOnNetworkChange = false - } - } - - async onActivation() { - const { ethereum, web3 } = window - - if (ethereum) { - await ethereum.enable().catch(error => { - const deniedAccessError = Error(error) - deniedAccessError.code = InjectedConnector.errorCodes.ETHEREUM_ACCESS_DENIED - throw deniedAccessError - }) - - // initialize event listeners - if (ethereum.on) { - ethereum.on('networkChanged', this.networkChangedHandler) - ethereum.on('accountsChanged', this.accountsChangedHandler) - - this.runOnDeactivation.push(() => { - if (ethereum.removeListener) { - ethereum.removeListener('networkChanged', this.networkChangedHandler) - ethereum.removeListener('accountsChanged', this.accountsChangedHandler) - } - }) - } - } else if (web3) { - console.warn('Your web3 provider is outdated, please upgrade to a modern provider.') - } else { - const noWeb3Error = Error('Your browser is not equipped with web3 capabilities.') - noWeb3Error.code = InjectedConnector.errorCodes.NO_WEB3 - throw noWeb3Error - } - } - - async getProvider() { - const { ethereum, web3 } = window - return ethereum || web3.currentProvider - } - - async getAccount(provider) { - const account = await super.getAccount(provider) - - if (account === null) { - const unlockRequiredError = Error('Ethereum account locked.') - unlockRequiredError.code = InjectedConnector.errorCodes.UNLOCK_REQUIRED - throw unlockRequiredError - } - - return account - } - - onDeactivation() { - this.runOnDeactivation.forEach(runner => runner()) - this.runOnDeactivation = [] - } - - // event handlers - networkChangedHandler(networkId) { - const networkIdNumber = Number(networkId) - - try { - super._validateNetworkId(networkIdNumber) - - super._web3ReactUpdateHandler({ - updateNetworkId: true, - networkId: networkIdNumber - }) - } catch (error) { - super._web3ReactErrorHandler(error) - } - } - - accountsChangedHandler(accounts) { - if (!accounts[0]) { - const unlockRequiredError = Error('Ethereum account locked.') - unlockRequiredError.code = InjectedConnector.errorCodes.UNLOCK_REQUIRED - super._web3ReactErrorHandler(unlockRequiredError) - } else { - super._web3ReactUpdateHandler({ - updateAccount: true, - account: accounts[0] - }) - } - } -} diff --git a/src/NetworkOnlyConnector.js b/src/NetworkOnlyConnector.js deleted file mode 100644 index bbec46d666..0000000000 --- a/src/NetworkOnlyConnector.js +++ /dev/null @@ -1,51 +0,0 @@ -import { ethers } from 'ethers' -import { Connectors } from 'web3-react' - -const { Connector } = Connectors - -function getFallbackProvider(providerURL) { - if (Number(process.env.REACT_APP_NETWORK_ID) === 1) { - const etherscan = new ethers.providers.EtherscanProvider() - const infura = new ethers.providers.JsonRpcProvider(providerURL) - - const providers = [infura, etherscan] - - return new ethers.providers.FallbackProvider(providers) - } else { - const infura = new ethers.providers.JsonRpcProvider(providerURL) - - const providers = [infura] - - return new ethers.providers.FallbackProvider(providers) - } -} - -export default class NetworkOnlyConnector extends Connector { - constructor(kwargs) { - const { providerURL, ...rest } = kwargs || {} - super(rest) - this.providerURL = providerURL - } - - async onActivation() { - if (!this.engine) { - const provider = getFallbackProvider(this.providerURL) - provider.polling = false - provider.pollingInterval = 300000 // 5 minutes - this.engine = provider - } - } - - async getNetworkId(provider) { - const networkId = await provider.getNetwork().then(network => network.chainId) - return super._validateNetworkId(networkId) - } - - async getProvider() { - return this.engine - } - - async getAccount() { - return null - } -} diff --git a/src/assets/images/arrow-right.svg b/src/assets/images/arrow-right.svg new file mode 100644 index 0000000000..89e10e639e --- /dev/null +++ b/src/assets/images/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/coinbaseWalletIcon.svg b/src/assets/images/coinbaseWalletIcon.svg new file mode 100644 index 0000000000..04e9f49b0c --- /dev/null +++ b/src/assets/images/coinbaseWalletIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/metamask.png b/src/assets/images/metamask.png new file mode 100644 index 0000000000..85714ea290 Binary files /dev/null and b/src/assets/images/metamask.png differ diff --git a/src/assets/images/trustWallet.png b/src/assets/images/trustWallet.png new file mode 100644 index 0000000000..bf939aac82 Binary files /dev/null and b/src/assets/images/trustWallet.png differ diff --git a/src/assets/images/walletConnectIcon.svg b/src/assets/images/walletConnectIcon.svg new file mode 100644 index 0000000000..f78a7b850d --- /dev/null +++ b/src/assets/images/walletConnectIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/x.svg b/src/assets/images/x.svg index 7d5875ca8b..1345e9bcea 100644 --- a/src/assets/images/x.svg +++ b/src/assets/images/x.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/components/WalletModal/Copy.js b/src/components/AccountDetails/Copy.js similarity index 100% rename from src/components/WalletModal/Copy.js rename to src/components/AccountDetails/Copy.js diff --git a/src/components/WalletModal/Transaction.js b/src/components/AccountDetails/Transaction.js similarity index 89% rename from src/components/WalletModal/Transaction.js rename to src/components/AccountDetails/Transaction.js index 8629eb6811..b147b16bec 100644 --- a/src/components/WalletModal/Transaction.js +++ b/src/components/AccountDetails/Transaction.js @@ -1,12 +1,12 @@ import React from 'react' import styled, { keyframes } from 'styled-components' -import { useWeb3Context } from 'web3-react' -import Copy from './Copy' +import { Check } from 'react-feather' +import { useWeb3React } from '../../hooks' import { getEtherscanLink } from '../../utils' import { Link, Spinner } from '../../theme' +import Copy from './Copy' import Circle from '../../assets/images/circle.svg' -import { Check } from 'react-feather' import { transparentize } from 'polished' @@ -75,17 +75,17 @@ const ButtonWrapper = styled.div` ` export default function Transaction({ hash, pending }) { - const { networkId } = useWeb3Context() + const { chainId } = useWeb3React() return ( - {hash} ↗ + {hash} ↗ {pending ? ( - + Pending @@ -94,7 +94,7 @@ export default function Transaction({ hash, pending }) { ) : ( - + Confirmed diff --git a/src/components/AccountDetails/index.js b/src/components/AccountDetails/index.js new file mode 100644 index 0000000000..991b210430 --- /dev/null +++ b/src/components/AccountDetails/index.js @@ -0,0 +1,327 @@ +import React from 'react' +import styled from 'styled-components' +import { useWeb3React } from '@web3-react/core' +import { isMobile } from 'react-device-detect' +import Copy from './Copy' +import Transaction from './Transaction' +import { SUPPORTED_WALLETS } from '../../constants' +import { ReactComponent as Close } from '../../assets/images/x.svg' +import { getEtherscanLink } from '../../utils' +import { injected, walletconnect, walletlink } from '../../connectors' +import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg' +import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg' +import Identicon from '../Identicon' + +import { Link } from '../../theme' + +const OptionButton = styled.div` + ${({ theme }) => theme.flexColumnNoWrap} + justify-content: center; + align-items: center; + border-radius: 20px; + border: 1px solid ${({ theme }) => theme.royalBlue}; + color: ${({ theme }) => theme.royalBlue}; + padding: 8px 24px; + + &:hover { + border: 1px solid ${({ theme }) => theme.malibuBlue}; + cursor: pointer; + } + + ${({ theme }) => theme.mediaWidth.upToMedium` + font-size: 12px; + `}; +` + +const HeaderRow = styled.div` + ${({ theme }) => theme.flexRowNoWrap}; + padding: 1.5rem 1.5rem; + font-weight: 500; + color: ${props => (props.color === 'blue' ? ({ theme }) => theme.royalBlue : 'inherit')}; + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 1rem; + `}; +` + +const UpperSection = styled.div` + position: relative; + background-color: ${({ theme }) => theme.concreteGray}; + + h5 { + margin: 0; + margin-bottom: 0.5rem; + font-size: 1rem; + font-weight: 400; + } + + h5:last-child { + margin-bottom: 0px; + } + + h4 { + margin-top: 0; + font-weight: 500; + } +` + +const InfoCard = styled.div` + padding: 1rem; + border: 1px solid ${({ theme }) => theme.placeholderGray}; + border-radius: 20px; +` + +const AccountGroupingRow = styled.div` + ${({ theme }) => theme.flexRowNoWrap}; + justify-content: space-between; + align-items: center; + font-weight: 500; + color: ${({ theme }) => theme.textColor}; + + div { + ${({ theme }) => theme.flexRowNoWrap} + align-items: center; + } + + &:first-of-type { + margin-bottom: 20px; + } +` + +const AccountSection = styled.div` + background-color: ${({ theme }) => theme.concreteGray}; + padding: 0rem 1.5rem; + ${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1rem 1rem;`}; +` + +const YourAccount = styled.div` + h5 { + margin: 0 0 1rem 0; + font-weight: 400; + } + + h4 { + margin: 0; + font-weight: 500; + } +` + +const GreenCircle = styled.div` + ${({ theme }) => theme.flexRowNoWrap} + justify-content: center; + align-items: center; + + &:first-child { + height: 8px; + width: 8px; + margin-left: 12px; + margin-right: 2px; + margin-top: -6px; + background-color: ${({ theme }) => theme.connectedGreen}; + border-radius: 50%; + } +` + +const GreenText = styled.div` + color: ${({ theme }) => theme.connectedGreen}; +` + +const LowerSection = styled.div` + ${({ theme }) => theme.flexColumnNoWrap} + padding: 2rem; + flex-grow: 1; + overflow: auto; + + h5 { + margin: 0; + font-weight: 400; + color: ${({ theme }) => theme.doveGray}; + } +` + +const AccountControl = styled.div` + ${({ theme }) => theme.flexRowNoWrap}; + align-items: center; + min-width: 0; + + font-weight: ${({ hasENS, isENS }) => (hasENS ? (isENS ? 500 : 400) : 500)}; + font-size: ${({ hasENS, isENS }) => (hasENS ? (isENS ? '1rem' : '0.8rem') : '1rem')}; + + a:hover { + text-decoration: underline; + } + + a { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +` + +const ConnectButtonRow = styled.div` + ${({ theme }) => theme.flexRowNoWrap} + align-items: center; + justify-content: center; + margin: 30px; +` + +const StyledLink = styled(Link)` + color: ${({ hasENS, isENS, theme }) => (hasENS ? (isENS ? theme.royalBlue : theme.doveGray) : theme.royalBlue)}; +` + +const CloseIcon = styled.div` + position: absolute; + right: 1rem; + top: 14px; + &:hover { + cursor: pointer; + opacity: 0.6; + } +` + +const CloseColor = styled(Close)` + path { + stroke: ${({ theme }) => theme.chaliceGray}; + } +` + +const WalletName = styled.div` + padding-left: 0.5rem; + width: initial; +` + +const IconWrapper = styled.div` + ${({ theme }) => theme.flexColumnNoWrap}; + align-items: center; + justify-content: center; + & > img, + span { + height: ${({ size }) => (size ? size + 'px' : '32px')}; + width: ${({ size }) => (size ? size + 'px' : '32px')}; + } + ${({ theme }) => theme.mediaWidth.upToMedium` + align-items: flex-end; + `}; +` + +const TransactionListWrapper = styled.div` + ${({ theme }) => theme.flexColumnNoWrap}; +` + +function renderTransactions(transactions, pending) { + return ( + + {transactions.map((hash, i) => { + return + })} + + ) +} + +export default function AccountDetails({ + toggleWalletModal, + pendingTransactions, + confirmedTransactions, + ENSName, + openOptions +}) { + const { chainId, account, connector } = useWeb3React() + + function formatConnectorName() { + const isMetaMask = window.ethereum && window.ethereum.isMetaMask + const name = Object.keys(SUPPORTED_WALLETS) + .filter( + k => + SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK')) + ) + .map(k => SUPPORTED_WALLETS[k].name)[0] + + return {name} + } + + function getStatusIcon() { + if (connector === injected) { + return ( + + {formatConnectorName()} + + ) + } else if (connector === walletconnect) { + return ( + + {''} {formatConnectorName()} + + ) + } else if (connector === walletlink) { + return ( + + {''} {formatConnectorName()} + + ) + } + } + + return ( + <> + + + + + Account + + + + + {getStatusIcon()} + + +
+ + + + + {ENSName ? ( + + + {ENSName} ↗{' '} + + + + ) : ( + + + {account} ↗{' '} + + + + )} + + + + {!isMobile && ( + + { + openOptions() + }} + > + Connect to a different wallet + + + )} + + + {!!pendingTransactions.length || !!confirmedTransactions.length ? ( + +
Recent Transactions
+ {renderTransactions(pendingTransactions, true)} + {renderTransactions(confirmedTransactions, false)} +
+ ) : ( + +
Your transactions will appear here...
+
+ )} + + ) +} diff --git a/src/components/AddressInputPanel/index.js b/src/components/AddressInputPanel/index.js index e55bfcbc40..76cb6c5c57 100644 --- a/src/components/AddressInputPanel/index.js +++ b/src/components/AddressInputPanel/index.js @@ -1,11 +1,10 @@ import React, { useState, useEffect } from 'react' import styled from 'styled-components' import { useTranslation } from 'react-i18next' -import { useWeb3Context } from 'web3-react' import { transparentize } from 'polished' import { isAddress } from '../../utils' -import { useDebounce } from '../../hooks' +import { useWeb3React, useDebounce } from '../../hooks' const InputPanel = styled.div` ${({ theme }) => theme.flexColumnNoWrap} @@ -73,7 +72,7 @@ const Input = styled.input` export default function AddressInputPanel({ title, initialInput = '', onChange = () => {}, onError = () => {} }) { const { t } = useTranslation() - const { library } = useWeb3Context() + const { library } = useWeb3React() const [input, setInput] = useState(initialInput.address ? initialInput.address : '') diff --git a/src/components/CurrencyInputPanel/index.js b/src/components/CurrencyInputPanel/index.js index 3256ff98d8..b29f5abd89 100644 --- a/src/components/CurrencyInputPanel/index.js +++ b/src/components/CurrencyInputPanel/index.js @@ -3,7 +3,6 @@ import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { ethers } from 'ethers' import { BigNumber } from '@uniswap/sdk' -import { useWeb3Context } from 'web3-react' import styled from 'styled-components' import escapeStringRegex from 'escape-string-regexp' import { darken } from 'polished' @@ -12,7 +11,7 @@ import '@reach/tooltip/styles.css' import { isMobile } from 'react-device-detect' import { BorderlessInput } from '../../theme' -import { useTokenContract } from '../../hooks' +import { useWeb3React, useTokenContract } from '../../hooks' import { isAddress, calculateGasMargin, formatToUsd, formatTokenBalance, formatEthBalance } from '../../utils' import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg' import Modal from '../Modal' @@ -225,7 +224,10 @@ const TokenModalRow = styled.div` background-color: ${({ theme }) => theme.tokenRowHover}; } - ${({ theme }) => theme.mediaWidth.upToMedium`padding: 0.8rem 1rem;`} + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 0.8rem 1rem; + padding-right: 2rem; + `} ` const TokenRowLeft = styled.div` @@ -297,7 +299,7 @@ export default function CurrencyInputPanel({ const allTokens = useAllTokenDetails() - const { account } = useWeb3Context() + const { account } = useWeb3React() const userTokenBalance = useAddressBalance(account, selectedTokenAddress) @@ -426,7 +428,6 @@ export default function CurrencyInputPanel({ {!disableTokenSelect && ( { setModalIsOpen(false) }} @@ -446,7 +447,7 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) const allTokens = useAllTokenDetails() - const { account } = useWeb3Context() + const { account } = useWeb3React() // BigNumber.js instance const ethPrice = useUSDPrice() @@ -606,6 +607,7 @@ function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, allBalances }) isOpen={isOpen} onDismiss={clearInputAndDismiss} minHeight={60} + maxHeight={50} initialFocusRef={isMobile ? undefined : inputRef} > diff --git a/src/components/ExchangePage/index.jsx b/src/components/ExchangePage/index.jsx index e8ec176baa..12d9b51de1 100644 --- a/src/components/ExchangePage/index.jsx +++ b/src/components/ExchangePage/index.jsx @@ -1,14 +1,12 @@ import React, { useState, useReducer, useEffect } from 'react' import ReactGA from 'react-ga' import { createBrowserHistory } from 'history' - -import { useTranslation } from 'react-i18next' -import { useWeb3Context } from 'web3-react' - import { ethers } from 'ethers' import styled from 'styled-components' +import { useTranslation } from 'react-i18next' import { Button } from '../../theme' +import { useWeb3React } from '../../hooks' import CurrencyInputPanel from '../CurrencyInputPanel' import AddressInputPanel from '../AddressInputPanel' import OversizedPanel from '../OversizedPanel' @@ -21,6 +19,7 @@ import { useTransactionAdder } from '../../contexts/Transactions' import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances' import { useFetchAllBalances } from '../../contexts/AllBalances' import { useAddressAllowance } from '../../contexts/Allowances' +import { useWalletModalToggle } from '../../contexts/Application' const INPUT = 0 const OUTPUT = 1 @@ -246,7 +245,7 @@ function getMarketRate( export default function ExchangePage({ initialCurrency, sending = false, params }) { const { t } = useTranslation() - const { account } = useWeb3Context() + const { account, error } = useWeb3React() const addTransaction = useTransactionAdder() @@ -294,7 +293,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState - const [recipient, setRecipient] = useState({ address: initialRecipient(), name: '' }) + const [recipient, setRecipient] = useState({ + address: initialRecipient(), + name: '' + }) const [recipientError, setRecipientError] = useState() // get swap type from the currency types @@ -416,7 +418,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params throw Error() } - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue }) + dispatchSwapState({ + type: 'UPDATE_DEPENDENT', + payload: calculatedDependentValue + }) } catch { setIndependentError(t('insufficientLiquidity')) } @@ -439,7 +444,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params throw Error() } - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue }) + dispatchSwapState({ + type: 'UPDATE_DEPENDENT', + payload: calculatedDependentValue + }) } catch { setIndependentError(t('insufficientLiquidity')) } @@ -469,7 +477,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params if (calculatedDependentValue.lte(ethers.constants.Zero)) { throw Error() } - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue }) + dispatchSwapState({ + type: 'UPDATE_DEPENDENT', + payload: calculatedDependentValue + }) } else { const intermediateValue = calculateEtherTokenInputFromOutput(amount, reserveETHSecond, reserveTokenSecond) if (intermediateValue.lte(ethers.constants.Zero)) { @@ -483,7 +494,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params if (calculatedDependentValue.lte(ethers.constants.Zero)) { throw Error() } - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: calculatedDependentValue }) + dispatchSwapState({ + type: 'UPDATE_DEPENDENT', + payload: calculatedDependentValue + }) } } catch { setIndependentError(t('insufficientLiquidity')) @@ -621,7 +635,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params } const estimatedGasLimit = await estimate(...args, { value }) - method(...args, { value, gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN) }).then(response => { + method(...args, { + value, + gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN) + }).then(response => { addTransaction(response) }) } @@ -630,6 +647,8 @@ export default function ExchangePage({ initialCurrency, sending = false, params const allBalances = useFetchAllBalances() + const toggleWalletModal = useWalletModalToggle() + return ( <> { - dispatchSwapState({ type: 'SELECT_CURRENCY', payload: { currency: inputCurrency, field: INPUT } }) + dispatchSwapState({ + type: 'SELECT_CURRENCY', + payload: { currency: inputCurrency, field: INPUT } + }) }} onValueChange={inputValue => { - dispatchSwapState({ type: 'UPDATE_INDEPENDENT', payload: { value: inputValue, field: INPUT } }) + dispatchSwapState({ + type: 'UPDATE_INDEPENDENT', + payload: { value: inputValue, field: INPUT } + }) }} showUnlock={showUnlock} selectedTokens={[inputCurrency, outputCurrency]} @@ -678,10 +706,16 @@ export default function ExchangePage({ initialCurrency, sending = false, params description={outputValueFormatted && independentField === INPUT ? estimatedText : ''} extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)} onCurrencySelected={outputCurrency => { - dispatchSwapState({ type: 'SELECT_CURRENCY', payload: { currency: outputCurrency, field: OUTPUT } }) + dispatchSwapState({ + type: 'SELECT_CURRENCY', + payload: { currency: outputCurrency, field: OUTPUT } + }) }} onValueChange={outputValue => { - dispatchSwapState({ type: 'UPDATE_INDEPENDENT', payload: { value: outputValue, field: OUTPUT } }) + dispatchSwapState({ + type: 'UPDATE_INDEPENDENT', + payload: { value: outputValue, field: OUTPUT } + }) }} selectedTokens={[inputCurrency, outputCurrency]} selectedTokenAddress={outputCurrency} @@ -753,11 +787,14 @@ export default function ExchangePage({ initialCurrency, sending = false, params />