From 71376cf7dbb9b1c384d12e69bcf5978e8abf62e6 Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Mon, 15 Apr 2019 12:56:40 -0400 Subject: [PATCH] set up eslint and prettier; run prettier --- .prettierrc | 5 + package.json | 13 +- src/components/AddressInputPanel/index.js | 42 +- .../ContextualInfo/contextual-info.scss | 2 +- src/components/ContextualInfo/index.js | 55 +- .../CurrencyInputPanel/currency-panel.scss | 48 +- src/components/CurrencyInputPanel/index.js | 312 +++---- src/components/Header/header.scss | 16 +- src/components/Header/index.js | 167 ++-- src/components/Logo/index.js | 10 +- src/components/Logo/logo.scss | 2 +- src/components/Modal/index.js | 22 +- src/components/Modal/modal.scss | 3 +- .../NavigationTabs/beta-message.scss | 12 +- src/components/NavigationTabs/index.js | 71 +- src/components/OversizedPanel/index.js | 10 +- .../OversizedPanel/oversized-panel.scss | 8 +- src/components/QrCode/index.js | 93 +- src/components/QrCode/qr-code.scss | 1 - src/components/Tab/index.js | 35 +- src/components/Tab/tab.scss | 4 +- src/components/TokenLogo/index.js | 54 +- src/components/Web3Status/index.js | 106 ++- src/components/Web3Status/web3-status.scss | 20 +- src/constants/actionTypes.js | 80 +- src/constants/currencyInputErrorTypes.js | 2 +- src/ducks/addresses.js | 110 ++- src/ducks/app.js | 12 +- src/ducks/index.js | 14 +- src/ducks/pending.js | 18 +- src/ducks/web3connect.js | 549 ++++++------ .../fuse/bitap/bitap_matched_indices.js | 6 +- .../fuse/bitap/bitap_pattern_alphabet.js | 2 +- src/helpers/fuse/bitap/bitap_regex_search.js | 2 +- src/helpers/fuse/bitap/bitap_score.js | 4 +- src/helpers/fuse/bitap/bitap_search.js | 17 +- src/helpers/fuse/bitap/index.js | 59 +- src/helpers/fuse/helpers/is_array.js | 2 +- src/helpers/fuse/index.js | 229 ++--- src/helpers/promise-utils.js | 21 +- src/helpers/web3-promisfy.js | 18 +- src/helpers/web3-utils.js | 14 +- src/i18n.js | 14 +- src/index.js | 29 +- src/index.scss | 25 +- src/libraries/qr-scanner.js | 210 ++++- src/pages/App.js | 50 +- src/pages/App.scss | 4 +- src/pages/App.test.js | 14 +- src/pages/Pool/AddLiquidity.js | 618 +++++++------ src/pages/Pool/CreateExchange.js | 169 ++-- src/pages/Pool/ModeSelector.js | 76 +- src/pages/Pool/RemoveLiquidity.js | 402 +++++---- src/pages/Pool/index.js | 25 +- src/pages/Pool/pool.scss | 38 +- src/pages/Send/index.js | 812 +++++++++--------- src/pages/Send/send.scss | 2 +- src/pages/Swap/index.js | 790 +++++++++-------- src/pages/Swap/swap.scss | 16 +- src/registerServiceWorker.js | 59 +- src/store/index.js | 4 +- src/store/initial-state.js | 2 +- src/store/store.dev.js | 23 +- src/variables.scss | 24 +- yarn.lock | 5 + 65 files changed, 2998 insertions(+), 2683 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..31ba22d8430 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/package.json b/package.json index 6e40ebe48f4..14cfb7ce38d 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,18 @@ "build": "react-scripts build", "build:rinkeby": "REACT_APP_NETWORK_ID=4 REACT_APP_NETWORK='Rinkeby Test Network' yarn build", "test": "react-scripts test --env=jsdom", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint:base": "yarn eslint './src/**/*.{js,jsx}'", + "format:base": "yarn prettier './src/**/*.{js,jsx,scss}'", + "lint": "yarn lint:base --fix", + "format": "yarn format:base --write", + "check:lint": "yarn lint:base", + "check:format": "yarn format:base --check", + "check:all": "yarn check:lint && yarn check:format" + }, + "devDependencies": { + "prettier": "^1.17.0" }, - "devDependencies": {}, "browserslist": [ ">0.2%", "not dead", diff --git a/src/components/AddressInputPanel/index.js b/src/components/AddressInputPanel/index.js index 19335a60da4..e8ebc29ef2b 100644 --- a/src/components/AddressInputPanel/index.js +++ b/src/components/AddressInputPanel/index.js @@ -1,48 +1,44 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import c from 'classnames'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import c from 'classnames' -import QrCode from '../QrCode'; -import './address-input-panel.scss'; +import QrCode from '../QrCode' +import './address-input-panel.scss' class AddressInputPanel extends Component { static propTypes = { title: PropTypes.string, onChange: PropTypes.func, value: PropTypes.string, - errorMessage: PropTypes.string, - }; + errorMessage: PropTypes.string + } static defaultProps = { onChange() {}, - value: '', - }; + value: '' + } render() { - const { - t, - title, - onChange, - value, - errorMessage, - } = this.props; + const { t, title, onChange, value, errorMessage } = this.props return (
-
+
- {title || t("recipientAddress")} + {title || t('recipientAddress')}
onChange(e.target.value)} @@ -59,4 +55,4 @@ class AddressInputPanel extends Component { } } -export default AddressInputPanel; +export default AddressInputPanel diff --git a/src/components/ContextualInfo/contextual-info.scss b/src/components/ContextualInfo/contextual-info.scss index 0b25b802737..d2a40512889 100644 --- a/src/components/ContextualInfo/contextual-info.scss +++ b/src/components/ContextualInfo/contextual-info.scss @@ -1,4 +1,4 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .contextual-info { &__summary-wrapper { diff --git a/src/components/ContextualInfo/index.js b/src/components/ContextualInfo/index.js index 17273ee019d..3a447f2a3cf 100644 --- a/src/components/ContextualInfo/index.js +++ b/src/components/ContextualInfo/index.js @@ -1,76 +1,69 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import c from 'classnames'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import c from 'classnames' -import DropdownBlue from "../../assets/images/dropdown-blue.svg"; -import DropupBlue from "../../assets/images/dropup-blue.svg"; -import './contextual-info.scss'; +import DropdownBlue from '../../assets/images/dropdown-blue.svg' +import DropupBlue from '../../assets/images/dropup-blue.svg' +import './contextual-info.scss' class ContextualInfo extends Component { static propTypes = { openDetailsText: PropTypes.string, renderTransactionDetails: PropTypes.func, contextualInfo: PropTypes.string, - isError: PropTypes.bool, - }; + isError: PropTypes.bool + } static defaultProps = { openDetailsText: 'Transaction Details', closeDetailsText: 'Hide Details', renderTransactionDetails() {}, contextualInfo: '', - isError: false, - }; + isError: false + } state = { - showDetails: false, - }; + showDetails: false + } renderDetails() { if (!this.state.showDetails) { - return null; + return null } - return ( -
- {this.props.renderTransactionDetails()} -
- ); + return
{this.props.renderTransactionDetails()}
} render() { - const { - openDetailsText, - closeDetailsText, - contextualInfo, - isError, - } = this.props; + const { openDetailsText, closeDetailsText, contextualInfo, isError } = this.props if (contextualInfo) { return (
{contextualInfo}
- ); + ) } return [
this.setState((prevState) => { - return { showDetails: !prevState.showDetails } - })} + onClick={() => + this.setState(prevState => { + return { showDetails: !prevState.showDetails } + }) + } > {!this.state.showDetails ? ( <> {openDetailsText} - dropdown + dropdown ) : ( <> {closeDetailsText} - dropup + dropup )}
, @@ -79,4 +72,4 @@ class ContextualInfo extends Component { } } -export default ContextualInfo; +export default ContextualInfo diff --git a/src/components/CurrencyInputPanel/currency-panel.scss b/src/components/CurrencyInputPanel/currency-panel.scss index 2729f9baec1..dfb38c96500 100644 --- a/src/components/CurrencyInputPanel/currency-panel.scss +++ b/src/components/CurrencyInputPanel/currency-panel.scss @@ -9,20 +9,20 @@ &__container { border-radius: 1.25rem; - box-shadow: 0 0 0 .5px $mercury-gray; + box-shadow: 0 0 0 0.5px $mercury-gray; background-color: $white; transition: box-shadow 200ms ease-in-out; &--error { - box-shadow: 0 0 0 .5px $salmon-red; + box-shadow: 0 0 0 0.5px $salmon-red; } &:focus-within { - box-shadow: 0 0 .5px .5px $malibu-blue; + box-shadow: 0 0 0.5px 0.5px $malibu-blue; } &--error:focus-within { - box-shadow: 0 0 .5px .5px $salmon-red; + box-shadow: 0 0 0.5px 0.5px $salmon-red; } } @@ -30,9 +30,9 @@ @extend %row-nowrap; align-items: center; color: $dove-gray; - font-size: .75rem; + font-size: 0.75rem; line-height: 1rem; - padding: .75rem 1rem; + padding: 0.75rem 1rem; } &__label-container { @@ -44,14 +44,14 @@ } &__label-description { - opacity: .75; - margin-left: .25rem; + opacity: 0.75; + margin-left: 0.25rem; } &__input-row { @extend %row-nowrap; align-items: center; - padding: .25rem .85rem .75rem; + padding: 0.25rem 0.85rem 0.75rem; } &__input { @@ -62,12 +62,12 @@ } &[type='number'] { - -moz-appearance:textfield; + -moz-appearance: textfield; } &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { - -webkit-appearance: none; + -webkit-appearance: none; } } @@ -92,14 +92,14 @@ user-select: none; &:active { - background-color: rgba($zumthor-blue, .8); + background-color: rgba($zumthor-blue, 0.8); } &--selected { background-color: $concrete-gray; border-color: $mercury-gray; color: $black; - padding: 0 .5rem; + padding: 0 0.5rem; .currency-input-panel__dropdown-icon { background-image: url(../../assets/images/dropdown.svg); @@ -128,18 +128,18 @@ user-select: none; &--pending { - line-height: .9; + line-height: 0.9; .loader { - height: .5rem; - width: .5rem; + height: 0.5rem; + width: 0.5rem; } } } &__dropdown-icon { height: 1rem; - width: .75rem; - margin-left: .7rem; + width: 0.75rem; + margin-left: 0.7rem; background-image: url(../../assets/images/dropdown-blue.svg); background-repeat: no-repeat; background-size: contain; @@ -147,13 +147,12 @@ } &__selected-token-logo { - margin-right: .4rem; + margin-right: 0.4rem; border-radius: 1rem; object-fit: contain; } } - .token-modal { background-color: $white; position: relative; @@ -176,7 +175,7 @@ } &__search-icon { - margin-right: .2rem; + margin-right: 0.2rem; } &__token-list { @@ -189,7 +188,7 @@ @extend %row-nowrap; align-items: center; padding: 1rem 1.5rem; - margin: .25rem .5rem; + margin: 0.25rem 0.5rem; justify-content: space-between; cursor: pointer; user-select: none; @@ -235,7 +234,7 @@ justify-content: center; background-color: $malibu-blue; &:hover { - background-color: lighten($malibu-blue, 1) + background-color: lighten($malibu-blue, 1); } &:active { @@ -253,7 +252,7 @@ color: $dove-gray; } - @media only screen and (min-width : 768px) { + @media only screen and (min-width: 768px) { max-width: 560px; max-height: 768px; position: absolute; @@ -269,7 +268,6 @@ } } - .token-modal-appear { bottom: 0; } diff --git a/src/components/CurrencyInputPanel/index.js b/src/components/CurrencyInputPanel/index.js index e860b23d224..ba9970c3da3 100644 --- a/src/components/CurrencyInputPanel/index.js +++ b/src/components/CurrencyInputPanel/index.js @@ -1,39 +1,36 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import { CSSTransitionGroup } from "react-transition-group"; -import classnames from 'classnames'; -import { withRouter } from 'react-router-dom'; -import { withNamespaces } from 'react-i18next'; -import Fuse from '../../helpers/fuse'; -import Modal from '../Modal'; -import TokenLogo from '../TokenLogo'; -import SearchIcon from '../../assets/images/magnifying-glass.svg'; -import { selectors, addPendingTx } from "../../ducks/web3connect"; -import { addApprovalTx } from "../../ducks/pending"; -import { addExchange } from "../../ducks/addresses"; -import { BigNumber as BN } from 'bignumber.js'; - -import './currency-panel.scss'; - -import ERC20_ABI from '../../abi/erc20'; -import FACTORY_ABI from '../../abi/factory'; +import React, { Component } from 'react' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import { CSSTransitionGroup } from 'react-transition-group' +import classnames from 'classnames' +import { withRouter } from 'react-router-dom' +import { withNamespaces } from 'react-i18next' +import Fuse from '../../helpers/fuse' +import Modal from '../Modal' +import TokenLogo from '../TokenLogo' +import SearchIcon from '../../assets/images/magnifying-glass.svg' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import { addApprovalTx } from '../../ducks/pending' +import { addExchange } from '../../ducks/addresses' +import { BigNumber as BN } from 'bignumber.js' + +import './currency-panel.scss' + +import ERC20_ABI from '../../abi/erc20' +import FACTORY_ABI from '../../abi/factory' const FUSE_OPTIONS = { includeMatches: false, threshold: 0.0, - tokenize:true, + tokenize: true, location: 0, distance: 100, maxPatternLength: 45, minMatchCharLength: 1, - keys: [ - {name:"address",weight:0.8}, - {name:"label",weight:0.5}, - ] -}; + keys: [{ name: 'address', weight: 0.8 }, { name: 'label', weight: 0.5 }] +} -const TOKEN_ADDRESS_TO_LABEL = { ETH: 'ETH' }; +const TOKEN_ADDRESS_TO_LABEL = { ETH: 'ETH' } class CurrencyInputPanel extends Component { static propTypes = { @@ -44,10 +41,10 @@ class CurrencyInputPanel extends Component { onCurrencySelected: PropTypes.func, onValueChange: PropTypes.func, tokenAddresses: PropTypes.shape({ - addresses: PropTypes.array.isRequired, + addresses: PropTypes.array.isRequired }).isRequired, exchangeAddresses: PropTypes.shape({ - fromToken: PropTypes.object.isRequired, + fromToken: PropTypes.object.isRequired }).isRequired, factoryAddress: PropTypes.string, selectedTokens: PropTypes.array.isRequired, @@ -59,52 +56,52 @@ class CurrencyInputPanel extends Component { addExchange: PropTypes.func.isRequired, filteredTokens: PropTypes.arrayOf(PropTypes.string), disableUnlock: PropTypes.bool, - renderInput: PropTypes.func, - }; + renderInput: PropTypes.func + } static defaultProps = { selectedTokens: [], filteredTokens: [], onCurrencySelected() {}, onValueChange() {}, - selectedTokenAddress: '', - }; + selectedTokenAddress: '' + } state = { isShowingModal: false, searchQuery: '', - loadingExchange: false, - }; + loadingExchange: false + } createTokenList = () => { - const { filteredTokens } = this.props; - let tokens = this.props.tokenAddresses.addresses; - let tokenList = [ { value: 'ETH', label: 'ETH', address: 'ETH' } ]; + const { filteredTokens } = this.props + let tokens = this.props.tokenAddresses.addresses + let tokenList = [{ value: 'ETH', label: 'ETH', address: 'ETH' }] for (let i = 0; i < tokens.length; i++) { - let entry = { value: '', label: '' }; - entry.value = tokens[i][0]; - entry.label = tokens[i][0]; - entry.address = tokens[i][1]; - tokenList.push(entry); - TOKEN_ADDRESS_TO_LABEL[tokens[i][1]] = tokens[i][0]; + let entry = { value: '', label: '' } + entry.value = tokens[i][0] + entry.label = tokens[i][0] + entry.address = tokens[i][1] + tokenList.push(entry) + TOKEN_ADDRESS_TO_LABEL[tokens[i][1]] = tokens[i][0] } - return tokenList.filter(({ address }) => !filteredTokens.includes(address)); - }; + return tokenList.filter(({ address }) => !filteredTokens.includes(address)) + } - onTokenSelect = (address) => { + onTokenSelect = address => { this.setState({ searchQuery: '', - isShowingModal: false, - }); + isShowingModal: false + }) - this.props.onCurrencySelected(address); - }; + this.props.onCurrencySelected(address) + } renderTokenList() { - const tokens = this.createTokenList(); - const { loadingExchange, searchQuery } = this.state; + const tokens = this.createTokenList() + const { loadingExchange, searchQuery } = this.state const { t, selectedTokens, @@ -115,8 +112,8 @@ class CurrencyInputPanel extends Component { factoryAddress, exchangeAddresses: { fromToken }, addExchange, - history, - } = this.props; + history + } = this.props if (loadingExchange) { return ( @@ -124,52 +121,52 @@ class CurrencyInputPanel extends Component {
Searching for Exchange...
- ); + ) } if (web3 && web3.utils && web3.utils.isAddress(searchQuery)) { - const tokenAddress = searchQuery; - const { label } = selectors().getBalance(account, tokenAddress); - const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress); - const exchangeAddress = fromToken[tokenAddress]; + const tokenAddress = searchQuery + const { label } = selectors().getBalance(account, tokenAddress) + const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress) + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress) { - this.setState({loadingExchange: true}); + this.setState({ loadingExchange: true }) factory.methods.getExchange(tokenAddress).call((err, data) => { if (!err && data !== '0x0000000000000000000000000000000000000000') { - addExchange({ label, tokenAddress, exchangeAddress: data }); + addExchange({ label, tokenAddress, exchangeAddress: data }) } - this.setState({loadingExchange: false}); - }); - return; + this.setState({ loadingExchange: false }) + }) + return } } if (disableTokenSelect) { - return; + return } - let results; + let results if (!searchQuery) { - results = tokens; + results = tokens } else { - const fuse = new Fuse(tokens, FUSE_OPTIONS); - results = fuse.search(this.state.searchQuery); + const fuse = new Fuse(tokens, FUSE_OPTIONS) + results = fuse.search(this.state.searchQuery) } if (!results.length && web3 && web3.utils && web3.utils.isAddress(searchQuery)) { - const { label } = selectors().getBalance(account, searchQuery); + const { label } = selectors().getBalance(account, searchQuery) return [
-
{t("noExchange")}
+
{t('noExchange')}
,
{ - this.setState({ isShowingModal: false }); - history.push(`/create-exchange/${searchQuery}`); + this.setState({ isShowingModal: false }) + history.push(`/create-exchange/${searchQuery}`) }} >
{`Create exchange for ${label}`}
@@ -180,34 +177,32 @@ class CurrencyInputPanel extends Component { if (!results.length) { return (
-
{t("noExchange")}
+
{t('noExchange')}
) } return results.map(({ label, address }) => { - const isSelected = selectedTokens.indexOf(address) > -1; + const isSelected = selectedTokens.indexOf(address) > -1 return (
this.onTokenSelect(address)} > -
{label}
+
{label}
- ); - }); + ) + }) } renderModal() { if (!this.state.isShowingModal) { - return null; + return null } return ( @@ -224,21 +219,19 @@ class CurrencyInputPanel extends Component {
{ - this.setState({ searchQuery: e.target.value }); + this.setState({ searchQuery: e.target.value }) }} /> - search -
-
- {this.renderTokenList()} + search
+
{this.renderTokenList()}
- ); + ) } renderUnlockButton() { @@ -254,69 +247,58 @@ class CurrencyInputPanel extends Component { pendingApprovals, value, addApprovalTx, - addPendingTx, - } = this.props; + addPendingTx + } = this.props if (disableUnlock || !selectedTokenAddress || selectedTokenAddress === 'ETH') { - return; + return } - const { value: allowance, decimals, label } = selectors().getApprovals(selectedTokenAddress, account, fromToken[selectedTokenAddress]); + const { value: allowance, decimals, label } = selectors().getApprovals( + selectedTokenAddress, + account, + fromToken[selectedTokenAddress] + ) - if ( - !label || - ( - allowance.isGreaterThanOrEqualTo(BN((value || 0) * 10 ** decimals)) && - !BN(allowance).isZero() - ) - ) { - return; + if (!label || (allowance.isGreaterThanOrEqualTo(BN((value || 0) * 10 ** decimals)) && !BN(allowance).isZero())) { + return } - const approvalTxId = pendingApprovals[selectedTokenAddress]; + const approvalTxId = pendingApprovals[selectedTokenAddress] if (approvalTxId && transactions.pending.includes(approvalTxId)) { return ( - - ); + ) } return ( - ); + ) } renderInput() { - const { - t, - errorMessage, - value, - onValueChange, - selectedTokenAddress, - disableTokenSelect, - renderInput, - } = this.props; + const { t, errorMessage, value, onValueChange, selectedTokenAddress, disableTokenSelect, renderInput } = this.props if (typeof renderInput === 'function') { - return renderInput(); + return renderInput() } return ( @@ -324,72 +306,64 @@ class CurrencyInputPanel extends Component { onValueChange(e.target.value)} onKeyPress={e => { - const charCode = e.which ? e.which : e.keyCode; + const charCode = e.which ? e.which : e.keyCode // Prevent 'minus' character if (charCode === 45) { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault() + e.stopPropagation() } }} value={value} /> - { this.renderUnlockButton() } + {this.renderUnlockButton()}
- ); + ) } render() { - const { - title, - description, - extraText, - errorMessage, - } = this.props; + const { title, description, extraText, errorMessage } = this.props return (
-
+
{title} {description}
- + {extraText}
@@ -412,13 +386,13 @@ export default withRouter( approvals: state.web3connect.approvals, transactions: state.web3connect.transactions, web3: state.web3connect.web3, - pendingApprovals: state.pending.approvals, + pendingApprovals: state.pending.approvals }), dispatch => ({ selectors: () => dispatch(selectors()), addExchange: opts => dispatch(addExchange(opts)), addPendingTx: opts => dispatch(addPendingTx(opts)), - addApprovalTx: opts => dispatch(addApprovalTx(opts)), - }), + addApprovalTx: opts => dispatch(addApprovalTx(opts)) + }) )(withNamespaces()(CurrencyInputPanel)) -); +) diff --git a/src/components/Header/header.scss b/src/components/Header/header.scss index 88ea1204d7e..a436e8dba2d 100644 --- a/src/components/Header/header.scss +++ b/src/components/Header/header.scss @@ -1,11 +1,11 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .header { @extend %col-nowrap; &__top { @extend %row-nowrap; - padding: 1.25rem .75rem; + padding: 1.25rem 0.75rem; align-items: center; border-bottom: 1px solid $concrete-gray; } @@ -28,22 +28,22 @@ } &--inactive { - opacity: .5; + opacity: 0.5; } &__dialog { @extend %col-nowrap; - border-radius: .875rem; + border-radius: 0.875rem; border: 1px solid $mercury-gray; - margin: 1rem .75rem 0 .75rem; + margin: 1rem 0.75rem 0 0.75rem; padding: 1.5rem 1rem; text-align: center; display: none; &__description { - font-size: .75rem; + font-size: 0.75rem; color: $dove-gray; - margin-top: .4rem; + margin-top: 0.4rem; } &--disconnected { @@ -68,7 +68,7 @@ } } - @media only screen and (min-width : 768px) { + @media only screen and (min-width: 768px) { //position: fixed; top: 0px; left: 0px; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 096f6fef9e1..1e614cbeab2 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,17 +1,17 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import classnames from 'classnames'; -import UAParser from 'ua-parser-js'; -import { withNamespaces } from 'react-i18next'; -import Logo from '../Logo'; -import CoinbaseWalletLogo from '../../assets/images/coinbase-wallet-logo.png'; -import TrustLogo from '../../assets/images/trust-wallet-logo.svg'; -import BraveLogo from '../../assets/images/brave-logo.svg'; -import MetamaskLogo from '../../assets/images/metamask-logo.svg'; -import Web3Status from '../Web3Status'; - -import "./header.scss"; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import classnames from 'classnames' +import UAParser from 'ua-parser-js' +import { withNamespaces } from 'react-i18next' +import Logo from '../Logo' +import CoinbaseWalletLogo from '../../assets/images/coinbase-wallet-logo.png' +import TrustLogo from '../../assets/images/trust-wallet-logo.svg' +import BraveLogo from '../../assets/images/brave-logo.svg' +import MetamaskLogo from '../../assets/images/metamask-logo.svg' +import Web3Status from '../Web3Status' + +import './header.scss' const links = { coinbaseWallet: { @@ -19,135 +19,132 @@ const links = { ios: 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455' }, trust: { - android: 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap', - ios: 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap', + android: + 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap', + ios: + 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=https://uniswap.exchange/swap' }, metamask: { - chrome: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', + chrome: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn' }, brave: { android: 'https://play.google.com/store/apps/details?id=com.brave.browser', - ios: 'https://itunes.apple.com/us/app/brave-browser-fast-adblocker/id1052879175', - }, -}; + ios: 'https://itunes.apple.com/us/app/brave-browser-fast-adblocker/id1052879175' + } +} -const ua = new UAParser(window.navigator.userAgent); +const ua = new UAParser(window.navigator.userAgent) function getTrustLink() { - const os = ua.getOS(); + const os = ua.getOS() if (os.name === 'Android') { - return links.trust.android; + return links.trust.android } if (os.name === 'iOS') { - return links.trust.ios; + return links.trust.ios } } function getCoinbaseWalletLink() { - const os = ua.getOS(); + const os = ua.getOS() if (os.name === 'Android') { - return links.coinbaseWallet.android; + return links.coinbaseWallet.android } if (os.name === 'iOS') { - return links.coinbaseWallet.ios; + return links.coinbaseWallet.ios } } function getBraveLink() { - const os = ua.getOS(); + const os = ua.getOS() if (os.name === 'Mac OS') { - return links.brave.ios; + return links.brave.ios } - return links.brave.android; + return links.brave.android } function getMetamaskLink() { - return links.metamask.chrome; + return links.metamask.chrome } function isMobile() { - return ua.getDevice().type === 'mobile'; + return ua.getDevice().type === 'mobile' } class BlockingWarning extends Component { - render () { - const { - t, - isConnected, - initialized, - networkId, - } = this.props; - let content = []; + render() { + const { t, isConnected, initialized, networkId } = this.props + let content = [] - const correctNetworkId = process.env.REACT_APP_NETWORK_ID || 1; - const correctNetwork = process.env.REACT_APP_NETWORK || 'Main Ethereum Network'; + const correctNetworkId = process.env.REACT_APP_NETWORK_ID || 1 + const correctNetwork = process.env.REACT_APP_NETWORK || 'Main Ethereum Network' - const wrongNetwork = networkId !== correctNetworkId; + const wrongNetwork = networkId !== correctNetworkId if (wrongNetwork && initialized) { content = [ -
{t("wrongNetwork")}
, +
{t('wrongNetwork')}
,
- {t("switchNetwork", {correctNetwork})} -
, - ]; + {t('switchNetwork', { correctNetwork })} +
+ ] } if (!isConnected && initialized) { content = [ -
{t("noWallet")}
, +
{t('noWallet')}
,
- { - isMobile() - ? t("installWeb3MobileBrowser") - : t("installMetamask") - } + {isMobile() ? t('installWeb3MobileBrowser') : t('installMetamask')}
,
- { - isMobile() - ? ( - [ - coinbase window.open(getCoinbaseWalletLink(), '_blank')} />, - trust window.open(getTrustLink(), '_blank')} /> - ] - ) - : ( - [ - metamask window.open(getMetamaskLink(), '_blank')} />, - brave window.open(getBraveLink(), '_blank')} /> - ] - ) - } -
, - ]; + {isMobile() + ? [ + coinbase window.open(getCoinbaseWalletLink(), '_blank')} + />, + trust window.open(getTrustLink(), '_blank')} /> + ] + : [ + metamask window.open(getMetamaskLink(), '_blank')} + />, + brave window.open(getBraveLink(), '_blank')} /> + ]} +
+ ] } return (
{content}
- ); + ) } } -function Header (props) { +function Header(props) { return (
@@ -162,15 +159,13 @@ function Header (props) { Header.propTypes = { currentAddress: PropTypes.string, - isConnected: PropTypes.bool.isRequired, -}; - -export default connect( - state => ({ - currentAddress: state.web3connect.account, - initialized: state.web3connect.initialized, - isConnected: !!state.web3connect.account, - web3: state.web3connect.web3, - networkId: state.web3connect.networkId, - }), -)(withNamespaces()(Header)); + isConnected: PropTypes.bool.isRequired +} + +export default connect(state => ({ + currentAddress: state.web3connect.account, + initialized: state.web3connect.initialized, + isConnected: !!state.web3connect.account, + web3: state.web3connect.web3, + networkId: state.web3connect.networkId +}))(withNamespaces()(Header)) diff --git a/src/components/Logo/index.js b/src/components/Logo/index.js index badcc70fb6e..096dbd9d9a0 100644 --- a/src/components/Logo/index.js +++ b/src/components/Logo/index.js @@ -1,10 +1,12 @@ -import React from 'react'; -import "./logo.scss"; +import React from 'react' +import './logo.scss' export default function Logo(props) { return (
- 🦄 + + 🦄 +
- ); + ) } diff --git a/src/components/Logo/logo.scss b/src/components/Logo/logo.scss index 9d7402ab0ea..dcf0eeeff9f 100644 --- a/src/components/Logo/logo.scss +++ b/src/components/Logo/logo.scss @@ -1,4 +1,4 @@ .logo { font-size: 1.5rem; line-height: 1.75rem; -} \ No newline at end of file +} diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js index f557644f767..5df52409d36 100644 --- a/src/components/Modal/index.js +++ b/src/components/Modal/index.js @@ -1,15 +1,15 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import { CSSTransitionGroup } from 'react-transition-group'; -import './modal.scss'; +import React, { Component } from 'react' +import ReactDOM from 'react-dom' +import PropTypes from 'prop-types' +import { CSSTransitionGroup } from 'react-transition-group' +import './modal.scss' -const modalRoot = document.querySelector('#modal-root'); +const modalRoot = document.querySelector('#modal-root') export default class Modal extends Component { static propTypes = { - onClose: PropTypes.func.isRequired, - }; + onClose: PropTypes.func.isRequired + } componentDidMount() { // The portal element is inserted in the DOM tree after @@ -28,7 +28,7 @@ export default class Modal extends Component { setTimeout(() => { // modalRoot.style.display = 'none'; // modalRoot.removeChild(this.el); - }, 500); + }, 500) } render() { @@ -46,7 +46,7 @@ export default class Modal extends Component { {this.props.children}
, - modalRoot, - ); + modalRoot + ) } } diff --git a/src/components/Modal/modal.scss b/src/components/Modal/modal.scss index 1f495b61462..6c159a1a6d8 100644 --- a/src/components/Modal/modal.scss +++ b/src/components/Modal/modal.scss @@ -4,9 +4,8 @@ position: relative; height: 100vh; width: 100vw; - background-color: rgba($black, .6); + background-color: rgba($black, 0.6); z-index: 1000; - } .modal-container-appear { diff --git a/src/components/NavigationTabs/beta-message.scss b/src/components/NavigationTabs/beta-message.scss index 1602eaf1bb1..08b19c52517 100644 --- a/src/components/NavigationTabs/beta-message.scss +++ b/src/components/NavigationTabs/beta-message.scss @@ -1,23 +1,23 @@ -@import "../../variables"; +@import '../../variables'; .beta-message { @extend %row-nowrap; flex: 1 0 auto; align-items: center; position: relative; - padding: .5rem 1rem; + padding: 0.5rem 1rem; margin-bottom: 1rem; - border: 1px solid rgba($wisteria-purple, .4); - background-color: rgba($wisteria-purple, .1); + border: 1px solid rgba($wisteria-purple, 0.4); + background-color: rgba($wisteria-purple, 0.1); border-radius: 2rem; - font-size: .75rem; + font-size: 0.75rem; line-height: 1rem; text-align: center; color: $wisteria-purple; &:after { content: '✕'; - top: .5rem; + top: 0.5rem; right: 1rem; position: absolute; color: $wisteria-purple; diff --git a/src/components/NavigationTabs/index.js b/src/components/NavigationTabs/index.js index 0791c3f1274..a3c5cb0919e 100644 --- a/src/components/NavigationTabs/index.js +++ b/src/components/NavigationTabs/index.js @@ -1,71 +1,66 @@ -import React, { Component } from 'react'; -import { withRouter } from 'react-router-dom'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import { withNamespaces } from 'react-i18next'; -import { dismissBetaMessage } from '../../ducks/app'; -import {Tab, Tabs} from "../Tab"; +import React, { Component } from 'react' +import { withRouter } from 'react-router-dom' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import { withNamespaces } from 'react-i18next' +import { dismissBetaMessage } from '../../ducks/app' +import { Tab, Tabs } from '../Tab' -import './beta-message.scss'; +import './beta-message.scss' class NavigationTabs extends Component { static propTypes = { history: PropTypes.shape({ - push: PropTypes.func.isRequired, + push: PropTypes.func.isRequired }), className: PropTypes.string, dismissBetaMessage: PropTypes.func.isRequired, - showBetaMessage: PropTypes.bool.isRequired, - }; + showBetaMessage: PropTypes.bool.isRequired + } constructor(props) { - super(props); + super(props) this.state = { selectedPath: this.props.location.pathname, className: '', - showWarning: true, - }; + showWarning: true + } } renderTab(name, path, regex) { - const { push } = this.props.history; - return ( - push(path)} - isSelected={regex.test(this.props.location.pathname)} - /> - ) + const { push } = this.props.history + return push(path)} isSelected={regex.test(this.props.location.pathname)} /> } render() { - const { t, showBetaMessage, className, dismissBetaMessage } = this.props; + const { t, showBetaMessage, className, dismissBetaMessage } = this.props return (
- { this.renderTab(t("swap"), '/swap', /swap/) } - { this.renderTab(t("send"), '/send', /send/) } - { this.renderTab(t("pool"), '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/) } + {this.renderTab(t('swap'), '/swap', /swap/)} + {this.renderTab(t('send'), '/send', /send/)} + {this.renderTab(t('pool'), '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/)} - { - showBetaMessage && ( -
- 💀 {t("betaWarning")} -
- ) - } + {showBetaMessage && ( +
+ + 💀 + {' '} + {t('betaWarning')} +
+ )}
- ); + ) } } export default withRouter( connect( state => ({ - showBetaMessage: state.app.showBetaMessage, + showBetaMessage: state.app.showBetaMessage }), dispatch => ({ - dismissBetaMessage: () => dispatch(dismissBetaMessage()), - }), + dismissBetaMessage: () => dispatch(dismissBetaMessage()) + }) )(withNamespaces()(NavigationTabs)) -); +) diff --git a/src/components/OversizedPanel/index.js b/src/components/OversizedPanel/index.js index ff1f3b7334c..9e6899ee782 100644 --- a/src/components/OversizedPanel/index.js +++ b/src/components/OversizedPanel/index.js @@ -1,13 +1,13 @@ -import React from 'react'; +import React from 'react' -import './oversized-panel.scss'; +import './oversized-panel.scss' export default function OversizedPanel(props) { return (
- { props.hideTop ||
} + {props.hideTop ||
} {props.children} - { props.hideBottom ||
} + {props.hideBottom ||
}
- ); + ) } diff --git a/src/components/OversizedPanel/oversized-panel.scss b/src/components/OversizedPanel/oversized-panel.scss index c7dffce0008..b473512fe43 100644 --- a/src/components/OversizedPanel/oversized-panel.scss +++ b/src/components/OversizedPanel/oversized-panel.scss @@ -5,12 +5,12 @@ background-color: $concrete-gray; width: calc(100% - 1rem); margin: 0 auto; - border-radius: .625rem; + border-radius: 0.625rem; &__top { - content: ""; + content: ''; position: absolute; - top: -.5rem; + top: -0.5rem; left: 0; height: 1rem; width: 100%; @@ -27,4 +27,4 @@ background-color: $concrete-gray; z-index: 100; } -} \ No newline at end of file +} diff --git a/src/components/QrCode/index.js b/src/components/QrCode/index.js index 7fed0a24030..8a757fe3c27 100644 --- a/src/components/QrCode/index.js +++ b/src/components/QrCode/index.js @@ -1,70 +1,71 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { CSSTransitionGroup } from "react-transition-group"; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { CSSTransitionGroup } from 'react-transition-group' -import Modal from '../Modal'; -import QrCodeSVG from '../../assets/images/qr-code.svg'; -import QrScanner from '../../libraries/qr-scanner'; -import './qr-code.scss'; +import Modal from '../Modal' +import QrCodeSVG from '../../assets/images/qr-code.svg' +import QrScanner from '../../libraries/qr-scanner' +import './qr-code.scss' class QrCode extends Component { static propTypes = { - onValueReceived: PropTypes.func, - }; + onValueReceived: PropTypes.func + } static defaultProps = { - onValueReceived() {}, - }; + onValueReceived() {} + } state = { videoOpen: false, - stream: null, - }; + stream: null + } componentDidUpdate() { - const { videoOpen, stream } = this.state; + const { videoOpen, stream } = this.state if (videoOpen && !stream && this.videoRef) { this.startStreamingVideo(this.videoRef) } else if (!videoOpen && stream) { - this.setState({stream: null}); + this.setState({ stream: null }) } } startStreamingVideo(videoRef) { if (navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia({video: { facingMode: 'user'}, audio: false}) - .then((stream) => { - videoRef.srcObject = stream; - new QrScanner(videoRef, (val) => { - this.closeVideo(); - this.props.onValueReceived(val); + navigator.mediaDevices + .getUserMedia({ video: { facingMode: 'user' }, audio: false }) + .then(stream => { + videoRef.srcObject = stream + new QrScanner(videoRef, val => { + this.closeVideo() + this.props.onValueReceived(val) }) this.setState({ stream: stream.getTracks()[0] - }); + }) + }) + .catch(error => { + this.closeVideo() + console.error(error) }) - .catch((error) => { - this.closeVideo(); - console.error(error); - }); } } openVideo = () => { - this.setState({videoOpen: true}); + this.setState({ videoOpen: true }) } closeVideo = () => { if (this.state.stream) { - this.state.stream.stop(); + this.state.stream.stop() } - this.setState({videoOpen: false, stream: null}); - this.videoRef = null; - }; + this.setState({ videoOpen: false, stream: null }) + this.videoRef = null + } - setVideoRef = (element) => { - this.videoRef = element; + setVideoRef = element => { + this.videoRef = element } renderQrReader() { @@ -80,31 +81,29 @@ class QrCode extends Component { transitionEnterTimeout={200} >
- +
- ); + ) } - return null; + return null } render() { return [ - code { - this.state.videoOpen ? this.closeVideo() : this.openVideo(); - }} />, + code { + this.state.videoOpen ? this.closeVideo() : this.openVideo() + }} + />, this.renderQrReader() ] } } -export default QrCode; +export default QrCode diff --git a/src/components/QrCode/qr-code.scss b/src/components/QrCode/qr-code.scss index fb35edf4a8e..9ac49cc4485 100644 --- a/src/components/QrCode/qr-code.scss +++ b/src/components/QrCode/qr-code.scss @@ -1,4 +1,3 @@ - .qr-code { &__video { height: 100%; diff --git a/src/components/Tab/index.js b/src/components/Tab/index.js index 734ec8abbb2..79ec80488b2 100644 --- a/src/components/Tab/index.js +++ b/src/components/Tab/index.js @@ -1,38 +1,33 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' -import './tab.scss'; +import './tab.scss' export const Tabs = props => { - return ( -
- { props.children } -
- ); -}; + return
{props.children}
+} export const Tab = props => { return (
- { props.text ? {props.text} : null } + {props.text ? {props.text} : null}
- ); -}; + ) +} Tab.propTypes = { className: PropTypes.string, text: PropTypes.string, isSelected: PropTypes.bool, - onClick: PropTypes.func, -}; + onClick: PropTypes.func +} Tab.defaultProps = { - className: '', -}; - + className: '' +} diff --git a/src/components/Tab/tab.scss b/src/components/Tab/tab.scss index c345371cd26..efd0457d793 100644 --- a/src/components/Tab/tab.scss +++ b/src/components/Tab/tab.scss @@ -6,7 +6,7 @@ height: 2.5rem; background-color: $concrete-gray; border-radius: 3rem; - box-shadow: 0 0 0 .5px darken($concrete-gray, 5); + box-shadow: 0 0 0 0.5px darken($concrete-gray, 5); .tab:first-child { //margin-left: -1px; @@ -35,7 +35,7 @@ &--selected { background-color: $white; border-radius: 3rem; - box-shadow: 0 0 .5px .5px $mercury-gray; + box-shadow: 0 0 0.5px 0.5px $mercury-gray; font-weight: 500; span { diff --git a/src/components/TokenLogo/index.js b/src/components/TokenLogo/index.js index bcc00d6646a..a3b7f435b9a 100644 --- a/src/components/TokenLogo/index.js +++ b/src/components/TokenLogo/index.js @@ -1,66 +1,72 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import EthereumLogo from '../../assets/images/ethereum-logo.svg'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import EthereumLogo from '../../assets/images/ethereum-logo.svg' const RINKEBY_TOKEN_MAP = { '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x0d8775f648430679a709e98d2b0cb6250d2887ef', '0x2448eE2641d78CC42D7AD76498917359D961A783': '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0xd26114cd6ee289accf82350c8d8487fedb8a0c07', - '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xe41d2489571d322189246dafa5ebde1f4699f498', -}; + '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xe41d2489571d322189246dafa5ebde1f4699f498' +} -const TOKEN_ICON_API = 'https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens'; -const BAD_IMAGES = {}; +const TOKEN_ICON_API = 'https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens' +const BAD_IMAGES = {} export default class TokenLogo extends Component { static propTypes = { address: PropTypes.string, size: PropTypes.string, - className: PropTypes.string, - }; + className: PropTypes.string + } static defaultProps = { address: '', size: '1rem', - className: '', - }; + className: '' + } state = { - error: false, - }; + error: false + } render() { - const { address, size, className } = this.props; + const { address, size, className } = this.props // let path = GenericTokenLogo; - let path = ''; - const mainAddress = RINKEBY_TOKEN_MAP[address] ? RINKEBY_TOKEN_MAP[address] : address; + let path = '' + const mainAddress = RINKEBY_TOKEN_MAP[address] ? RINKEBY_TOKEN_MAP[address] : address if (mainAddress === 'ETH') { - path = EthereumLogo; + path = EthereumLogo } if (!this.state.error && !BAD_IMAGES[mainAddress] && mainAddress !== 'ETH') { - path = `${TOKEN_ICON_API}/${mainAddress.toLowerCase()}.png`; + path = `${TOKEN_ICON_API}/${mainAddress.toLowerCase()}.png` } if (!path) { - return
🤔
+ return ( +
+ + 🤔 + +
+ ) } return ( images { - this.setState({ error: true }); - BAD_IMAGES[mainAddress] = true; + this.setState({ error: true }) + BAD_IMAGES[mainAddress] = true }} /> - ); + ) } } diff --git a/src/components/Web3Status/index.js b/src/components/Web3Status/index.js index a0c5ed4e115..30cf46d8a75 100644 --- a/src/components/Web3Status/index.js +++ b/src/components/Web3Status/index.js @@ -1,53 +1,51 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import classnames from 'classnames'; -import Jazzicon from 'jazzicon'; -import { CSSTransitionGroup } from "react-transition-group"; -import { withNamespaces } from 'react-i18next'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import classnames from 'classnames' +import Jazzicon from 'jazzicon' +import { CSSTransitionGroup } from 'react-transition-group' +import { withNamespaces } from 'react-i18next' import { ethers } from 'ethers' -import './web3-status.scss'; -import Modal from '../Modal'; +import './web3-status.scss' +import Modal from '../Modal' function getEtherscanLink(tx) { - return `https://etherscan.io/tx/${tx}`; + return `https://etherscan.io/tx/${tx}` } console.log(ethers) class Web3Status extends Component { state = { - isShowingModal: false, - }; + isShowingModal: false + } handleClick = () => { if (this.props.pending.length && !this.state.isShowingModal) { - this.setState({isShowingModal: true}); + this.setState({ isShowingModal: true }) } - }; + } renderPendingTransactions() { - return this.props.pending.map((transaction) => { + return this.props.pending.map(transaction => { return (
window.open(getEtherscanLink(transaction), '_blank')} > -
- {transaction} -
+
{transaction}
-
{this.props.t("pending")} +
{this.props.t('pending')}
- ); - }); + ) + }) } renderModal() { if (!this.state.isShowingModal) { - return null; + return null } return ( @@ -68,84 +66,82 @@ class Web3Status extends Component {
- ); + ) } render() { - const { t, address, pending, confirmed } = this.props; - const hasPendingTransactions = !!pending.length; - const hasConfirmedTransactions = !!confirmed.length; + const { t, address, pending, confirmed } = this.props + const hasPendingTransactions = !!pending.length + const hasConfirmedTransactions = !!confirmed.length return (
- {hasPendingTransactions ? getPendingText(pending, t("pending")) : getText(address, t("disconnected")) } + {hasPendingTransactions ? getPendingText(pending, t('pending')) : getText(address, t('disconnected'))}
{ if (!el) { - return; + return } if (!address || address.length < 42 || !ethers.utils.isHexString(address)) { - return; + return } - el.innerHTML = ''; - el.appendChild(Jazzicon(16, parseInt(address.slice(2), 16))); + el.innerHTML = '' + el.appendChild(Jazzicon(16, parseInt(address.slice(2), 16))) }} /> {this.renderModal()}
- ); + ) } } - - function getPendingText(pendingTransactions, pendingLabel) { return (
- {pendingTransactions.length} {pendingLabel} + + {pendingTransactions.length} {pendingLabel} +
- ); + ) } function getText(text, disconnectedText) { if (!text || text.length < 42 || !ethers.utils.isHexString(text)) { - return disconnectedText; + return disconnectedText } - const address = ethers.utils.getAddress(text); - return `${address.substring(0, 6)}...${address.substring(38)}`; + const address = ethers.utils.getAddress(text) + return `${address.substring(0, 6)}...${address.substring(38)}` } Web3Status.propTypes = { isConnected: PropTypes.bool, - address: PropTypes.string, -}; + address: PropTypes.string +} Web3Status.defaultProps = { isConnected: false, - address: 'Disconnected', -}; - -export default connect( - state => { - return { - address: state.web3connect.account, - isConnected: !!(state.web3connect.web3 && state.web3connect.account), - pending: state.web3connect.transactions.pending, - confirmed: state.web3connect.transactions.confirmed, - }; + address: 'Disconnected' +} + +export default connect(state => { + return { + address: state.web3connect.account, + isConnected: !!(state.web3connect.web3 && state.web3connect.account), + pending: state.web3connect.transactions.pending, + confirmed: state.web3connect.transactions.confirmed } -)(withNamespaces()(Web3Status)); +})(withNamespaces()(Web3Status)) diff --git a/src/components/Web3Status/web3-status.scss b/src/components/Web3Status/web3-status.scss index 769f43bd5ab..fd8acf67d60 100644 --- a/src/components/Web3Status/web3-status.scss +++ b/src/components/Web3Status/web3-status.scss @@ -1,12 +1,12 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .web3-status { @extend %row-nowrap; height: 2rem; - font-size: .9rem; + font-size: 0.9rem; align-items: center; border: 1px solid $mercury-gray; - padding: .5rem; + padding: 0.5rem; border-radius: 2rem; color: $dove-gray; font-weight: 400; @@ -22,9 +22,9 @@ &__text { flex: 1 1 auto; overflow: hidden; - margin-right: .75rem; - margin-left: .25rem; - font-size: .75rem; + margin-right: 0.75rem; + margin-left: 0.25rem; + font-size: 0.75rem; } &__pending-container { @@ -39,7 +39,6 @@ } } - .pending-modal { background-color: $white; position: relative; @@ -80,12 +79,12 @@ color: $royal-blue; border: 1px solid $royal-blue; background-color: $zumthor-blue; - padding: .5rem .75rem;; + padding: 0.5rem 0.75rem; border-radius: 100px; - font-size: .75rem; + font-size: 0.75rem; & > .loading { - margin-right: .5rem; + margin-right: 0.5rem; } } @@ -96,7 +95,6 @@ } } - .pending-modal-appear { bottom: 0; } diff --git a/src/constants/actionTypes.js b/src/constants/actionTypes.js index 96dc3e3412c..cd24799418c 100644 --- a/src/constants/actionTypes.js +++ b/src/constants/actionTypes.js @@ -1,53 +1,53 @@ // string literals for actions // set global web3 object -export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3'; +export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3' // web3 actions, all set from action creator to reducer to app -export const SET_WEB3_CONNECTION_STATUS = 'WEB3_CONNECTION_STATUS'; -export const CHECK_WEB3_CONNECTION = 'CHECK_WEB3_CONNECTION'; -export const SET_CURRENT_MASK_ADDRESS = 'SET_CURRENT_MASK_ADDRESS'; +export const SET_WEB3_CONNECTION_STATUS = 'WEB3_CONNECTION_STATUS' +export const CHECK_WEB3_CONNECTION = 'CHECK_WEB3_CONNECTION' +export const SET_CURRENT_MASK_ADDRESS = 'SET_CURRENT_MASK_ADDRESS' -export const METAMASK_LOCKED = 'METAMASK_LOCKED'; -export const METAMASK_UNLOCKED = 'METAMASK_UNLOCKED'; -export const SET_INTERACTION_STATE = 'SET_INTERACTION_STATE'; -export const SET_NETWORK_MESSAGE = 'SET_NETWORK_MESSAGE'; +export const METAMASK_LOCKED = 'METAMASK_LOCKED' +export const METAMASK_UNLOCKED = 'METAMASK_UNLOCKED' +export const SET_INTERACTION_STATE = 'SET_INTERACTION_STATE' +export const SET_NETWORK_MESSAGE = 'SET_NETWORK_MESSAGE' -export const SET_BLOCK_TIMESTAMP = 'SET_BLOCK_TIMESTAMP'; -export const SET_EXCHANGE_TYPE = 'SET_EXCHANGE_TYPE'; +export const SET_BLOCK_TIMESTAMP = 'SET_BLOCK_TIMESTAMP' +export const SET_EXCHANGE_TYPE = 'SET_EXCHANGE_TYPE' // actions to toggle divs -export const TOGGLE_ABOUT = 'TOGGLE_ABOUT'; -export const TOGGLE_INVEST = 'TOGGLE_INVEST'; +export const TOGGLE_ABOUT = 'TOGGLE_ABOUT' +export const TOGGLE_INVEST = 'TOGGLE_INVEST' // CONTRACT actions in actions, action creator, reducer -export const FACTORY_CONTRACT_READY = 'FACTORY_CONTRACT_READY'; -export const EXCHANGE_CONTRACT_READY = 'EXCHANGE_CONTRACT_READY'; -export const TOKEN_CONTRACT_READY = 'TOKEN_CONTRACT_READY'; +export const FACTORY_CONTRACT_READY = 'FACTORY_CONTRACT_READY' +export const EXCHANGE_CONTRACT_READY = 'EXCHANGE_CONTRACT_READY' +export const TOKEN_CONTRACT_READY = 'TOKEN_CONTRACT_READY' // actions for the exchange -export const SET_INPUT_BALANCE = 'SET_INPUT_BALANCE'; -export const SET_OUTPUT_BALANCE = 'SET_OUTPUT_BALANCE'; -export const SET_INPUT_TOKEN = 'SET_INPUT_TOKEN'; -export const SET_OUTPUT_TOKEN = 'SET_OUTPUT_TOKEN'; -export const SET_ETH_POOL_1 = 'SET_ETH_POOL_1'; -export const SET_ETH_POOL_2 = 'SET_ETH_POOL_2'; -export const SET_TOKEN_POOL_1 = 'SET_TOKEN_POOL_1'; -export const SET_TOKEN_POOL_2 = 'SET_TOKEN_POOL_2'; -export const SET_ALLOWANCE_APPROVAL_STATE = 'SET_ALLOWANCE_APPROVAL_STATE'; -export const SET_EXCHANGE_INPUT_VALUE = 'SET_EXCHANGE_INPUT_VALUE'; -export const SET_EXCHANGE_OUTPUT_VALUE = 'SET_EXCHANGE_OUTPUT_VALUE'; -export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE'; -export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE'; -export const SET_INVEST_TOKEN = 'SET_INVEST_TOKEN'; -export const SET_INVEST_ETH_POOL = 'SET_INVEST_ETH'; -export const SET_INVEST_TOKEN_POOL = 'SET_INVEST_TOKENS'; -export const SET_INVEST_TOKEN_ALLOWANCE = 'SET_INVEST_TOKEN_ALLOWANCE'; -export const SET_INVEST_SHARES = 'SET_INVEST_SHARES'; -export const SET_USER_SHARES = 'SET_USER_SHARES'; -export const SET_INVEST_TOKEN_BALANCE = 'SET_INVEST_TOKEN_BALANCE'; -export const SET_INVEST_ETH_BALANCE = 'SET_INVEST_ETH_BALANCE'; -export const SET_INVEST_SHARES_INPUT = 'SET_INVEST_SHARES_INPUT'; -export const SET_INVEST_ETH_REQUIRED = 'SET_INVEST_ETH_REQUIRED'; -export const SET_INVEST_TOKENS_REQUIRED = 'SET_INVEST_TOKENS_REQUIRED'; -export const SET_INVEST_CHECKED = 'SET_INVEST_CHECKED'; +export const SET_INPUT_BALANCE = 'SET_INPUT_BALANCE' +export const SET_OUTPUT_BALANCE = 'SET_OUTPUT_BALANCE' +export const SET_INPUT_TOKEN = 'SET_INPUT_TOKEN' +export const SET_OUTPUT_TOKEN = 'SET_OUTPUT_TOKEN' +export const SET_ETH_POOL_1 = 'SET_ETH_POOL_1' +export const SET_ETH_POOL_2 = 'SET_ETH_POOL_2' +export const SET_TOKEN_POOL_1 = 'SET_TOKEN_POOL_1' +export const SET_TOKEN_POOL_2 = 'SET_TOKEN_POOL_2' +export const SET_ALLOWANCE_APPROVAL_STATE = 'SET_ALLOWANCE_APPROVAL_STATE' +export const SET_EXCHANGE_INPUT_VALUE = 'SET_EXCHANGE_INPUT_VALUE' +export const SET_EXCHANGE_OUTPUT_VALUE = 'SET_EXCHANGE_OUTPUT_VALUE' +export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE' +export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE' +export const SET_INVEST_TOKEN = 'SET_INVEST_TOKEN' +export const SET_INVEST_ETH_POOL = 'SET_INVEST_ETH' +export const SET_INVEST_TOKEN_POOL = 'SET_INVEST_TOKENS' +export const SET_INVEST_TOKEN_ALLOWANCE = 'SET_INVEST_TOKEN_ALLOWANCE' +export const SET_INVEST_SHARES = 'SET_INVEST_SHARES' +export const SET_USER_SHARES = 'SET_USER_SHARES' +export const SET_INVEST_TOKEN_BALANCE = 'SET_INVEST_TOKEN_BALANCE' +export const SET_INVEST_ETH_BALANCE = 'SET_INVEST_ETH_BALANCE' +export const SET_INVEST_SHARES_INPUT = 'SET_INVEST_SHARES_INPUT' +export const SET_INVEST_ETH_REQUIRED = 'SET_INVEST_ETH_REQUIRED' +export const SET_INVEST_TOKENS_REQUIRED = 'SET_INVEST_TOKENS_REQUIRED' +export const SET_INVEST_CHECKED = 'SET_INVEST_CHECKED' diff --git a/src/constants/currencyInputErrorTypes.js b/src/constants/currencyInputErrorTypes.js index dbaa1f3859f..79004fdf892 100644 --- a/src/constants/currencyInputErrorTypes.js +++ b/src/constants/currencyInputErrorTypes.js @@ -1 +1 @@ -export const INSUFFICIENT_BALANCE = 'Insufficient balance'; +export const INSUFFICIENT_BALANCE = 'Insufficient balance' diff --git a/src/ducks/addresses.js b/src/ducks/addresses.js index 4d9d93f8c11..73ca144e3f3 100644 --- a/src/ducks/addresses.js +++ b/src/ducks/addresses.js @@ -2,30 +2,30 @@ const RINKEBY = { factoryAddress: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36', exchangeAddresses: { addresses: [ - ['BAT','0x9B913956036a3462330B0642B20D3879ce68b450'], - ['DAI','0x77dB9C915809e7BE439D2AB21032B1b8B58F6891'], - ['MKR','0x93bB63aFe1E0180d0eF100D774B473034fd60C36'], - ['OMG','0x26C226EBb6104676E593F8A070aD6f25cDa60F8D'], + ['BAT', '0x9B913956036a3462330B0642B20D3879ce68b450'], + ['DAI', '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891'], + ['MKR', '0x93bB63aFe1E0180d0eF100D774B473034fd60C36'], + ['OMG', '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D'] // ['ZRX','0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D'], ], fromToken: { '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B': '0x9B913956036a3462330B0642B20D3879ce68b450', '0x2448eE2641d78CC42D7AD76498917359D961A783': '0x77dB9C915809e7BE439D2AB21032B1b8B58F6891', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85': '0x93bB63aFe1E0180d0eF100D774B473034fd60C36', - '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D', + '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0': '0x26C226EBb6104676E593F8A070aD6f25cDa60F8D' // '0xF22e3F33768354c9805d046af3C0926f27741B43': '0xaBD44a1D1b9Fb0F39fE1D1ee6b1e2a14916D067D', - }, + } }, tokenAddresses: { addresses: [ - ['BAT','0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B'], - ['DAI','0x2448eE2641d78CC42D7AD76498917359D961A783'], - ['MKR','0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'], - ['OMG','0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0'], + ['BAT', '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B'], + ['DAI', '0x2448eE2641d78CC42D7AD76498917359D961A783'], + ['MKR', '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'], + ['OMG', '0x879884c3C46A24f56089f3bBbe4d5e38dB5788C0'] // ['ZRX','0xF22e3F33768354c9805d046af3C0926f27741B43'], - ], - }, -}; + ] + } +} const MAIN = { factoryAddress: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95', @@ -73,7 +73,7 @@ const MAIN = { ['WETH', '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb'], ['XCHF', '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'], ['ZIL', '0x7dc095A5CF7D6208CC680fA9866F80a53911041a'], - ['ZRX', '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF'], + ['ZRX', '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF'] ], fromToken: { '0x960b236A07cf122663c4303350609A66A7B288C0': '0x077d52B047735976dfdA76feF74d4d988AC25196', @@ -118,8 +118,8 @@ const MAIN = { '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27': '0x7dc095A5CF7D6208CC680fA9866F80a53911041a', '0xE41d2489571d322189246DaFA5ebDe1F4699F498': '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF', '0x3772f9716Cf6D7a09edE3587738AA2af5577483a': '0x5d8888a212d033cff5f2e0ac24ad91a5495bad62', - '0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25': '0xa1ecdcca26150cf69090280ee2ee32347c238c7b', - }, + '0x0cbe2df57ca9191b64a7af3baa3f946fa7df2f25': '0xa1ecdcca26150cf69090280ee2ee32347c238c7b' + } }, tokenAddresses: { addresses: [ @@ -165,94 +165,90 @@ const MAIN = { ['WETH', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'], ['XCHF', '0xB4272071eCAdd69d933AdcD19cA99fe80664fc08'], ['ZIL', '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27'], - ['ZRX', '0xE41d2489571d322189246DaFA5ebDe1F4699F498'], - ], - }, -}; + ['ZRX', '0xE41d2489571d322189246DaFA5ebDe1F4699F498'] + ] + } +} -const SET_ADDRESSES = 'app/addresses/setAddresses'; -const ADD_EXCHANGE = 'app/addresses/addExchange'; +const SET_ADDRESSES = 'app/addresses/setAddresses' +const ADD_EXCHANGE = 'app/addresses/addExchange' -const initialState = RINKEBY; +const initialState = RINKEBY -export const addExchange = ({label, exchangeAddress, tokenAddress}) => (dispatch, getState) => { - const { addresses: { tokenAddresses, exchangeAddresses } } = getState(); +export const addExchange = ({ label, exchangeAddress, tokenAddress }) => (dispatch, getState) => { + const { + addresses: { tokenAddresses, exchangeAddresses } + } = getState() - if (tokenAddresses.addresses.filter(([ symbol ]) => symbol === label).length) { - return; + if (tokenAddresses.addresses.filter(([symbol]) => symbol === label).length) { + return } if (exchangeAddresses.fromToken[tokenAddresses]) { - return; + return } dispatch({ type: ADD_EXCHANGE, - payload: { + payload: { label, - exchangeAddress, - tokenAddress, - }, - }); -}; + exchangeAddress, + tokenAddress + } + }) +} export const setAddresses = networkId => { - switch(networkId) { + switch (networkId) { // Main Net case 1: case '1': return { type: SET_ADDRESSES, - payload: MAIN, - }; + payload: MAIN + } // Rinkeby case 4: case '4': default: return { type: SET_ADDRESSES, - payload: RINKEBY, - }; + payload: RINKEBY + } } -}; +} export default (state = initialState, { type, payload }) => { switch (type) { case SET_ADDRESSES: - return payload; + return payload case ADD_EXCHANGE: - return handleAddExchange(state, { payload }); + return handleAddExchange(state, { payload }) default: - return state; + return state } } function handleAddExchange(state, { payload }) { - const { label, tokenAddress, exchangeAddress } = payload; + const { label, tokenAddress, exchangeAddress } = payload if (!label || !tokenAddress || !exchangeAddress) { - return state; + return state } return { ...state, exchangeAddresses: { ...state.exchangeAddresses, - addresses: [ - ...state.exchangeAddresses.addresses, - [label, exchangeAddress] - ], + addresses: [...state.exchangeAddresses.addresses, [label, exchangeAddress]], fromToken: { ...state.exchangeAddresses.fromToken, - [tokenAddress]: exchangeAddress, - }, + [tokenAddress]: exchangeAddress + } }, tokenAddresses: { ...state.tokenAddresses, - addresses: [ - ...state.tokenAddresses.addresses, - [label, tokenAddress] - ], - }, - }; + addresses: [...state.tokenAddresses.addresses, [label, tokenAddress]] + } + } } diff --git a/src/ducks/app.js b/src/ducks/app.js index ca1f3dc5a4c..7b87a447c2a 100644 --- a/src/ducks/app.js +++ b/src/ducks/app.js @@ -1,16 +1,16 @@ -const DISMISS_BETA_MESSAGE = 'app/app/dismissBetaMessage'; +const DISMISS_BETA_MESSAGE = 'app/app/dismissBetaMessage' const initialState = { - showBetaMessage: true, -}; + showBetaMessage: true +} -export const dismissBetaMessage = () => ({ type: DISMISS_BETA_MESSAGE }); +export const dismissBetaMessage = () => ({ type: DISMISS_BETA_MESSAGE }) export default function appReducer(state = initialState, { type, payload }) { switch (type) { case DISMISS_BETA_MESSAGE: - return { ...state, showBetaMessage: false }; + return { ...state, showBetaMessage: false } default: - return state; + return state } } diff --git a/src/ducks/index.js b/src/ducks/index.js index 5cd72ac9b76..e8c847cf96b 100644 --- a/src/ducks/index.js +++ b/src/ducks/index.js @@ -1,12 +1,12 @@ -import { combineReducers } from 'redux'; -import addresses from './addresses'; -import app from './app'; -import pending from './pending'; -import web3connect from './web3connect'; +import { combineReducers } from 'redux' +import addresses from './addresses' +import app from './app' +import pending from './pending' +import web3connect from './web3connect' export default combineReducers({ app, addresses, pending, - web3connect, -}); + web3connect +}) diff --git a/src/ducks/pending.js b/src/ducks/pending.js index 09e0c757a81..9d8019f3142 100644 --- a/src/ducks/pending.js +++ b/src/ducks/pending.js @@ -1,15 +1,15 @@ -const ADD_APPROVAL_TX = 'app/send/addApprovalTx'; +const ADD_APPROVAL_TX = 'app/send/addApprovalTx' const getInitialState = () => { return { - approvals: {}, - }; -}; + approvals: {} + } +} export const addApprovalTx = ({ tokenAddress, txId }) => ({ type: ADD_APPROVAL_TX, - payload: { tokenAddress, txId }, -}); + payload: { tokenAddress, txId } +}) export default function sendReducer(state = getInitialState(), { type, payload }) { switch (type) { @@ -17,10 +17,10 @@ export default function sendReducer(state = getInitialState(), { type, payload } return { approvals: { ...state.approvals, - [payload.tokenAddress]: payload.txId, + [payload.tokenAddress]: payload.txId } - }; + } default: - return state; + return state } } diff --git a/src/ducks/web3connect.js b/src/ducks/web3connect.js index 177814f1902..ee1e67780e1 100644 --- a/src/ducks/web3connect.js +++ b/src/ducks/web3connect.js @@ -1,24 +1,24 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import {BigNumber as BN} from 'bignumber.js'; -import Web3 from 'web3'; -import ERC20_ABI from "../abi/erc20"; -import ERC20_WITH_BYTES_ABI from "../abi/erc20_symbol_bytes32"; - -export const INITIALIZE = 'we3connect/initialize'; -export const UPDATE_ACCOUNT = 'we3connect/updateAccount'; -export const WATCH_ETH_BALANCE = 'web3connect/watchEthBalance'; -export const WATCH_TOKEN_BALANCE = 'web3connect/watchTokenBalance'; -export const UPDATE_ETH_BALANCE = 'web3connect/updateEthBalance'; -export const UPDATE_TOKEN_BALANCE = 'web3connect/updateTokenBalance'; -export const WATCH_APPROVALS = 'web3connect/watchApprovals'; -export const UPDATE_APPROVALS = 'web3connect/updateApprovals'; -export const ADD_CONTRACT = 'web3connect/addContract'; -export const UPDATE_NETWORK_ID = 'web3connect/updateNetworkId'; -export const ADD_PENDING_TX = 'web3connect/addPendingTx'; -export const REMOVE_PENDING_TX = 'web3connect/removePendingTx'; -export const ADD_CONFIRMED_TX = 'web3connect/addConfirmedTx'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { BigNumber as BN } from 'bignumber.js' +import Web3 from 'web3' +import ERC20_ABI from '../abi/erc20' +import ERC20_WITH_BYTES_ABI from '../abi/erc20_symbol_bytes32' + +export const INITIALIZE = 'we3connect/initialize' +export const UPDATE_ACCOUNT = 'we3connect/updateAccount' +export const WATCH_ETH_BALANCE = 'web3connect/watchEthBalance' +export const WATCH_TOKEN_BALANCE = 'web3connect/watchTokenBalance' +export const UPDATE_ETH_BALANCE = 'web3connect/updateEthBalance' +export const UPDATE_TOKEN_BALANCE = 'web3connect/updateTokenBalance' +export const WATCH_APPROVALS = 'web3connect/watchApprovals' +export const UPDATE_APPROVALS = 'web3connect/updateApprovals' +export const ADD_CONTRACT = 'web3connect/addContract' +export const UPDATE_NETWORK_ID = 'web3connect/updateNetworkId' +export const ADD_PENDING_TX = 'web3connect/addPendingTx' +export const REMOVE_PENDING_TX = 'web3connect/removePendingTx' +export const ADD_CONFIRMED_TX = 'web3connect/addConfirmedTx' const initialState = { web3: null, @@ -26,181 +26,183 @@ const initialState = { initialized: false, account: '', balances: { - ethereum: {}, + ethereum: {} }, approvals: { '0x0': { TOKEN_OWNER: { - SPENDER: {}, - }, - }, + SPENDER: {} + } + } }, transactions: { pending: [], - confirmed: [], + confirmed: [] }, watched: { balances: { - ethereum: [], + ethereum: [] }, - approvals: {}, + approvals: {} }, - contracts: {}, -}; + contracts: {} +} // selectors export const selectors = () => (dispatch, getState) => { - const state = getState().web3connect; + const state = getState().web3connect const getTokenBalance = (tokenAddress, address) => { - const tokenBalances = state.balances[tokenAddress] || {}; - const balance = tokenBalances[address]; + const tokenBalances = state.balances[tokenAddress] || {} + const balance = tokenBalances[address] if (!balance) { - dispatch(watchBalance({ balanceOf: address, tokenAddress })); - return Balance(0); + dispatch(watchBalance({ balanceOf: address, tokenAddress })) + return Balance(0) } - return balance; - }; + return balance + } const getBalance = (address, tokenAddress) => { if (process.env.NODE_ENV !== 'production' && !tokenAddress) { - console.warn('No token address found - return ETH balance'); + console.warn('No token address found - return ETH balance') } if (!tokenAddress || tokenAddress === 'ETH') { - const balance = state.balances.ethereum[address]; + const balance = state.balances.ethereum[address] if (!balance) { - dispatch(watchBalance({ balanceOf: address })); - return Balance(0, 'ETH'); + dispatch(watchBalance({ balanceOf: address })) + return Balance(0, 'ETH') } - return balance; + return balance } else if (tokenAddress) { - return getTokenBalance(tokenAddress, address); + return getTokenBalance(tokenAddress, address) } - return Balance(NaN); - }; + return Balance(NaN) + } const getApprovals = (tokenAddress, tokenOwner, spender) => { - const token = state.approvals[tokenAddress] || {}; - const owner = token[tokenOwner] || {}; + const token = state.approvals[tokenAddress] || {} + const owner = token[tokenOwner] || {} if (!owner[spender]) { - dispatch(watchApprovals({ tokenAddress, tokenOwner, spender })); - return Balance(0); + dispatch(watchApprovals({ tokenAddress, tokenOwner, spender })) + return Balance(0) } - return owner[spender]; - }; + return owner[spender] + } return { getBalance, getTokenBalance, - getApprovals, - }; -}; + getApprovals + } +} const Balance = (value, label = '', decimals = 0) => ({ value: BN(value), label: label.toUpperCase(), - decimals: +decimals, -}); + decimals: +decimals +}) export const initialize = () => (dispatch, getState) => { - const { web3connect } = getState(); + const { web3connect } = getState() return new Promise(async (resolve, reject) => { if (web3connect.web3) { - resolve(web3connect.web3); - return; + resolve(web3connect.web3) + return } if (typeof window.ethereum !== 'undefined') { try { - const web3 = new Web3(window.ethereum); - await window.ethereum.enable(); + const web3 = new Web3(window.ethereum) + await window.ethereum.enable() dispatch({ type: INITIALIZE, - payload: web3, - }); - resolve(web3); - return; + payload: web3 + }) + resolve(web3) + return } catch (error) { - console.error('User denied access.'); - dispatch({ type: INITIALIZE }); - reject(); - return; + console.error('User denied access.') + dispatch({ type: INITIALIZE }) + reject() + return } } if (typeof window.web3 !== 'undefined') { - const web3 = new Web3(window.web3.currentProvider); + const web3 = new Web3(window.web3.currentProvider) dispatch({ type: INITIALIZE, - payload: web3, - }); - resolve(web3); - return; + payload: web3 + }) + resolve(web3) + return } - dispatch({ type: INITIALIZE }); - reject(); + dispatch({ type: INITIALIZE }) + reject() }) -}; +} export const watchBalance = ({ balanceOf, tokenAddress }) => (dispatch, getState) => { if (!balanceOf) { - return; + return } - const { web3connect } = getState(); - const { watched } = web3connect; + const { web3connect } = getState() + const { watched } = web3connect if (!tokenAddress) { if (watched.balances.ethereum.includes(balanceOf)) { - return; + return } dispatch({ type: WATCH_ETH_BALANCE, - payload: balanceOf, - }); - setTimeout(() => dispatch(sync()), 0); + payload: balanceOf + }) + setTimeout(() => dispatch(sync()), 0) } else if (tokenAddress) { if (watched.balances[tokenAddress] && watched.balances[tokenAddress].includes(balanceOf)) { - return; + return } dispatch({ type: WATCH_TOKEN_BALANCE, payload: { tokenAddress, - balanceOf, - }, - }); - setTimeout(() => dispatch(sync()), 0); + balanceOf + } + }) + setTimeout(() => dispatch(sync()), 0) } -}; +} export const watchApprovals = ({ tokenAddress, tokenOwner, spender }) => (dispatch, getState) => { - const { web3connect: { watched } } = getState(); - const token = watched.approvals[tokenAddress] || {}; - const owner = token[tokenOwner] || []; + const { + web3connect: { watched } + } = getState() + const token = watched.approvals[tokenAddress] || {} + const owner = token[tokenOwner] || [] if (owner.includes(spender)) { - return; + return } return dispatch({ type: WATCH_APPROVALS, payload: { tokenAddress, tokenOwner, - spender, - }, - }); -}; + spender + } + }) +} export const addPendingTx = txId => ({ type: ADD_PENDING_TX, - payload: txId, -}); + payload: txId +}) export const updateApprovals = ({ tokenAddress, tokenOwner, spender, balance }) => ({ type: UPDATE_APPROVALS, @@ -208,181 +210,187 @@ export const updateApprovals = ({ tokenAddress, tokenOwner, spender, balance }) tokenAddress, tokenOwner, spender, - balance, - }, -}); + balance + } +}) export const sync = () => async (dispatch, getState) => { - const { getBalance, getApprovals } = dispatch(selectors()); - const web3 = await dispatch(initialize()); + const { getBalance, getApprovals } = dispatch(selectors()) + const web3 = await dispatch(initialize()) const { account, watched, contracts, networkId, - transactions: { pending }, - } = getState().web3connect; + transactions: { pending } + } = getState().web3connect // Sync Account - const accounts = await web3.eth.getAccounts(); + const accounts = await web3.eth.getAccounts() if (account !== accounts[0]) { - dispatch({ type: UPDATE_ACCOUNT, payload: accounts[0] }); - dispatch(watchBalance({ balanceOf: accounts[0] })); + dispatch({ type: UPDATE_ACCOUNT, payload: accounts[0] }) + dispatch(watchBalance({ balanceOf: accounts[0] })) } if (!networkId) { dispatch({ type: UPDATE_NETWORK_ID, - payload: await web3.eth.net.getId(), - }); + payload: await web3.eth.net.getId() + }) } // Sync Ethereum Balances watched.balances.ethereum.forEach(async address => { - const balance = await web3.eth.getBalance(address); - const { value } = getBalance(address); + const balance = await web3.eth.getBalance(address) + const { value } = getBalance(address) if (value.isEqualTo(BN(balance))) { - return; + return } dispatch({ type: UPDATE_ETH_BALANCE, payload: { balance: Balance(balance, 'ETH', 18), - balanceOf: address, - }, + balanceOf: address + } }) - }); + }) // Sync Token Balances - Object.keys(watched.balances) - .forEach(tokenAddress => { - if (tokenAddress === 'ethereum') { - return; - } + Object.keys(watched.balances).forEach(tokenAddress => { + if (tokenAddress === 'ethereum') { + return + } - const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress); - const contractBytes32 = contracts[tokenAddress] || new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress); + const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress) + const contractBytes32 = contracts[tokenAddress] || new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress) - if (!contracts[tokenAddress]) { - dispatch({ - type: ADD_CONTRACT, - payload: { - address: tokenAddress, - contract: contract, - }, - }); + if (!contracts[tokenAddress]) { + dispatch({ + type: ADD_CONTRACT, + payload: { + address: tokenAddress, + contract: contract + } + }) + } + + const watchlist = watched.balances[tokenAddress] || [] + watchlist.forEach(async address => { + const tokenBalance = getBalance(address, tokenAddress) + const balance = await contract.methods.balanceOf(address).call() + const decimals = tokenBalance.decimals || (await contract.methods.decimals().call()) + let symbol = tokenBalance.symbol + try { + symbol = + symbol || + (await contract.methods + .symbol() + .call() + .catch()) + } catch (e) { + try { + symbol = + symbol || + web3.utils.hexToString( + await contractBytes32.methods + .symbol() + .call() + .catch() + ) + } catch (err) {} + } + + if (tokenBalance.value.isEqualTo(BN(balance)) && tokenBalance.label && tokenBalance.decimals) { + return } - const watchlist = watched.balances[tokenAddress] || []; - watchlist.forEach(async address => { - const tokenBalance = getBalance(address, tokenAddress); - const balance = await contract.methods.balanceOf(address).call(); - const decimals = tokenBalance.decimals || await contract.methods.decimals().call(); - let symbol = tokenBalance.symbol; + dispatch({ + type: UPDATE_TOKEN_BALANCE, + payload: { + tokenAddress, + balanceOf: address, + balance: Balance(balance, symbol, decimals) + } + }) + }) + }) + + // Update Approvals + Object.entries(watched.approvals).forEach(([tokenAddress, token]) => { + const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress) + const contractBytes32 = contracts[tokenAddress] || new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress) + + Object.entries(token).forEach(([tokenOwnerAddress, tokenOwner]) => { + tokenOwner.forEach(async spenderAddress => { + const approvalBalance = getApprovals(tokenAddress, tokenOwnerAddress, spenderAddress) + const balance = await contract.methods.allowance(tokenOwnerAddress, spenderAddress).call() + const decimals = approvalBalance.decimals || (await contract.methods.decimals().call()) + let symbol = approvalBalance.label try { - symbol = symbol || await contract.methods.symbol().call().catch(); + symbol = symbol || (await contract.methods.symbol().call()) } catch (e) { try { - symbol = symbol || web3.utils.hexToString(await contractBytes32.methods.symbol().call().catch()); - } catch (err) { - } + symbol = symbol || web3.utils.hexToString(await contractBytes32.methods.symbol().call()) + } catch (err) {} } - if (tokenBalance.value.isEqualTo(BN(balance)) && tokenBalance.label && tokenBalance.decimals) { - return; + if (approvalBalance.label && approvalBalance.value.isEqualTo(BN(balance))) { + return } - dispatch({ - type: UPDATE_TOKEN_BALANCE, - payload: { + dispatch( + updateApprovals({ tokenAddress, - balanceOf: address, - balance: Balance(balance, symbol, decimals), - }, - }); - }); - }); - - // Update Approvals - Object.entries(watched.approvals) - .forEach(([tokenAddress, token]) => { - const contract = contracts[tokenAddress] || new web3.eth.Contract(ERC20_ABI, tokenAddress); - const contractBytes32 = contracts[tokenAddress] || new web3.eth.Contract(ERC20_WITH_BYTES_ABI, tokenAddress); - - Object.entries(token) - .forEach(([ tokenOwnerAddress, tokenOwner ]) => { - tokenOwner.forEach(async spenderAddress => { - const approvalBalance = getApprovals(tokenAddress, tokenOwnerAddress, spenderAddress); - const balance = await contract.methods.allowance(tokenOwnerAddress, spenderAddress).call(); - const decimals = approvalBalance.decimals || await contract.methods.decimals().call(); - let symbol = approvalBalance.label; - try { - symbol = symbol || await contract.methods.symbol().call(); - } catch (e) { - try { - symbol = symbol || web3.utils.hexToString(await contractBytes32.methods.symbol().call()); - } catch (err) { - } - } - - if (approvalBalance.label && approvalBalance.value.isEqualTo(BN(balance))) { - return; - } - - dispatch(updateApprovals({ - tokenAddress, - tokenOwner: tokenOwnerAddress, - spender: spenderAddress, - balance: Balance(balance, symbol, decimals), - })); - }); - }); - }); + tokenOwner: tokenOwnerAddress, + spender: spenderAddress, + balance: Balance(balance, symbol, decimals) + }) + ) + }) + }) + }) pending.forEach(async txId => { try { - const data = await web3.eth.getTransactionReceipt(txId) || {}; + const data = (await web3.eth.getTransactionReceipt(txId)) || {} // If data is an empty obj, then it's still pending. if (!('status' in data)) { - return; + return } dispatch({ type: REMOVE_PENDING_TX, - payload: txId, - }); + payload: txId + }) if (data.status) { dispatch({ type: ADD_CONFIRMED_TX, - payload: txId, - }); + payload: txId + }) } else { // TODO: dispatch ADD_REJECTED_TX } } catch (err) { dispatch({ type: REMOVE_PENDING_TX, - payload: txId, - }); + payload: txId + }) // TODO: dispatch ADD_REJECTED_TX } - - }); -}; + }) +} export const startWatching = () => async (dispatch, getState) => { - const { account } = getState().web3connect; - const timeout = !account - ? 1000 - : 5000; + const { account } = getState().web3connect + const timeout = !account ? 1000 : 5000 - dispatch(sync()); - setTimeout(() => dispatch(startWatching()), timeout); -}; + dispatch(sync()) + setTimeout(() => dispatch(startWatching()), timeout) +} export default function web3connectReducer(state = initialState, { type, payload }) { switch (type) { @@ -390,13 +398,13 @@ export default function web3connectReducer(state = initialState, { type, payload return { ...state, web3: payload, - initialized: true, - }; + initialized: true + } case UPDATE_ACCOUNT: return { ...state, - account: payload, - }; + account: payload + } case WATCH_ETH_BALANCE: return { ...state, @@ -404,14 +412,14 @@ export default function web3connectReducer(state = initialState, { type, payload ...state.watched, balances: { ...state.watched.balances, - ethereum: [ ...state.watched.balances.ethereum, payload ], - }, - }, - }; + ethereum: [...state.watched.balances.ethereum, payload] + } + } + } case WATCH_TOKEN_BALANCE: - const { watched } = state; - const { balances } = watched; - const watchlist = balances[payload.tokenAddress] || []; + const { watched } = state + const { balances } = watched + const watchlist = balances[payload.tokenAddress] || [] return { ...state, @@ -419,10 +427,10 @@ export default function web3connectReducer(state = initialState, { type, payload ...watched, balances: { ...balances, - [payload.tokenAddress]: [ ...watchlist, payload.balanceOf ], - }, - }, - }; + [payload.tokenAddress]: [...watchlist, payload.balanceOf] + } + } + } case UPDATE_ETH_BALANCE: return { ...state, @@ -430,33 +438,33 @@ export default function web3connectReducer(state = initialState, { type, payload ...state.balances, ethereum: { ...state.balances.ethereum, - [payload.balanceOf]: payload.balance, - }, - }, - }; + [payload.balanceOf]: payload.balance + } + } + } case UPDATE_TOKEN_BALANCE: - const tokenBalances = state.balances[payload.tokenAddress] || {}; + const tokenBalances = state.balances[payload.tokenAddress] || {} return { ...state, balances: { ...state.balances, [payload.tokenAddress]: { ...tokenBalances, - [payload.balanceOf]: payload.balance, - }, - }, - }; + [payload.balanceOf]: payload.balance + } + } + } case ADD_CONTRACT: return { ...state, contracts: { ...state.contracts, - [payload.address]: payload.contract, - }, - }; + [payload.address]: payload.contract + } + } case WATCH_APPROVALS: - const token = state.watched.approvals[payload.tokenAddress] || {}; - const tokenOwner = token[payload.tokenOwner] || []; + const token = state.watched.approvals[payload.tokenAddress] || {} + const tokenOwner = token[payload.tokenOwner] || [] return { ...state, @@ -466,14 +474,14 @@ export default function web3connectReducer(state = initialState, { type, payload ...state.watched.approvals, [payload.tokenAddress]: { ...token, - [payload.tokenOwner]: [ ...tokenOwner, payload.spender ], - }, - }, - }, - }; + [payload.tokenOwner]: [...tokenOwner, payload.spender] + } + } + } + } case UPDATE_APPROVALS: - const erc20 = state.approvals[payload.tokenAddress] || {}; - const erc20Owner = erc20[payload.tokenOwner] || {}; + const erc20 = state.approvals[payload.tokenAddress] || {} + const erc20Owner = erc20[payload.tokenOwner] || {} return { ...state, @@ -483,72 +491,71 @@ export default function web3connectReducer(state = initialState, { type, payload ...erc20, [payload.tokenOwner]: { ...erc20Owner, - [payload.spender]: payload.balance, - }, - }, - }, - }; + [payload.spender]: payload.balance + } + } + } + } case UPDATE_NETWORK_ID: - return { ...state, networkId: payload }; + return { ...state, networkId: payload } case ADD_PENDING_TX: return { ...state, transactions: { ...state.transactions, - pending: [ ...state.transactions.pending, payload ], - }, - }; + pending: [...state.transactions.pending, payload] + } + } case REMOVE_PENDING_TX: return { ...state, transactions: { ...state.transactions, - pending: state.transactions.pending.filter(id => id !== payload), - }, - }; + pending: state.transactions.pending.filter(id => id !== payload) + } + } case ADD_CONFIRMED_TX: if (state.transactions.confirmed.includes(payload)) { - return state; + return state } return { ...state, transactions: { ...state.transactions, - confirmed: [ ...state.transactions.confirmed, payload ], - }, - }; + confirmed: [...state.transactions.confirmed, payload] + } + } default: - return state; + return state } } // Connect Component export class _Web3Connect extends Component { static propTypes = { - initialize: PropTypes.func.isRequired, - }; + initialize: PropTypes.func.isRequired + } static defaultProps = { initialize() {} - }; + } componentWillMount() { - this.props.initialize() - .then(this.props.startWatching()); + this.props.initialize().then(this.props.startWatching()) } render() { - return
- ); + ) } } @@ -64,11 +64,11 @@ export default connect( state => ({ account: state.web3connect.account, initialized: state.web3connect.initialized, - web3: state.web3connect.web3, + web3: state.web3connect.web3 }), dispatch => ({ setAddresses: networkId => dispatch(setAddresses(networkId)), initialize: () => dispatch(initialize()), - startWatching: () => dispatch(startWatching()), - }), -)(App); + startWatching: () => dispatch(startWatching()) + }) +)(App) diff --git a/src/pages/App.scss b/src/pages/App.scss index 16e8f20c067..3c9842d3a5c 100644 --- a/src/pages/App.scss +++ b/src/pages/App.scss @@ -1,4 +1,4 @@ -@import "../variables.scss"; +@import '../variables.scss'; .app { &__wrapper { @@ -23,4 +23,4 @@ height: 100vh; @extend %col-nowrap; -} \ No newline at end of file +} diff --git a/src/pages/App.test.js b/src/pages/App.test.js index a754b201bf9..4bf19359ea4 100644 --- a/src/pages/App.test.js +++ b/src/pages/App.test.js @@ -1,9 +1,9 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; +import React from 'react' +import ReactDOM from 'react-dom' +import App from './App' it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); + const div = document.createElement('div') + ReactDOM.render(, div) + ReactDOM.unmountComponentAtNode(div) +}) diff --git a/src/pages/Pool/AddLiquidity.js b/src/pages/Pool/AddLiquidity.js index 453591a3033..7f7a06c51eb 100644 --- a/src/pages/Pool/AddLiquidity.js +++ b/src/pages/Pool/AddLiquidity.js @@ -1,25 +1,25 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import classnames from "classnames"; -import { withNamespaces } from 'react-i18next'; -import CurrencyInputPanel from '../../components/CurrencyInputPanel'; -import OversizedPanel from '../../components/OversizedPanel'; -import ContextualInfo from '../../components/ContextualInfo'; -import NavigationTabs from '../../components/NavigationTabs'; -import { selectors, addPendingTx } from '../../ducks/web3connect'; -import PlusBlue from '../../assets/images/plus-blue.svg'; -import PlusGrey from '../../assets/images/plus-grey.svg'; -import { getBlockDeadline } from '../../helpers/web3-utils'; -import { retry } from '../../helpers/promise-utils'; -import ModeSelector from './ModeSelector'; -import {BigNumber as BN} from 'bignumber.js'; -import EXCHANGE_ABI from '../../abi/exchange'; -import "./pool.scss"; -import ReactGA from "react-ga"; - -const INPUT = 0; -const OUTPUT = 1; +import React, { Component } from 'react' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { withNamespaces } from 'react-i18next' +import CurrencyInputPanel from '../../components/CurrencyInputPanel' +import OversizedPanel from '../../components/OversizedPanel' +import ContextualInfo from '../../components/ContextualInfo' +import NavigationTabs from '../../components/NavigationTabs' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import PlusBlue from '../../assets/images/plus-blue.svg' +import PlusGrey from '../../assets/images/plus-grey.svg' +import { getBlockDeadline } from '../../helpers/web3-utils' +import { retry } from '../../helpers/promise-utils' +import ModeSelector from './ModeSelector' +import { BigNumber as BN } from 'bignumber.js' +import EXCHANGE_ABI from '../../abi/exchange' +import './pool.scss' +import ReactGA from 'react-ga' + +const INPUT = 0 +const OUTPUT = 1 class AddLiquidity extends Component { static propTypes = { @@ -28,9 +28,9 @@ class AddLiquidity extends Component { selectors: PropTypes.func.isRequired, balances: PropTypes.object.isRequired, exchangeAddresses: PropTypes.shape({ - fromToken: PropTypes.object.isRequired, - }).isRequired, - }; + fromToken: PropTypes.object.isRequired + }).isRequired + } state = { inputValue: '', @@ -38,22 +38,23 @@ class AddLiquidity extends Component { inputCurrency: 'ETH', outputCurrency: '', lastEditedField: '', - totalSupply: BN(0), - }; + totalSupply: BN(0) + } reset = () => { this.setState({ inputValue: '', outputValue: '', - lastEditedField: '', - }); - }; + lastEditedField: '' + }) + } shouldComponentUpdate(nextProps, nextState) { - const { t, isConnected, account, exchangeAddresses, balances, web3 } = this.props; - const { inputValue, outputValue, inputCurrency, outputCurrency, lastEditedField } = this.state; + const { t, isConnected, account, exchangeAddresses, balances, web3 } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency, lastEditedField } = this.state - return isConnected !== nextProps.isConnected || + return ( + isConnected !== nextProps.isConnected || t !== nextProps.t || account !== nextProps.account || exchangeAddresses !== nextProps.exchangeAddresses || @@ -63,308 +64,337 @@ class AddLiquidity extends Component { outputValue !== nextState.outputValue || inputCurrency !== nextState.inputCurrency || outputCurrency !== nextState.outputCurrency || - lastEditedField !== nextState.lastEditedField; + lastEditedField !== nextState.lastEditedField + ) } componentWillReceiveProps() { - this.recalcForm(); + this.recalcForm() } recalcForm = async () => { + const { outputCurrency, inputValue, outputValue, lastEditedField, totalSupply: oldTotalSupply } = this.state const { - outputCurrency, - inputValue, - outputValue, - lastEditedField, - totalSupply: oldTotalSupply, - } = this.state; - const { exchangeAddresses: { fromToken }, web3 } = this.props; - const exchangeAddress = fromToken[outputCurrency]; - const exchangeRate = this.getExchangeRate(); - const append = {}; + exchangeAddresses: { fromToken }, + web3 + } = this.props + const exchangeAddress = fromToken[outputCurrency] + const exchangeRate = this.getExchangeRate() + const append = {} if (!outputCurrency || this.isNewExchange() || !web3) { - return; + return } - const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress); - const totalSupply = await exchange.methods.totalSupply().call(); + const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress) + const totalSupply = await exchange.methods.totalSupply().call() if (!oldTotalSupply.isEqualTo(BN(totalSupply))) { - append.totalSupply = BN(totalSupply); + append.totalSupply = BN(totalSupply) } if (lastEditedField === INPUT) { - const newOutputValue = exchangeRate.multipliedBy(inputValue).toFixed(7); + const newOutputValue = exchangeRate.multipliedBy(inputValue).toFixed(7) if (newOutputValue !== outputValue) { - append.outputValue = newOutputValue; + append.outputValue = newOutputValue } } if (lastEditedField === OUTPUT) { - const newInputValue = BN(outputValue).dividedBy(exchangeRate).toFixed(7); + const newInputValue = BN(outputValue) + .dividedBy(exchangeRate) + .toFixed(7) if (newInputValue !== inputValue) { - append.inputValue = newInputValue; + append.inputValue = newInputValue } } - this.setState(append); - }; + this.setState(append) + } getBalance(currency) { - const { t, selectors, account } = this.props; + const { t, selectors, account } = this.props if (!currency) { - return ''; + return '' } - const { value, decimals } = selectors().getBalance(account, currency); + const { value, decimals } = selectors().getBalance(account, currency) if (!decimals) { - return ''; + return '' } - const balanceInput = value.dividedBy(10 ** decimals).toFixed(4); - return t("balance", { balanceInput }); + const balanceInput = value.dividedBy(10 ** decimals).toFixed(4) + return t('balance', { balanceInput }) } isUnapproved() { - const { account, exchangeAddresses, selectors } = this.props; - const { outputCurrency, outputValue } = this.state; + const { account, exchangeAddresses, selectors } = this.props + const { outputCurrency, outputValue } = this.state if (!outputCurrency) { - return false; + return false } const { value: allowance, label, decimals } = selectors().getApprovals( outputCurrency, account, exchangeAddresses.fromToken[outputCurrency] - ); + ) if (label && allowance.isLessThan(BN(outputValue * 10 ** decimals || 0))) { - return true; + return true } - return false; + return false } onAddLiquidity = async () => { - const { account, web3, exchangeAddresses: { fromToken }, selectors } = this.props; - const { inputValue, outputValue, outputCurrency } = this.state; - const exchange = new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]); - - const ethAmount = BN(inputValue).multipliedBy(10 ** 18); - const { decimals } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]); - const tokenAmount = BN(outputValue).multipliedBy(10 ** decimals); - const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency]); - const totalLiquidity = await exchange.methods.totalSupply().call(); - const liquidityMinted = BN(totalLiquidity).multipliedBy(ethAmount.dividedBy(ethReserve)); - let deadline; + const { + account, + web3, + exchangeAddresses: { fromToken }, + selectors + } = this.props + const { inputValue, outputValue, outputCurrency } = this.state + const exchange = new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]) + + const ethAmount = BN(inputValue).multipliedBy(10 ** 18) + const { decimals } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]) + const tokenAmount = BN(outputValue).multipliedBy(10 ** decimals) + const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency]) + const totalLiquidity = await exchange.methods.totalSupply().call() + const liquidityMinted = BN(totalLiquidity).multipliedBy(ethAmount.dividedBy(ethReserve)) + let deadline try { - deadline = await retry(() => getBlockDeadline(web3, 600)); - } catch(e) { + deadline = await retry(() => getBlockDeadline(web3, 600)) + } catch (e) { // TODO: Handle error. - return; + return } - const MAX_LIQUIDITY_SLIPPAGE = 0.025; - const minLiquidity = this.isNewExchange() ? BN(0) : liquidityMinted.multipliedBy(1 - MAX_LIQUIDITY_SLIPPAGE); - const maxTokens = this.isNewExchange() ? tokenAmount : tokenAmount.multipliedBy(1 + MAX_LIQUIDITY_SLIPPAGE); + const MAX_LIQUIDITY_SLIPPAGE = 0.025 + const minLiquidity = this.isNewExchange() ? BN(0) : liquidityMinted.multipliedBy(1 - MAX_LIQUIDITY_SLIPPAGE) + const maxTokens = this.isNewExchange() ? tokenAmount : tokenAmount.multipliedBy(1 + MAX_LIQUIDITY_SLIPPAGE) try { - exchange.methods.addLiquidity(minLiquidity.toFixed(0), maxTokens.toFixed(0), deadline).send({ - from: account, - value: ethAmount.toFixed(0) - }, (err, data) => { - this.reset(); - this.props.addPendingTx(data); - if (data) { - ReactGA.event({ - category: 'Pool', - action: 'AddLiquidity', - }); + exchange.methods.addLiquidity(minLiquidity.toFixed(0), maxTokens.toFixed(0), deadline).send( + { + from: account, + value: ethAmount.toFixed(0) + }, + (err, data) => { + this.reset() + this.props.addPendingTx(data) + if (data) { + ReactGA.event({ + category: 'Pool', + action: 'AddLiquidity' + }) + } } - }); + ) } catch (err) { - console.error(err); + console.error(err) } - }; + } onInputChange = value => { - const { inputCurrency, outputCurrency } = this.state; - const exchangeRate = this.getExchangeRate(); - let outputValue; + const { inputCurrency, outputCurrency } = this.state + const exchangeRate = this.getExchangeRate() + let outputValue if (inputCurrency === 'ETH' && outputCurrency && outputCurrency !== 'ETH') { - outputValue = exchangeRate.multipliedBy(value).toFixed(7); + outputValue = exchangeRate.multipliedBy(value).toFixed(7) } if (outputCurrency === 'ETH' && inputCurrency && inputCurrency !== 'ETH') { - outputValue = BN(value).dividedBy(exchangeRate).toFixed(7); + outputValue = BN(value) + .dividedBy(exchangeRate) + .toFixed(7) } const append = { inputValue: value, - lastEditedField: INPUT, - }; + lastEditedField: INPUT + } if (!this.isNewExchange()) { - append.outputValue = outputValue; + append.outputValue = outputValue } - this.setState(append); - }; + this.setState(append) + } onOutputChange = value => { - const { inputCurrency, outputCurrency } = this.state; - const exchangeRate = this.getExchangeRate(); - let inputValue; + const { inputCurrency, outputCurrency } = this.state + const exchangeRate = this.getExchangeRate() + let inputValue if (inputCurrency === 'ETH' && outputCurrency && outputCurrency !== 'ETH') { - inputValue = BN(value).dividedBy(exchangeRate).toFixed(7); + inputValue = BN(value) + .dividedBy(exchangeRate) + .toFixed(7) } if (outputCurrency === 'ETH' && inputCurrency && inputCurrency !== 'ETH') { - inputValue = exchangeRate.multipliedBy(value).toFixed(7); + inputValue = exchangeRate.multipliedBy(value).toFixed(7) } const append = { outputValue: value, - lastEditedField: INPUT, - }; + lastEditedField: INPUT + } if (!this.isNewExchange()) { - append.inputValue = inputValue; + append.inputValue = inputValue } - this.setState(append); - }; + this.setState(append) + } isNewExchange() { - const { selectors, exchangeAddresses: { fromToken } } = this.props; - const { inputCurrency, outputCurrency } = this.state; - const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]; - const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]; + const { + selectors, + exchangeAddresses: { fromToken } + } = this.props + const { inputCurrency, outputCurrency } = this.state + const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0] + const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0] if (!eth || !token) { - return false; + return false } - const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token); - const { value: ethValue } = selectors().getBalance(fromToken[token], eth); + const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token) + const { value: ethValue } = selectors().getBalance(fromToken[token], eth) - return tokenValue.isZero() && ethValue.isZero() && decimals !== 0; + return tokenValue.isZero() && ethValue.isZero() && decimals !== 0 } getExchangeRate() { - const { selectors, exchangeAddresses: { fromToken } } = this.props; - const { inputCurrency, outputCurrency } = this.state; - const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]; - const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]; + const { + selectors, + exchangeAddresses: { fromToken } + } = this.props + const { inputCurrency, outputCurrency } = this.state + const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0] + const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0] if (!eth || !token) { - return; + return } - const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token); - const { value: ethValue } = selectors().getBalance(fromToken[token], eth); + const { value: tokenValue, decimals } = selectors().getBalance(fromToken[token], token) + const { value: ethValue } = selectors().getBalance(fromToken[token], eth) - return tokenValue.multipliedBy(10 ** (18 - decimals)).dividedBy(ethValue); + return tokenValue.multipliedBy(10 ** (18 - decimals)).dividedBy(ethValue) } validate() { - const { t, selectors, account } = this.props; - const { - inputValue, outputValue, - inputCurrency, outputCurrency, - } = this.state; - - let inputError; - let outputError; - let isValid = true; - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); - - if (!inputValue || inputIsZero || !outputValue || outputIsZero || !inputCurrency || !outputCurrency || this.isUnapproved()) { - isValid = false; + const { t, selectors, account } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state + + let inputError + let outputError + let isValid = true + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() + + if ( + !inputValue || + inputIsZero || + !outputValue || + outputIsZero || + !inputCurrency || + !outputCurrency || + this.isUnapproved() + ) { + isValid = false } - const { value: ethValue } = selectors().getBalance(account, inputCurrency); - const { value: tokenValue, decimals } = selectors().getBalance(account, outputCurrency); + const { value: ethValue } = selectors().getBalance(account, inputCurrency) + const { value: tokenValue, decimals } = selectors().getBalance(account, outputCurrency) if (ethValue.isLessThan(BN(inputValue * 10 ** 18))) { - inputError = t("insufficientBalance"); + inputError = t('insufficientBalance') } if (tokenValue.isLessThan(BN(outputValue * 10 ** decimals))) { - outputError = t("insufficientBalance"); + outputError = t('insufficientBalance') } return { inputError, outputError, - isValid: isValid && !inputError && !outputError, - }; + isValid: isValid && !inputError && !outputError + } } renderInfo() { - const t = this.props.t; + const t = this.props.t const blank = (
- {t("exchangeRate")} + {t('exchangeRate')} -
- {t("currentPoolSize")} + {t('currentPoolSize')} -
- {t("yourPoolShare")} + {t('yourPoolShare')} -
- ); + ) - const { selectors, exchangeAddresses: { fromToken }, account } = this.props; - const { getBalance } = selectors(); - const { inputCurrency, outputCurrency, inputValue, outputValue, totalSupply } = this.state; - const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0]; - const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]; - const exchangeAddress = fromToken[token]; + const { + selectors, + exchangeAddresses: { fromToken }, + account + } = this.props + const { getBalance } = selectors() + const { inputCurrency, outputCurrency, inputValue, outputValue, totalSupply } = this.state + const eth = [inputCurrency, outputCurrency].filter(currency => currency === 'ETH')[0] + const token = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0] + const exchangeAddress = fromToken[token] if (!eth || !token || !exchangeAddress) { - return blank; + return blank } - const { value: tokenValue, decimals, label } = getBalance(exchangeAddress, token); - const { value: ethValue } = getBalance(exchangeAddress); - const { value: liquidityBalance } = getBalance(account, exchangeAddress); - const ownership = liquidityBalance.dividedBy(totalSupply); - const ethPer = ethValue.dividedBy(totalSupply); - const tokenPer = tokenValue.dividedBy(totalSupply); - const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18); - const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** decimals); + const { value: tokenValue, decimals, label } = getBalance(exchangeAddress, token) + const { value: ethValue } = getBalance(exchangeAddress) + const { value: liquidityBalance } = getBalance(account, exchangeAddress) + const ownership = liquidityBalance.dividedBy(totalSupply) + const ethPer = ethValue.dividedBy(totalSupply) + const tokenPer = tokenValue.dividedBy(totalSupply) + const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18) + const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** decimals) if (!label || !decimals) { - return blank; + return blank } if (this.isNewExchange()) { - const rate = BN(outputValue).dividedBy(inputValue); - const rateText = rate.isNaN() ? '---' : rate.toFixed(4); + const rate = BN(outputValue).dividedBy(inputValue) + const rateText = rate.isNaN() ? '---' : rate.toFixed(4) return (
- {t("exchangeRate")} + {t('exchangeRate')} {`1 ETH = ${rateText} ${label}`}
- {t("currentPoolSize")} - {` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`} + {t('currentPoolSize')} + {` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue + .dividedBy(10 ** decimals) + .toFixed(2)} ${label}`}
- {t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%) + {t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%) {`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}
@@ -373,23 +403,28 @@ class AddLiquidity extends Component { } if (tokenValue.dividedBy(ethValue).isNaN()) { - return blank; + return blank } return (
- {t("exchangeRate")} - {`1 ETH = ${tokenValue.multipliedBy(10 ** (18 - decimals)).dividedBy(ethValue).toFixed(4)} ${label}`} + {t('exchangeRate')} + {`1 ETH = ${tokenValue + .multipliedBy(10 ** (18 - decimals)) + .dividedBy(ethValue) + .toFixed(4)} ${label}`}
- {t("currentPoolSize")} - {` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`} + {t('currentPoolSize')} + {` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue + .dividedBy(10 ** decimals) + .toFixed(2)} ${label}`}
- - {t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%) - + + {t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%) + {`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}
@@ -397,93 +432,114 @@ class AddLiquidity extends Component { } renderSummary(inputError, outputError) { - const { t, selectors, exchangeAddresses: { fromToken } } = this.props; const { - inputValue, - outputValue, - inputCurrency, - outputCurrency, - } = this.state; - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); - - let contextualInfo = ''; - let isError = false; - const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]); + t, + selectors, + exchangeAddresses: { fromToken } + } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() + + let contextualInfo = '' + let isError = false + const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]) if (inputError || outputError) { - contextualInfo = inputError || outputError; - isError = true; + contextualInfo = inputError || outputError + isError = true } else if (!inputCurrency || !outputCurrency) { - contextualInfo = t("selectTokenCont"); + contextualInfo = t('selectTokenCont') } else if (inputCurrency === outputCurrency) { - contextualInfo = t("differentToken"); + contextualInfo = t('differentToken') } else if (![inputCurrency, outputCurrency].includes('ETH')) { - contextualInfo = t("mustBeETH"); + contextualInfo = t('mustBeETH') } else if (inputIsZero || outputIsZero) { - contextualInfo = t("noZero"); + contextualInfo = t('noZero') } else if (this.isUnapproved()) { - contextualInfo = t("unlockTokenCont"); + contextualInfo = t('unlockTokenCont') } else if (!inputValue || !outputValue) { - contextualInfo = t("enterCurrencyOrLabelCont", {inputCurrency, label}); + contextualInfo = t('enterCurrencyOrLabelCont', { inputCurrency, label }) } return ( - ); + ) } renderTransactionDetails = () => { - const { t, selectors, exchangeAddresses: { fromToken }, account } = this.props; const { - inputValue, - outputValue, - outputCurrency, - totalSupply, - } = this.state; + t, + selectors, + exchangeAddresses: { fromToken }, + account + } = this.props + const { inputValue, outputValue, outputCurrency, totalSupply } = this.state ReactGA.event({ category: 'TransactionDetail', - action: 'Open', - }); + action: 'Open' + }) - const { value: tokenReserve, label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]); - const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency]); - const { decimals: poolTokenDecimals } = selectors().getBalance(account, fromToken[outputCurrency]); + const { value: tokenReserve, label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]) + const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency]) + const { decimals: poolTokenDecimals } = selectors().getBalance(account, fromToken[outputCurrency]) if (this.isNewExchange()) { return (
-
{t("youAreAdding")} {b(`${inputValue} ETH`)} {t("and")} {b(`${outputValue} ${label}`)} {t("intoPool")}
-
{t("youAreSettingExRate")} {b(`1 ETH = ${BN(outputValue).dividedBy(inputValue).toFixed(4)} ${label}`)}.
-
{t("youWillMint")} {b(`${inputValue}`)} {t("liquidityTokens")}
-
{t("totalSupplyIs0")}
+
+ {t('youAreAdding')} {b(`${inputValue} ETH`)} {t('and')} {b(`${outputValue} ${label}`)} {t('intoPool')} +
+
+ {t('youAreSettingExRate')}{' '} + {b( + `1 ETH = ${BN(outputValue) + .dividedBy(inputValue) + .toFixed(4)} ${label}` + )} + . +
+
+ {t('youWillMint')} {b(`${inputValue}`)} {t('liquidityTokens')} +
+
{t('totalSupplyIs0')}
- ); + ) } - const SLIPPAGE = 0.025; - const minOutput = BN(outputValue).multipliedBy(1 - SLIPPAGE); - const maxOutput = BN(outputValue).multipliedBy(1 + SLIPPAGE); + const SLIPPAGE = 0.025 + const minOutput = BN(outputValue).multipliedBy(1 - SLIPPAGE) + const maxOutput = BN(outputValue).multipliedBy(1 + SLIPPAGE) // const minPercentage = minOutput.dividedBy(minOutput.plus(tokenReserve)).multipliedBy(100); // const maxPercentage = maxOutput.dividedBy(maxOutput.plus(tokenReserve)).multipliedBy(100); - const liquidityMinted = BN(inputValue).multipliedBy(totalSupply.dividedBy(ethReserve)); - const adjTotalSupply = totalSupply.dividedBy(10 ** poolTokenDecimals); + const liquidityMinted = BN(inputValue).multipliedBy(totalSupply.dividedBy(ethReserve)) + const adjTotalSupply = totalSupply.dividedBy(10 ** poolTokenDecimals) return (
-
{t("youAreAdding")} {b(`${+BN(inputValue).toFixed(7)} ETH`)} {t("and")} {b(`${+minOutput.toFixed(7)} - ${+maxOutput.toFixed(7)} ${label}`)} {t("intoPool")}
-
{t("youWillMint")} {b(+liquidityMinted.toFixed(7))} {t("liquidityTokens")}
-
{t("totalSupplyIs")} {b(+adjTotalSupply.toFixed(7))}
-
{t("tokenWorth")} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t("and")} {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}
+
+ {t('youAreAdding')} {b(`${+BN(inputValue).toFixed(7)} ETH`)} {t('and')}{' '} + {b(`${+minOutput.toFixed(7)} - ${+maxOutput.toFixed(7)} ${label}`)} {t('intoPool')} +
+
+ {t('youWillMint')} {b(+liquidityMinted.toFixed(7))} {t('liquidityTokens')} +
+
+ {t('totalSupplyIs')} {b(+adjTotalSupply.toFixed(7))} +
+
+ {t('tokenWorth')} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t('and')}{' '} + {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label} +
- ); + ) } render() { @@ -491,49 +547,41 @@ class AddLiquidity extends Component { t, isConnected, exchangeAddresses: { fromToken }, - selectors, - } = this.props; + selectors + } = this.props - const { - inputValue, - outputValue, - inputCurrency, - outputCurrency, - } = this.state; + const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state - const { inputError, outputError, isValid } = this.validate(); - const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]); + const { inputError, outputError, isValid } = this.validate() + const { label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]) return [
- { - this.isNewExchange() - ? ( -
-
- 🚰 {t("firstLiquidity")} -
-
- { t("initialExchangeRate", { label }) } -
-
- ) - : null - } - + {this.isNewExchange() ? ( +
+
+ + 🚰 + {' '} + {t('firstLiquidity')} +
+
{t('initialExchangeRate', { label })}
+
+ ) : null} +
- plus + plus
{ - this.setState({ - outputCurrency: currency, - }, this.recalcForm); + this.setState( + { + outputCurrency: currency + }, + this.recalcForm + ) }} onValueChange={this.onOutputChange} value={outputValue} errorMessage={outputError} - filteredTokens={[ 'ETH' ]} + filteredTokens={['ETH']} /> - - { this.renderInfo() } - - { this.renderSummary(inputError, outputError) } + {this.renderInfo()} + {this.renderSummary(inputError, outputError)}
- ]; + ] } } export default connect( state => ({ - isConnected: Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), + isConnected: + Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1), account: state.web3connect.account, balances: state.web3connect.balances, web3: state.web3connect.web3, - exchangeAddresses: state.addresses.exchangeAddresses, + exchangeAddresses: state.addresses.exchangeAddresses }), dispatch => ({ selectors: () => dispatch(selectors()), - addPendingTx: id => dispatch(addPendingTx(id)), + addPendingTx: id => dispatch(addPendingTx(id)) }) -)(withNamespaces()(AddLiquidity)); +)(withNamespaces()(AddLiquidity)) function b(text) { return {text} diff --git a/src/pages/Pool/CreateExchange.js b/src/pages/Pool/CreateExchange.js index ce0087200a7..9488a074c75 100644 --- a/src/pages/Pool/CreateExchange.js +++ b/src/pages/Pool/CreateExchange.js @@ -1,17 +1,17 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import { withRouter } from 'react-router-dom'; -import { withNamespaces } from 'react-i18next'; -import {selectors, addPendingTx} from "../../ducks/web3connect"; -import classnames from "classnames"; -import NavigationTabs from "../../components/NavigationTabs"; -import ModeSelector from "./ModeSelector"; -import AddressInputPanel from "../../components/AddressInputPanel"; -import OversizedPanel from "../../components/OversizedPanel"; -import FACTORY_ABI from "../../abi/factory"; -import {addExchange} from "../../ducks/addresses"; -import ReactGA from "react-ga"; +import React, { Component } from 'react' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import { withRouter } from 'react-router-dom' +import { withNamespaces } from 'react-i18next' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import classnames from 'classnames' +import NavigationTabs from '../../components/NavigationTabs' +import ModeSelector from './ModeSelector' +import AddressInputPanel from '../../components/AddressInputPanel' +import OversizedPanel from '../../components/OversizedPanel' +import FACTORY_ABI from '../../abi/factory' +import { addExchange } from '../../ducks/addresses' +import ReactGA from 'react-ga' class CreateExchange extends Component { static propTypes = { @@ -22,23 +22,27 @@ class CreateExchange extends Component { isConnected: PropTypes.bool.isRequired, factoryAddress: PropTypes.string.isRequired, exchangeAddresses: PropTypes.shape({ - fromToken: PropTypes.object.isRequired, - }).isRequired, - }; + fromToken: PropTypes.object.isRequired + }).isRequired + } constructor(props) { - super(props); - const { match: { params: { tokenAddress } } } = this.props; + super(props) + const { + match: { + params: { tokenAddress } + } + } = this.props this.state = { tokenAddress, label: '', - decimals: 0, - }; + decimals: 0 + } } validate() { - const { tokenAddress } = this.state; + const { tokenAddress } = this.state const { t, web3, @@ -46,104 +50,104 @@ class CreateExchange extends Component { selectors, factoryAddress, exchangeAddresses: { fromToken }, - addExchange, - } = this.props; + addExchange + } = this.props - let isValid = true; - let errorMessage = ''; + let isValid = true + let errorMessage = '' if (!tokenAddress) { return { - isValid: false, - }; + isValid: false + } } if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) { return { isValid: false, - errorMessage: t("invalidTokenAddress"), - }; + errorMessage: t('invalidTokenAddress') + } } - const { label, decimals } = selectors().getBalance(account, tokenAddress); - const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress); - const exchangeAddress = fromToken[tokenAddress]; + const { label, decimals } = selectors().getBalance(account, tokenAddress) + const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress) + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress) { factory.methods.getExchange(tokenAddress).call((err, data) => { if (!err && data !== '0x0000000000000000000000000000000000000000') { - addExchange({ label, tokenAddress, exchangeAddress: data }); + addExchange({ label, tokenAddress, exchangeAddress: data }) } - }); + }) } else { - errorMessage = t("exchangeExists", { label }); + errorMessage = t('exchangeExists', { label }) } if (!label) { - errorMessage = t("invalidSymbol"); + errorMessage = t('invalidSymbol') } if (!decimals) { - errorMessage = t("invalidDecimals"); + errorMessage = t('invalidDecimals') } return { isValid: isValid && !errorMessage, - errorMessage, - }; + errorMessage + } } onChange = tokenAddress => { - const { selectors, account, web3 } = this.props; + const { selectors, account, web3 } = this.props if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) { - const { label, decimals } = selectors().getBalance(account, tokenAddress); + const { label, decimals } = selectors().getBalance(account, tokenAddress) this.setState({ label, decimals, - tokenAddress, - }); + tokenAddress + }) } else { this.setState({ label: '', decimals: 0, - tokenAddress, - }); + tokenAddress + }) } - }; + } onCreateExchange = () => { - const { tokenAddress } = this.state; - const { account, web3, factoryAddress } = this.props; + const { tokenAddress } = this.state + const { account, web3, factoryAddress } = this.props if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) { - return; + return } - const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress); + const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress) factory.methods.createExchange(tokenAddress).send({ from: account }, (err, data) => { if (!err) { this.setState({ label: '', decimals: 0, - tokenAddress: '', - }); - this.props.addPendingTx(data); + tokenAddress: '' + }) + this.props.addPendingTx(data) ReactGA.event({ category: 'Pool', - action: 'CreateExchange', - }); + action: 'CreateExchange' + }) } }) - }; + } renderSummary() { - const { tokenAddress } = this.state; - const { errorMessage } = this.validate(); + const { tokenAddress } = this.state + const { errorMessage } = this.validate() if (!tokenAddress) { return (
-
{this.props.t("enterTokenCont")}
+
{this.props.t('enterTokenCont')}
) } @@ -156,36 +160,36 @@ class CreateExchange extends Component { ) } - return null; + return null } render() { - const { tokenAddress } = this.state; - const { t, isConnected, account, selectors, web3 } = this.props; - const { isValid, errorMessage } = this.validate(); - let label, decimals; + const { tokenAddress } = this.state + const { t, isConnected, account, selectors, web3 } = this.props + const { isValid, errorMessage } = this.validate() + let label, decimals if (web3 && web3.utils && web3.utils.isAddress(tokenAddress)) { - const { label: _label, decimals: _decimals } = selectors().getBalance(account, tokenAddress); - label = _label; - decimals = _decimals; + const { label: _label, decimals: _decimals } = selectors().getBalance(account, tokenAddress) + label = _label + decimals = _decimals } return (
- +
- {t("label")} + {t('label')} {label || ' - '}
- {t("decimals")} + {t('decimals')} {decimals || ' - '}
- { this.renderSummary() } + {this.renderSummary()}
- ); + ) } } export default withRouter( connect( state => ({ - isConnected: Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), + isConnected: + Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1), account: state.web3connect.account, balances: state.web3connect.balances, web3: state.web3connect.web3, exchangeAddresses: state.addresses.exchangeAddresses, - factoryAddress: state.addresses.factoryAddress, + factoryAddress: state.addresses.factoryAddress }), dispatch => ({ selectors: () => dispatch(selectors()), addExchange: opts => dispatch(addExchange(opts)), - addPendingTx: id => dispatch(addPendingTx(id)), + addPendingTx: id => dispatch(addPendingTx(id)) }) )(withNamespaces()(CreateExchange)) -); +) diff --git a/src/pages/Pool/ModeSelector.js b/src/pages/Pool/ModeSelector.js index a84c8de8043..7cf7f344334 100644 --- a/src/pages/Pool/ModeSelector.js +++ b/src/pages/Pool/ModeSelector.js @@ -1,44 +1,44 @@ -import React, { Component } from 'react'; -import { withRouter } from 'react-router-dom'; -import { withNamespaces } from 'react-i18next'; -import OversizedPanel from "../../components/OversizedPanel"; -import Dropdown from "../../assets/images/dropdown-blue.svg"; -import Modal from "../../components/Modal"; -import {CSSTransitionGroup} from "react-transition-group"; +import React, { Component } from 'react' +import { withRouter } from 'react-router-dom' +import { withNamespaces } from 'react-i18next' +import OversizedPanel from '../../components/OversizedPanel' +import Dropdown from '../../assets/images/dropdown-blue.svg' +import Modal from '../../components/Modal' +import { CSSTransitionGroup } from 'react-transition-group' -const ADD = 'Add Liquidity'; -const REMOVE = 'Remove Liquidity'; -const CREATE = 'Create Exchange'; +const ADD = 'Add Liquidity' +const REMOVE = 'Remove Liquidity' +const CREATE = 'Create Exchange' class ModeSelector extends Component { state = { isShowingModal: false, - selected: ADD, - }; + selected: ADD + } changeView(view) { - const { history } = this.props; + const { history } = this.props this.setState({ isShowingModal: false, - selected: view, - }); + selected: view + }) switch (view) { case ADD: - return history.push('/add-liquidity'); + return history.push('/add-liquidity') case REMOVE: - return history.push('/remove-liquidity'); + return history.push('/remove-liquidity') case CREATE: - return history.push('/create-exchange'); + return history.push('/create-exchange') default: - return; + return } } renderModal() { if (!this.state.isShowingModal) { - return; + return } return ( @@ -52,41 +52,27 @@ class ModeSelector extends Component { transitionEnterTimeout={200} >
-
this.changeView(ADD)} - > - {this.props.t("addLiquidity")} +
this.changeView(ADD)}> + {this.props.t('addLiquidity')}
-
this.changeView(REMOVE)} - > - {this.props.t("removeLiquidity")} +
this.changeView(REMOVE)}> + {this.props.t('removeLiquidity')}
-
this.changeView(CREATE)} - > - {this.props.t("createExchange")} +
this.changeView(CREATE)}> + {this.props.t('createExchange')}
- ); + ) } render() { return ( -
this.setState({ isShowingModal: true })} - > - - {this.props.title} - - dropdown +
this.setState({ isShowingModal: true })}> + {this.props.title} + dropdown
{this.renderModal()} @@ -94,4 +80,4 @@ class ModeSelector extends Component { } } -export default withRouter(withNamespaces()(ModeSelector)); +export default withRouter(withNamespaces()(ModeSelector)) diff --git a/src/pages/Pool/RemoveLiquidity.js b/src/pages/Pool/RemoveLiquidity.js index 38a68715606..be4a358e6c7 100644 --- a/src/pages/Pool/RemoveLiquidity.js +++ b/src/pages/Pool/RemoveLiquidity.js @@ -1,21 +1,21 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from "classnames"; -import { connect } from 'react-redux'; -import { BigNumber as BN } from 'bignumber.js'; -import { withNamespaces } from 'react-i18next'; -import NavigationTabs from "../../components/NavigationTabs"; -import ModeSelector from "./ModeSelector"; -import CurrencyInputPanel from "../../components/CurrencyInputPanel"; -import { selectors, addPendingTx } from '../../ducks/web3connect'; -import ContextualInfo from "../../components/ContextualInfo"; -import OversizedPanel from "../../components/OversizedPanel"; -import ArrowDownBlue from "../../assets/images/arrow-down-blue.svg"; -import ArrowDownGrey from "../../assets/images/arrow-down-grey.svg"; -import { getBlockDeadline } from '../../helpers/web3-utils'; -import { retry } from '../../helpers/promise-utils'; -import EXCHANGE_ABI from "../../abi/exchange"; -import ReactGA from "react-ga"; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { connect } from 'react-redux' +import { BigNumber as BN } from 'bignumber.js' +import { withNamespaces } from 'react-i18next' +import NavigationTabs from '../../components/NavigationTabs' +import ModeSelector from './ModeSelector' +import CurrencyInputPanel from '../../components/CurrencyInputPanel' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import ContextualInfo from '../../components/ContextualInfo' +import OversizedPanel from '../../components/OversizedPanel' +import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' +import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg' +import { getBlockDeadline } from '../../helpers/web3-utils' +import { retry } from '../../helpers/promise-utils' +import EXCHANGE_ABI from '../../abi/exchange' +import ReactGA from 'react-ga' class RemoveLiquidity extends Component { static propTypes = { @@ -23,220 +23,242 @@ class RemoveLiquidity extends Component { balances: PropTypes.object, web3: PropTypes.object, exchangeAddresses: PropTypes.shape({ - fromToken: PropTypes.object.isRequired, - }).isRequired, - }; + fromToken: PropTypes.object.isRequired + }).isRequired + } state = { tokenAddress: '', value: '', - totalSupply: BN(0), - }; + totalSupply: BN(0) + } reset() { this.setState({ - value: '', - }); + value: '' + }) } validate() { - const { tokenAddress, value } = this.state; - const { t, account, selectors, exchangeAddresses: { fromToken }, web3 } = this.props; - const exchangeAddress = fromToken[tokenAddress]; + const { tokenAddress, value } = this.state + const { + t, + account, + selectors, + exchangeAddresses: { fromToken }, + web3 + } = this.props + const exchangeAddress = fromToken[tokenAddress] if (!web3 || !exchangeAddress || !account || !value) { return { - isValid: false, - }; + isValid: false + } } - const { getBalance } = selectors(); + const { getBalance } = selectors() - const { value: liquidityBalance, decimals: liquidityDecimals } = getBalance(account, exchangeAddress); + const { value: liquidityBalance, decimals: liquidityDecimals } = getBalance(account, exchangeAddress) if (liquidityBalance.isLessThan(BN(value).multipliedBy(10 ** liquidityDecimals))) { - return { isValid: false, errorMessage: t("insufficientBalance") }; + return { isValid: false, errorMessage: t('insufficientBalance') } } return { - isValid: true, - }; + isValid: true + } } onTokenSelect = async tokenAddress => { - const { exchangeAddresses: { fromToken }, web3 } = this.props; - const exchangeAddress = fromToken[tokenAddress]; - this.setState({ tokenAddress }); + const { + exchangeAddresses: { fromToken }, + web3 + } = this.props + const exchangeAddress = fromToken[tokenAddress] + this.setState({ tokenAddress }) if (!web3 || !exchangeAddress) { - return; + return } - const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress); + const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress) - const totalSupply = await exchange.methods.totalSupply().call(); + const totalSupply = await exchange.methods.totalSupply().call() this.setState({ - totalSupply: BN(totalSupply), - }); - }; + totalSupply: BN(totalSupply) + }) + } onInputChange = value => { - this.setState({ value }); - }; + this.setState({ value }) + } onRemoveLiquidity = async () => { - const { tokenAddress, value: input, totalSupply } = this.state; + const { tokenAddress, value: input, totalSupply } = this.state const { exchangeAddresses: { fromToken }, web3, selectors, - account, - } = this.props; - const exchangeAddress = fromToken[tokenAddress]; - const { getBalance } = selectors(); + account + } = this.props + const exchangeAddress = fromToken[tokenAddress] + const { getBalance } = selectors() if (!web3 || !exchangeAddress) { - return; + return } - const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress); - const SLIPPAGE = .02; - const { decimals } = getBalance(account, exchangeAddress); - const { value: ethReserve } = getBalance(exchangeAddress); - const { value: tokenReserve } = getBalance(exchangeAddress, tokenAddress); - const amount = BN(input).multipliedBy(10 ** decimals); - - const ownership = amount.dividedBy(totalSupply); - const ethWithdrawn = ethReserve.multipliedBy(ownership); - const tokenWithdrawn = tokenReserve.multipliedBy(ownership); - let deadline; + const exchange = new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress) + const SLIPPAGE = 0.02 + const { decimals } = getBalance(account, exchangeAddress) + const { value: ethReserve } = getBalance(exchangeAddress) + const { value: tokenReserve } = getBalance(exchangeAddress, tokenAddress) + const amount = BN(input).multipliedBy(10 ** decimals) + + const ownership = amount.dividedBy(totalSupply) + const ethWithdrawn = ethReserve.multipliedBy(ownership) + const tokenWithdrawn = tokenReserve.multipliedBy(ownership) + let deadline try { - deadline = await retry(() => getBlockDeadline(web3, 600)); - } catch(e) { + deadline = await retry(() => getBlockDeadline(web3, 600)) + } catch (e) { // TODO: Handle error. - return; + return } - exchange.methods.removeLiquidity( - amount.toFixed(0), - ethWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0), - tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0), - deadline, - ).send({ from: account }, (err, data) => { - if (data) { - this.reset(); - - this.props.addPendingTx(data); - ReactGA.event({ - category: 'Pool', - action: 'RemoveLiquidity', - }); - } - }); - }; + exchange.methods + .removeLiquidity( + amount.toFixed(0), + ethWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0), + tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(0), + deadline + ) + .send({ from: account }, (err, data) => { + if (data) { + this.reset() + + this.props.addPendingTx(data) + ReactGA.event({ + category: 'Pool', + action: 'RemoveLiquidity' + }) + } + }) + } getBalance = () => { const { exchangeAddresses: { fromToken }, account, web3, - selectors, - } = this.props; + selectors + } = this.props - const { tokenAddress } = this.state; + const { tokenAddress } = this.state if (!web3) { - return ''; + return '' } - const exchangeAddress = fromToken[tokenAddress]; + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress) { - return ''; + return '' } - const { value, decimals } = selectors().getBalance(account, exchangeAddress); + const { value, decimals } = selectors().getBalance(account, exchangeAddress) if (!decimals) { - return ''; + return '' } - return `Balance: ${value.dividedBy(10 ** decimals).toFixed(7)}`; - }; + return `Balance: ${value.dividedBy(10 ** decimals).toFixed(7)}` + } renderSummary(errorMessage) { - const { t, selectors, exchangeAddresses: { fromToken } } = this.props; const { - value: input, - tokenAddress, - } = this.state; - const inputIsZero = BN(input).isZero(); - let contextualInfo = ''; - let isError = false; + t, + selectors, + exchangeAddresses: { fromToken } + } = this.props + const { value: input, tokenAddress } = this.state + const inputIsZero = BN(input).isZero() + let contextualInfo = '' + let isError = false if (errorMessage) { - contextualInfo = errorMessage; - isError = true; + contextualInfo = errorMessage + isError = true } else if (!tokenAddress) { - contextualInfo = t("selectTokenCont"); + contextualInfo = t('selectTokenCont') } else if (inputIsZero) { - contextualInfo = t("noZero"); + contextualInfo = t('noZero') } else if (!input) { - const { label } = selectors().getTokenBalance(tokenAddress, fromToken[tokenAddress]); - contextualInfo = t("enterLabelCont", { label }); + const { label } = selectors().getTokenBalance(tokenAddress, fromToken[tokenAddress]) + contextualInfo = t('enterLabelCont', { label }) } return ( - ); + ) } renderTransactionDetails = () => { - const { tokenAddress, value: input, totalSupply } = this.state; + const { tokenAddress, value: input, totalSupply } = this.state const { t, exchangeAddresses: { fromToken }, selectors, - account, - } = this.props; - const exchangeAddress = fromToken[tokenAddress]; - const { getBalance } = selectors(); + account + } = this.props + const exchangeAddress = fromToken[tokenAddress] + const { getBalance } = selectors() if (!exchangeAddress) { - return null; + return null } ReactGA.event({ category: 'TransactionDetail', - action: 'Open', - }); + action: 'Open' + }) - const SLIPPAGE = 0.025; - const { decimals } = getBalance(account, exchangeAddress); - const { value: ethReserve } = getBalance(exchangeAddress); - const { value: tokenReserve, label } = getBalance(exchangeAddress, tokenAddress); + const SLIPPAGE = 0.025 + const { decimals } = getBalance(account, exchangeAddress) + const { value: ethReserve } = getBalance(exchangeAddress) + const { value: tokenReserve, label } = getBalance(exchangeAddress, tokenAddress) - const ethPer = ethReserve.dividedBy(totalSupply); - const tokenPer = tokenReserve.dividedBy(totalSupply); + const ethPer = ethReserve.dividedBy(totalSupply) + const tokenPer = tokenReserve.dividedBy(totalSupply) - const ethWithdrawn = ethPer.multipliedBy(input); + const ethWithdrawn = ethPer.multipliedBy(input) - const tokenWithdrawn = tokenPer.multipliedBy(input); - const minTokenWithdrawn = tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(7); - const maxTokenWithdrawn = tokenWithdrawn.multipliedBy(1 + SLIPPAGE).toFixed(7); + const tokenWithdrawn = tokenPer.multipliedBy(input) + const minTokenWithdrawn = tokenWithdrawn.multipliedBy(1 - SLIPPAGE).toFixed(7) + const maxTokenWithdrawn = tokenWithdrawn.multipliedBy(1 + SLIPPAGE).toFixed(7) - const adjTotalSupply = totalSupply.dividedBy(10 ** decimals).minus(input); + const adjTotalSupply = totalSupply.dividedBy(10 ** decimals).minus(input) return (
-
{t("youAreRemoving")} {b(`${+BN(ethWithdrawn).toFixed(7)} ETH`)} {t("and")} {b(`${+minTokenWithdrawn} - ${+maxTokenWithdrawn} ${label}`)} {t("outPool")}
-
{t("youWillRemove")} {b(+input)} {t("liquidityTokens")}
-
{t("totalSupplyIs")} {b(+adjTotalSupply.toFixed(7))}
-
{t("tokenWorth")} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t("and")} {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}
+
+ {t('youAreRemoving')} {b(`${+BN(ethWithdrawn).toFixed(7)} ETH`)} {t('and')}{' '} + {b(`${+minTokenWithdrawn} - ${+maxTokenWithdrawn} ${label}`)} {t('outPool')} +
+
+ {t('youWillRemove')} {b(+input)} {t('liquidityTokens')} +
+
+ {t('totalSupplyIs')} {b(+adjTotalSupply.toFixed(7))} +
+
+ {t('tokenWorth')} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t('and')}{' '} + {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label} +
- ); + ) } renderOutput() { @@ -245,80 +267,77 @@ class RemoveLiquidity extends Component { exchangeAddresses: { fromToken }, account, web3, - selectors, - } = this.props; - const { getBalance } = selectors(); + selectors + } = this.props + const { getBalance } = selectors() - const { tokenAddress, totalSupply, value: input } = this.state; + const { tokenAddress, totalSupply, value: input } = this.state const blank = [ ( -
- )} + title={t('output')} + description={`(${t('estimated')})`} + renderInput={() =>
} disableTokenSelect disableUnlock />,
- {t("exchangeRate")} + {t('exchangeRate')} -
- {t("currentPoolSize")} + {t('currentPoolSize')} -
- {t("yourPoolShare")} + {t('yourPoolShare')} -
- ]; + ] - const exchangeAddress = fromToken[tokenAddress]; + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress || !web3) { - return blank; + return blank } - const { value: liquidityBalance } = getBalance(account, exchangeAddress); - const { value: ethReserve } = getBalance(exchangeAddress); - const { value: tokenReserve, decimals: tokenDecimals, label } = getBalance(exchangeAddress, tokenAddress); + const { value: liquidityBalance } = getBalance(account, exchangeAddress) + const { value: ethReserve } = getBalance(exchangeAddress) + const { value: tokenReserve, decimals: tokenDecimals, label } = getBalance(exchangeAddress, tokenAddress) if (!tokenDecimals) { - return blank; + return blank } - const ownership = liquidityBalance.dividedBy(totalSupply); - const ethPer = ethReserve.dividedBy(totalSupply); - const tokenPer = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).dividedBy(totalSupply); - const exchangeRate = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).div(ethReserve); + const ownership = liquidityBalance.dividedBy(totalSupply) + const ethPer = ethReserve.dividedBy(totalSupply) + const tokenPer = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).dividedBy(totalSupply) + const exchangeRate = tokenReserve.multipliedBy(10 ** (18 - tokenDecimals)).div(ethReserve) - const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18); - const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** tokenDecimals); + const ownedEth = ethPer.multipliedBy(liquidityBalance).dividedBy(10 ** 18) + const ownedToken = tokenPer.multipliedBy(liquidityBalance).dividedBy(10 ** tokenDecimals) return [ input - ? ( + renderInput={() => + input ? (
-
- {`${ethPer.multipliedBy(input).toFixed(3)} ETH`} -
+
{`${ethPer.multipliedBy(input).toFixed(3)} ETH`}
+
{`${tokenPer.multipliedBy(input).toFixed(3)} ${label}`}
+ ) : ( +
) - :
} disableTokenSelect disableUnlock @@ -326,46 +345,46 @@ class RemoveLiquidity extends Component {
- {t("exchangeRate")} - - {`1 ETH = ${exchangeRate.toFixed(4)} ${label}`} - + {t('exchangeRate')} + {`1 ETH = ${exchangeRate.toFixed(4)} ${label}`}
- {t("currentPoolSize")} - {`${ethReserve.dividedBy(10 ** 18).toFixed(2)} ETH + ${tokenReserve.dividedBy(10 ** tokenDecimals).toFixed(2)} ${label}`} + {t('currentPoolSize')} + {`${ethReserve.dividedBy(10 ** 18).toFixed(2)} ETH + ${tokenReserve + .dividedBy(10 ** tokenDecimals) + .toFixed(2)} ${label}`}
- {t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%) + {t('yourPoolShare')} ({ownership.multipliedBy(100).toFixed(2)}%) {`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}
- ]; + ] } render() { - const { t, isConnected } = this.props; - const { tokenAddress, value } = this.state; - const { isValid, errorMessage } = this.validate(); + const { t, isConnected } = this.props + const { tokenAddress, value } = this.state + const { isValid, errorMessage } = this.validate() return [
- +
- arrow + arrow
- { this.renderOutput() } - { this.renderSummary(errorMessage) } + {this.renderOutput()} + {this.renderSummary(errorMessage)}
- ]; + ] } } export default connect( state => ({ - isConnected: Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), + isConnected: + Boolean(state.web3connect.account) && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1), web3: state.web3connect.web3, balances: state.web3connect.balances, account: state.web3connect.account, - exchangeAddresses: state.addresses.exchangeAddresses, + exchangeAddresses: state.addresses.exchangeAddresses }), dispatch => ({ selectors: () => dispatch(selectors()), - addPendingTx: id => dispatch(addPendingTx(id)), + addPendingTx: id => dispatch(addPendingTx(id)) }) -)(withNamespaces()(RemoveLiquidity)); +)(withNamespaces()(RemoveLiquidity)) function b(text) { return {text} diff --git a/src/pages/Pool/index.js b/src/pages/Pool/index.js index 21bf9253682..d8288011a2b 100644 --- a/src/pages/Pool/index.js +++ b/src/pages/Pool/index.js @@ -1,17 +1,16 @@ -import React, { Component } from 'react'; -import Header from '../../components/Header'; -import AddLiquidity from './AddLiquidity'; -import CreateExchange from './CreateExchange'; -import RemoveLiquidity from './RemoveLiquidity'; -import { Switch, Route } from 'react-router-dom'; -import "./pool.scss"; -import MediaQuery from "react-responsive"; -import ReactGA from "react-ga"; - +import React, { Component } from 'react' +import Header from '../../components/Header' +import AddLiquidity from './AddLiquidity' +import CreateExchange from './CreateExchange' +import RemoveLiquidity from './RemoveLiquidity' +import { Switch, Route } from 'react-router-dom' +import './pool.scss' +import MediaQuery from 'react-responsive' +import ReactGA from 'react-ga' class Pool extends Component { componentWillMount() { - ReactGA.pageview(window.location.pathname + window.location.search); + ReactGA.pageview(window.location.pathname + window.location.search) } render() { return ( @@ -25,8 +24,8 @@ class Pool extends Component {
- ); + ) } } -export default Pool; +export default Pool diff --git a/src/pages/Pool/pool.scss b/src/pages/Pool/pool.scss index 8ff2647b818..9bae69dc745 100644 --- a/src/pages/Pool/pool.scss +++ b/src/pages/Pool/pool.scss @@ -1,4 +1,4 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .pool { @extend %col-nowrap; @@ -8,16 +8,16 @@ &__liquidity-container { @extend %row-nowrap; align-items: center; - font-size: .75rem; - padding: .625rem 1rem; - font-size: .75rem; + font-size: 0.75rem; + padding: 0.625rem 1rem; + font-size: 0.75rem; color: $royal-blue; font-weight: 500; cursor: pointer; img { - height: .75rem; - width: .75rem; + height: 0.75rem; + width: 0.75rem; } } @@ -38,8 +38,8 @@ @extend %row-nowrap; align-items: center; color: $dove-gray; - font-size: .75rem; - padding: .25rem 1rem 0; + font-size: 0.75rem; + padding: 0.25rem 1rem 0; } &__exchange-rate { @@ -64,25 +64,25 @@ &__new-exchange-warning { padding: 1rem; margin-bottom: 2rem; - border: 1px solid rgba($pizazz-orange, .4); - background-color: rgba($pizazz-orange, .1); + border: 1px solid rgba($pizazz-orange, 0.4); + background-color: rgba($pizazz-orange, 0.1); border-radius: 1rem; } &__new-exchange-warning-text { - font-size: .75rem; + font-size: 0.75rem; line-height: 1rem; text-align: center; &:first-child { - padding-bottom: .3rem; + padding-bottom: 0.3rem; font-weight: 500; } } &__summary-item { &:not(:last-child) { - margin-bottom: .5rem; + margin-bottom: 0.5rem; } } } @@ -97,9 +97,9 @@ border-top-left-radius: 1rem; border-top-right-radius: 1rem; transition: 250ms ease-in-out; - padding: 1rem 0 .5rem; + padding: 1rem 0 0.5rem; - @media only screen and (min-width : 768px) { + @media only screen and (min-width: 768px) { max-width: 560px; position: absolute; margin-left: auto; @@ -149,9 +149,9 @@ } &__summary-text { - font-size: .75rem; + font-size: 0.75rem; } - + &--error { color: $salmon-red; } @@ -166,7 +166,7 @@ &__output-text { font-size: 1.25rem; line-height: 1.5rem; - padding: 1rem .75rem; + padding: 1rem 0.75rem; } &__output-plus { @@ -174,4 +174,4 @@ line-height: 1.5rem; padding: 1rem 0; } -} \ No newline at end of file +} diff --git a/src/pages/Send/index.js b/src/pages/Send/index.js index d96a925b80d..e1de7995a03 100644 --- a/src/pages/Send/index.js +++ b/src/pages/Send/index.js @@ -1,36 +1,36 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import {BigNumber as BN} from "bignumber.js"; -import { withNamespaces } from 'react-i18next'; -import { selectors, addPendingTx } from '../../ducks/web3connect'; -import Header from '../../components/Header'; -import NavigationTabs from '../../components/NavigationTabs'; -import AddressInputPanel from '../../components/AddressInputPanel'; -import CurrencyInputPanel from '../../components/CurrencyInputPanel'; -import ContextualInfo from '../../components/ContextualInfo'; -import OversizedPanel from '../../components/OversizedPanel'; -import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg'; -import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'; -import { getBlockDeadline } from '../../helpers/web3-utils'; -import { retry } from '../../helpers/promise-utils'; -import EXCHANGE_ABI from '../../abi/exchange'; - -import "./send.scss"; -import MediaQuery from "react-responsive"; -import ReactGA from "react-ga"; - -const INPUT = 0; -const OUTPUT = 1; +import React, { Component } from 'react' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { BigNumber as BN } from 'bignumber.js' +import { withNamespaces } from 'react-i18next' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import Header from '../../components/Header' +import NavigationTabs from '../../components/NavigationTabs' +import AddressInputPanel from '../../components/AddressInputPanel' +import CurrencyInputPanel from '../../components/CurrencyInputPanel' +import ContextualInfo from '../../components/ContextualInfo' +import OversizedPanel from '../../components/OversizedPanel' +import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' +import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg' +import { getBlockDeadline } from '../../helpers/web3-utils' +import { retry } from '../../helpers/promise-utils' +import EXCHANGE_ABI from '../../abi/exchange' + +import './send.scss' +import MediaQuery from 'react-responsive' +import ReactGA from 'react-ga' + +const INPUT = 0 +const OUTPUT = 1 class Send extends Component { static propTypes = { account: PropTypes.string, isConnected: PropTypes.bool.isRequired, selectors: PropTypes.func.isRequired, - web3: PropTypes.object.isRequired, - }; + web3: PropTypes.object.isRequired + } state = { inputValue: '', @@ -39,15 +39,15 @@ class Send extends Component { outputCurrency: '', inputAmountB: '', lastEditedField: '', - recipient: '', - }; + recipient: '' + } componentWillMount() { - ReactGA.pageview(window.location.pathname + window.location.search); + ReactGA.pageview(window.location.pathname + window.location.search) } shouldComponentUpdate(nextProps, nextState) { - return true; + return true } reset() { @@ -56,116 +56,125 @@ class Send extends Component { outputValue: '', inputAmountB: '', lastEditedField: '', - recipient: '', - }); + recipient: '' + }) } componentWillReceiveProps() { - this.recalcForm(); + this.recalcForm() } validate() { - const { selectors, account, web3 } = this.props; - const { - inputValue, outputValue, - inputCurrency, outputCurrency, - recipient, - } = this.state; - - let inputError = ''; - let outputError = ''; - let isValid = true; - const validRecipientAddress = web3 && web3.utils.isAddress(recipient); - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); - - if (!inputValue || inputIsZero || !outputValue || outputIsZero || !inputCurrency || !outputCurrency || !recipient || this.isUnapproved() || !validRecipientAddress) { - isValid = false; + const { selectors, account, web3 } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency, recipient } = this.state + + let inputError = '' + let outputError = '' + let isValid = true + const validRecipientAddress = web3 && web3.utils.isAddress(recipient) + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() + + if ( + !inputValue || + inputIsZero || + !outputValue || + outputIsZero || + !inputCurrency || + !outputCurrency || + !recipient || + this.isUnapproved() || + !validRecipientAddress + ) { + isValid = false } - const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); + const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) if (inputBalance.isLessThan(BN(inputValue * 10 ** inputDecimals))) { - inputError = this.props.t("insufficientBalance"); + inputError = this.props.t('insufficientBalance') } if (inputValue === 'N/A') { - inputError = this.props.t("inputNotValid"); + inputError = this.props.t('inputNotValid') } return { inputError, outputError, - isValid: isValid && !inputError && !outputError, - }; + isValid: isValid && !inputError && !outputError + } } flipInputOutput = () => { - const { state } = this; - this.setState({ - inputValue: state.outputValue, - outputValue: state.inputValue, - inputCurrency: state.outputCurrency, - outputCurrency: state.inputCurrency, - lastEditedField: state.lastEditedField === INPUT ? OUTPUT : INPUT - }, () => this.recalcForm()); + const { state } = this + this.setState( + { + inputValue: state.outputValue, + outputValue: state.inputValue, + inputCurrency: state.outputCurrency, + outputCurrency: state.inputCurrency, + lastEditedField: state.lastEditedField === INPUT ? OUTPUT : INPUT + }, + () => this.recalcForm() + ) } isUnapproved() { - const { account, exchangeAddresses, selectors } = this.props; - const { inputCurrency, inputValue } = this.state; + const { account, exchangeAddresses, selectors } = this.props + const { inputCurrency, inputValue } = this.state if (!inputCurrency || inputCurrency === 'ETH') { - return false; + return false } const { value: allowance, label, decimals } = selectors().getApprovals( inputCurrency, account, exchangeAddresses.fromToken[inputCurrency] - ); + ) if (label && allowance.isLessThan(BN(inputValue * 10 ** decimals || 0))) { - return true; + return true } - return false; + return false } recalcForm() { - const { inputCurrency, outputCurrency, lastEditedField } = this.state; + const { inputCurrency, outputCurrency, lastEditedField } = this.state if (!inputCurrency || !outputCurrency) { - return; + return } - const editedValue = lastEditedField === INPUT ? this.state.inputValue : this.state.outputValue; + const editedValue = lastEditedField === INPUT ? this.state.inputValue : this.state.outputValue if (BN(editedValue).isZero()) { - return; + return } if (inputCurrency === outputCurrency) { this.setState({ inputValue: '', - outputValue: '', - }); - return; + outputValue: '' + }) + return } if (inputCurrency !== 'ETH' && outputCurrency !== 'ETH') { - this.recalcTokenTokenForm(); - return; + this.recalcTokenTokenForm() + return } - this.recalcEthTokenForm(); + this.recalcEthTokenForm() } recalcTokenTokenForm = () => { const { exchangeAddresses: { fromToken }, - selectors, - } = this.props; + selectors + } = this.props const { inputValue: oldInputValue, @@ -174,109 +183,109 @@ class Send extends Component { outputCurrency, lastEditedField, exchangeRate: oldExchangeRate, - inputAmountB: oldInputAmountB, - } = this.state; + inputAmountB: oldInputAmountB + } = this.state - const exchangeAddressA = fromToken[inputCurrency]; - const exchangeAddressB = fromToken[outputCurrency]; + const exchangeAddressA = fromToken[inputCurrency] + const exchangeAddressB = fromToken[outputCurrency] - const { value: inputReserveA, decimals: inputDecimalsA } = selectors().getBalance(exchangeAddressA, inputCurrency); - const { value: outputReserveA }= selectors().getBalance(exchangeAddressA, 'ETH'); - const { value: inputReserveB } = selectors().getBalance(exchangeAddressB, 'ETH'); - const { value: outputReserveB, decimals: outputDecimalsB }= selectors().getBalance(exchangeAddressB, outputCurrency); + const { value: inputReserveA, decimals: inputDecimalsA } = selectors().getBalance(exchangeAddressA, inputCurrency) + const { value: outputReserveA } = selectors().getBalance(exchangeAddressA, 'ETH') + const { value: inputReserveB } = selectors().getBalance(exchangeAddressB, 'ETH') + const { value: outputReserveB, decimals: outputDecimalsB } = selectors().getBalance( + exchangeAddressB, + outputCurrency + ) if (lastEditedField === INPUT) { if (!oldInputValue) { return this.setState({ outputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const inputAmountA = BN(oldInputValue).multipliedBy(10 ** inputDecimalsA); + const inputAmountA = BN(oldInputValue).multipliedBy(10 ** inputDecimalsA) const outputAmountA = calculateEtherTokenOutput({ inputAmount: inputAmountA, inputReserve: inputReserveA, - outputReserve: outputReserveA, - }); + outputReserve: outputReserveA + }) // Redundant Variable for readability of the formala // OutputAmount from the first send becomes InputAmount of the second send - const inputAmountB = outputAmountA; + const inputAmountB = outputAmountA const outputAmountB = calculateEtherTokenOutput({ inputAmount: inputAmountB, inputReserve: inputReserveB, - outputReserve: outputReserveB, - }); + outputReserve: outputReserveB + }) - const outputValue = outputAmountB.dividedBy(BN(10 ** outputDecimalsB)).toFixed(7); - const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)); + const outputValue = outputAmountB.dividedBy(BN(10 ** outputDecimalsB)).toFixed(7) + const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (outputValue !== oldOutputValue) { - appendState.outputValue = outputValue; + appendState.outputValue = outputValue } - this.setState(appendState); + this.setState(appendState) } if (lastEditedField === OUTPUT) { if (!oldOutputValue) { return this.setState({ inputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const outputAmountB = BN(oldOutputValue).multipliedBy(10 ** outputDecimalsB); + const outputAmountB = BN(oldOutputValue).multipliedBy(10 ** outputDecimalsB) const inputAmountB = calculateEtherTokenInput({ outputAmount: outputAmountB, inputReserve: inputReserveB, - outputReserve: outputReserveB, - }); + outputReserve: outputReserveB + }) // Redundant Variable for readability of the formala // InputAmount from the first send becomes OutputAmount of the second send - const outputAmountA = inputAmountB; + const outputAmountA = inputAmountB const inputAmountA = calculateEtherTokenInput({ outputAmount: outputAmountA, inputReserve: inputReserveA, - outputReserve: outputReserveA, - }); + outputReserve: outputReserveA + }) - const inputValue = inputAmountA.isNegative() - ? 'N/A' - : inputAmountA.dividedBy(BN(10 ** inputDecimalsA)).toFixed(7); - const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)); + const inputValue = inputAmountA.isNegative() ? 'N/A' : inputAmountA.dividedBy(BN(10 ** inputDecimalsA)).toFixed(7) + const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (inputValue !== oldInputValue) { - appendState.inputValue = inputValue; + appendState.inputValue = inputValue } if (!inputAmountB.isEqualTo(BN(oldInputAmountB))) { - appendState.inputAmountB = inputAmountB; + appendState.inputAmountB = inputAmountB } - this.setState(appendState); + this.setState(appendState) } - - }; + } recalcEthTokenForm = () => { const { exchangeAddresses: { fromToken }, - selectors, - } = this.props; + selectors + } = this.props const { inputValue: oldInputValue, @@ -284,83 +293,87 @@ class Send extends Component { inputCurrency, outputCurrency, lastEditedField, - exchangeRate: oldExchangeRate, - } = this.state; + exchangeRate: oldExchangeRate + } = this.state - const tokenAddress = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]; - const exchangeAddress = fromToken[tokenAddress]; + const tokenAddress = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0] + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress) { - return; + return } - const { value: inputReserve, decimals: inputDecimals } = selectors().getBalance(exchangeAddress, inputCurrency); - const { value: outputReserve, decimals: outputDecimals }= selectors().getBalance(exchangeAddress, outputCurrency); + const { value: inputReserve, decimals: inputDecimals } = selectors().getBalance(exchangeAddress, inputCurrency) + const { value: outputReserve, decimals: outputDecimals } = selectors().getBalance(exchangeAddress, outputCurrency) if (lastEditedField === INPUT) { if (!oldInputValue) { return this.setState({ outputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const inputAmount = BN(oldInputValue).multipliedBy(10 ** inputDecimals); - const outputAmount = calculateEtherTokenOutput({ inputAmount, inputReserve, outputReserve }); - const outputValue = outputAmount.dividedBy(BN(10 ** outputDecimals)).toFixed(7); - const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)); + const inputAmount = BN(oldInputValue).multipliedBy(10 ** inputDecimals) + const outputAmount = calculateEtherTokenOutput({ inputAmount, inputReserve, outputReserve }) + const outputValue = outputAmount.dividedBy(BN(10 ** outputDecimals)).toFixed(7) + const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (outputValue !== oldOutputValue) { - appendState.outputValue = outputValue; + appendState.outputValue = outputValue } - this.setState(appendState); + this.setState(appendState) } else if (lastEditedField === OUTPUT) { if (!oldOutputValue) { return this.setState({ inputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const outputAmount = BN(oldOutputValue).multipliedBy(10 ** outputDecimals); - const inputAmount = calculateEtherTokenInput({ outputAmount, inputReserve, outputReserve }); - const inputValue = inputAmount.isNegative() - ? 'N/A' - : inputAmount.dividedBy(BN(10 ** inputDecimals)).toFixed(7); - const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)); + const outputAmount = BN(oldOutputValue).multipliedBy(10 ** outputDecimals) + const inputAmount = calculateEtherTokenInput({ outputAmount, inputReserve, outputReserve }) + const inputValue = inputAmount.isNegative() ? 'N/A' : inputAmount.dividedBy(BN(10 ** inputDecimals)).toFixed(7) + const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (inputValue !== oldInputValue) { - appendState.inputValue = inputValue; + appendState.inputValue = inputValue } - this.setState(appendState); + this.setState(appendState) } - }; + } updateInput = amount => { - this.setState({ - inputValue: amount, - lastEditedField: INPUT, - }, this.recalcForm); - }; + this.setState( + { + inputValue: amount, + lastEditedField: INPUT + }, + this.recalcForm + ) + } updateOutput = amount => { - this.setState({ - outputValue: amount, - lastEditedField: OUTPUT, - }, this.recalcForm); - }; + this.setState( + { + outputValue: amount, + lastEditedField: OUTPUT + }, + this.recalcForm + ) + } onSend = async () => { const { @@ -368,8 +381,8 @@ class Send extends Component { account, web3, selectors, - addPendingTx, - } = this.props; + addPendingTx + } = this.props const { inputValue, outputValue, @@ -377,83 +390,98 @@ class Send extends Component { outputCurrency, inputAmountB, lastEditedField, - recipient, - } = this.state; - const ALLOWED_SLIPPAGE = 0.025; - const TOKEN_ALLOWED_SLIPPAGE = 0.04; - - const type = getSendType(inputCurrency, outputCurrency); - const { decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); - const { decimals: outputDecimals } = selectors().getBalance(account, outputCurrency); - let deadline; + recipient + } = this.state + const ALLOWED_SLIPPAGE = 0.025 + const TOKEN_ALLOWED_SLIPPAGE = 0.04 + + const type = getSendType(inputCurrency, outputCurrency) + const { decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) + const { decimals: outputDecimals } = selectors().getBalance(account, outputCurrency) + let deadline try { - deadline = await retry(() => getBlockDeadline(web3, 600)); - } catch(e) { + deadline = await retry(() => getBlockDeadline(web3, 600)) + } catch (e) { // TODO: Handle error. - return; + return } if (lastEditedField === INPUT) { ReactGA.event({ category: type, - action: 'TransferInput', - }); + action: 'TransferInput' + }) // send input - switch(type) { + switch (type) { case 'ETH_TO_TOKEN': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]).methods .ethToTokenTransferInput( - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(0), deadline, - recipient, + recipient ) - .send({ - from: account, - value: BN(inputValue).multipliedBy(10 ** 18).toFixed(0), - }, (err, data) => { - if (!err) { - addPendingTx(data); - this.reset(); + .send( + { + from: account, + value: BN(inputValue) + .multipliedBy(10 ** 18) + .toFixed(0) + }, + (err, data) => { + if (!err) { + addPendingTx(data) + this.reset() + } } - }); - break; + ) + break case 'TOKEN_TO_ETH': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToEthTransferInput( - BN(inputValue).multipliedBy(10 ** inputDecimals).toFixed(0), - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(0), deadline, - recipient, + recipient ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break case 'TOKEN_TO_TOKEN': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToTokenTransferInput( - BN(inputValue).multipliedBy(10 ** inputDecimals).toFixed(0), - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE).toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE) + .toFixed(0), '1', deadline, recipient, - outputCurrency, + outputCurrency ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break default: - break; + break } } @@ -461,261 +489,266 @@ class Send extends Component { // send output ReactGA.event({ category: type, - action: 'TransferOutput', - }); + action: 'TransferOutput' + }) switch (type) { case 'ETH_TO_TOKEN': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]).methods .ethToTokenTransferOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), deadline, - recipient, + recipient ) - .send({ - from: account, - value: BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(0), - }, (err, data) => { - if (!err) { - addPendingTx(data); - this.reset(); + .send( + { + from: account, + value: BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(0) + }, + (err, data) => { + if (!err) { + addPendingTx(data) + this.reset() + } } - }); - break; + ) + break case 'TOKEN_TO_ETH': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToEthTransferOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), - BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(0), deadline, - recipient, + recipient ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break case 'TOKEN_TO_TOKEN': if (!inputAmountB) { - return; + return } - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToTokenTransferOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), - BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE).toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE) + .toFixed(0), inputAmountB.multipliedBy(1.2).toFixed(0), deadline, recipient, - outputCurrency, + outputCurrency ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break default: - break; + break } } - }; + } renderSummary(inputError, outputError) { - const { - inputValue, - inputCurrency, - outputValue, - outputCurrency, - recipient, - } = this.state; - const { t, web3 } = this.props; + const { inputValue, inputCurrency, outputValue, outputCurrency, recipient } = this.state + const { t, web3 } = this.props - const { selectors, account } = this.props; - const { label: inputLabel } = selectors().getBalance(account, inputCurrency); - const { label: outputLabel } = selectors().getBalance(account, outputCurrency); - const validRecipientAddress = web3 && web3.utils.isAddress(recipient); - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); + const { selectors, account } = this.props + const { label: inputLabel } = selectors().getBalance(account, inputCurrency) + const { label: outputLabel } = selectors().getBalance(account, outputCurrency) + const validRecipientAddress = web3 && web3.utils.isAddress(recipient) + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() - let contextualInfo = ''; - let isError = false; + let contextualInfo = '' + let isError = false if (inputError || outputError) { - contextualInfo = inputError || outputError; - isError = true; + contextualInfo = inputError || outputError + isError = true } else if (!inputCurrency || !outputCurrency) { - contextualInfo = t("selectTokenCont"); + contextualInfo = t('selectTokenCont') } else if (inputCurrency === outputCurrency) { - contextualInfo = t("differentToken"); + contextualInfo = t('differentToken') } else if (!inputValue || !outputValue) { - const missingCurrencyValue = !inputValue ? inputLabel : outputLabel; - contextualInfo = t("enterValueCont", {missingCurrencyValue}); + const missingCurrencyValue = !inputValue ? inputLabel : outputLabel + contextualInfo = t('enterValueCont', { missingCurrencyValue }) } else if (inputIsZero || outputIsZero) { - contextualInfo = t("noLiquidity"); + contextualInfo = t('noLiquidity') } else if (this.isUnapproved()) { - contextualInfo = t("unlockTokenCont"); + contextualInfo = t('unlockTokenCont') } else if (!recipient) { - contextualInfo = t("noRecipient"); + contextualInfo = t('noRecipient') } else if (!validRecipientAddress) { - contextualInfo = t("invalidRecipient"); + contextualInfo = t('invalidRecipient') } return ( - ); + ) } renderTransactionDetails = () => { - const { - inputValue, - inputCurrency, - outputValue, - outputCurrency, - recipient, - lastEditedField, - } = this.state; - const { t, selectors, account } = this.props; + const { inputValue, inputCurrency, outputValue, outputCurrency, recipient, lastEditedField } = this.state + const { t, selectors, account } = this.props ReactGA.event({ category: 'TransactionDetail', - action: 'Open', - }); + action: 'Open' + }) - const ALLOWED_SLIPPAGE = 0.025; - const TOKEN_ALLOWED_SLIPPAGE = 0.04; + const ALLOWED_SLIPPAGE = 0.025 + const TOKEN_ALLOWED_SLIPPAGE = 0.04 - const type = getSendType(inputCurrency, outputCurrency); - const { label: inputLabel } = selectors().getBalance(account, inputCurrency); - const { label: outputLabel } = selectors().getBalance(account, outputCurrency); + const type = getSendType(inputCurrency, outputCurrency) + const { label: inputLabel } = selectors().getBalance(account, inputCurrency) + const { label: outputLabel } = selectors().getBalance(account, outputCurrency) // const label = lastEditedField === INPUT ? outputLabel : inputLabel; - let minOutput; - let maxInput; + let minOutput + let maxInput if (lastEditedField === INPUT) { - switch(type) { + switch (type) { case 'ETH_TO_TOKEN': - minOutput = BN(outputValue).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(7); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_ETH': - minOutput = BN(outputValue).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(7); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_TOKEN': - minOutput = BN(outputValue).multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE).toFixed(7); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE) + .toFixed(7) + break default: - break; + break } } if (lastEditedField === OUTPUT) { switch (type) { case 'ETH_TO_TOKEN': - maxInput = BN(inputValue).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(7); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_ETH': - maxInput = BN(inputValue).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(7); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_TOKEN': - maxInput = BN(inputValue).multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE).toFixed(7); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE) + .toFixed(7) + break default: - break; + break } } - const recipientText = b(`${recipient.slice(0, 6)}...${recipient.slice(-4)}`); + const recipientText = b(`${recipient.slice(0, 6)}...${recipient.slice(-4)}`) if (lastEditedField === INPUT) { return (
- {t("youAreSending")} {b(`${+inputValue} ${inputLabel}`)}. + {t('youAreSending')} {b(`${+inputValue} ${inputLabel}`)}.
- {recipientText} {t("willReceive")} {b(`${+minOutput} ${outputLabel}`)} {t("orTransFail")} + {recipientText} {t('willReceive')} {b(`${+minOutput} ${outputLabel}`)} {t('orTransFail')}
- ); + ) } else { return (
- {t("youAreSending")} {b(`${+outputValue} ${outputLabel}`)} {t("to")} {recipientText}. + {t('youAreSending')} {b(`${+outputValue} ${outputLabel}`)} {t('to')} {recipientText}. {/*You are selling between {b(`${+inputValue} ${inputLabel}`)} to {b(`${+maxInput} ${inputLabel}`)}.*/}
{/*{b(`${recipient.slice(0, 6)}...${recipient.slice(-4)}`)} will receive {b(`${+outputValue} ${outputLabel}`)}.*/} - {t("itWillCost")} {b(`${+maxInput} ${inputLabel}`)} {t("orTransFail")} + {t('itWillCost')} {b(`${+maxInput} ${inputLabel}`)} {t('orTransFail')}
- ); + ) } } renderExchangeRate() { - const { t, account, selectors } = this.props; - const { exchangeRate, inputCurrency, outputCurrency } = this.state; - const { label: inputLabel } = selectors().getBalance(account, inputCurrency); - const { label: outputLabel } = selectors().getBalance(account, outputCurrency); + const { t, account, selectors } = this.props + const { exchangeRate, inputCurrency, outputCurrency } = this.state + const { label: inputLabel } = selectors().getBalance(account, inputCurrency) + const { label: outputLabel } = selectors().getBalance(account, outputCurrency) if (!exchangeRate || exchangeRate.isNaN() || !inputCurrency || !outputCurrency) { return (
- {t("exchangeRate")} + {t('exchangeRate')} -
- ); + ) } return (
- {t("exchangeRate")} - - {`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`} - + {t('exchangeRate')} + {`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`}
- ); + ) } renderBalance(currency, balance, decimals) { if (!currency || decimals === 0) { - return ''; + return '' } const balanceInput = balance.dividedBy(BN(10 ** decimals)).toFixed(4) - return this.props.t("balance", { balanceInput }) + return this.props.t('balance', { balanceInput }) } render() { - const { t, selectors, account } = this.props; - const { - lastEditedField, - inputCurrency, - outputCurrency, - inputValue, - outputValue, - recipient, - } = this.state; - const estimatedText = `(${t("estimated")})`; + const { t, selectors, account } = this.props + const { lastEditedField, inputCurrency, outputCurrency, inputValue, outputValue, recipient } = this.state + const estimatedText = `(${t('estimated')})` - const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); - const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency); - const { inputError, outputError, isValid } = this.validate(); + const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) + const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency) + const { inputError, outputError, isValid } = this.validate() return (
@@ -724,17 +757,17 @@ class Send extends Component {
this.setState({ inputCurrency }, this.recalcForm)} @@ -746,11 +779,16 @@ class Send extends Component { />
- arrow + arrow
this.setState({ outputCurrency }, this.recalcForm)} @@ -763,85 +801,93 @@ class Send extends Component { />
- arrow + arrow
this.setState({recipient: address})} + onChange={address => this.setState({ recipient: address })} /> - { this.renderExchangeRate() } - { this.renderSummary(inputError, outputError) } + {this.renderExchangeRate()} + {this.renderSummary(inputError, outputError)}
- ); + ) } } export default connect( state => ({ balances: state.web3connect.balances, - isConnected: !!state.web3connect.account && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), + isConnected: !!state.web3connect.account && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1), account: state.web3connect.account, web3: state.web3connect.web3, - exchangeAddresses: state.addresses.exchangeAddresses, + exchangeAddresses: state.addresses.exchangeAddresses }), dispatch => ({ selectors: () => dispatch(selectors()), - addPendingTx: id => dispatch(addPendingTx(id)), - }), -)(withNamespaces()(Send)); + addPendingTx: id => dispatch(addPendingTx(id)) + }) +)(withNamespaces()(Send)) -const b = text => {text}; +const b = text => {text} -function calculateEtherTokenOutput({ inputAmount: rawInput, inputReserve: rawReserveIn, outputReserve: rawReserveOut }) { - const inputAmount = BN(rawInput); - const inputReserve = BN(rawReserveIn); - const outputReserve = BN(rawReserveOut); +function calculateEtherTokenOutput({ + inputAmount: rawInput, + inputReserve: rawReserveIn, + outputReserve: rawReserveOut +}) { + const inputAmount = BN(rawInput) + const inputReserve = BN(rawReserveIn) + const outputReserve = BN(rawReserveOut) if (inputAmount.isLessThan(BN(10 ** 9))) { - console.warn(`inputAmount is only ${inputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`); + console.warn(`inputAmount is only ${inputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`) } - const numerator = inputAmount.multipliedBy(outputReserve).multipliedBy(997); - const denominator = inputReserve.multipliedBy(1000).plus(inputAmount.multipliedBy(997)); + const numerator = inputAmount.multipliedBy(outputReserve).multipliedBy(997) + const denominator = inputReserve.multipliedBy(1000).plus(inputAmount.multipliedBy(997)) - return numerator.dividedBy(denominator); + return numerator.dividedBy(denominator) } -function calculateEtherTokenInput({ outputAmount: rawOutput, inputReserve: rawReserveIn, outputReserve: rawReserveOut }) { - const outputAmount = BN(rawOutput); - const inputReserve = BN(rawReserveIn); - const outputReserve = BN(rawReserveOut); +function calculateEtherTokenInput({ + outputAmount: rawOutput, + inputReserve: rawReserveIn, + outputReserve: rawReserveOut +}) { + const outputAmount = BN(rawOutput) + const inputReserve = BN(rawReserveIn) + const outputReserve = BN(rawReserveOut) if (outputAmount.isLessThan(BN(10 ** 9))) { - console.warn(`inputAmount is only ${outputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`); + console.warn(`inputAmount is only ${outputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`) } - const numerator = outputAmount.multipliedBy(inputReserve).multipliedBy(1000); - const denominator = outputReserve.minus(outputAmount).multipliedBy(997); - return (numerator.dividedBy(denominator)).plus(1); + const numerator = outputAmount.multipliedBy(inputReserve).multipliedBy(1000) + const denominator = outputReserve.minus(outputAmount).multipliedBy(997) + return numerator.dividedBy(denominator).plus(1) } function getSendType(inputCurrency, outputCurrency) { if (!inputCurrency || !outputCurrency) { - return; + return } if (inputCurrency === outputCurrency) { - return; + return } if (inputCurrency !== 'ETH' && outputCurrency !== 'ETH') { @@ -849,12 +895,12 @@ function getSendType(inputCurrency, outputCurrency) { } if (inputCurrency === 'ETH') { - return 'ETH_TO_TOKEN'; + return 'ETH_TO_TOKEN' } if (outputCurrency === 'ETH') { - return 'TOKEN_TO_ETH'; + return 'TOKEN_TO_ETH' } - return; + return } diff --git a/src/pages/Send/send.scss b/src/pages/Send/send.scss index 2ee113a59a9..baae9babf95 100644 --- a/src/pages/Send/send.scss +++ b/src/pages/Send/send.scss @@ -1,4 +1,4 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .send { @extend %col-nowrap; diff --git a/src/pages/Swap/index.js b/src/pages/Swap/index.js index ff2998c93ca..290e6479cc3 100644 --- a/src/pages/Swap/index.js +++ b/src/pages/Swap/index.js @@ -1,27 +1,27 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import {BigNumber as BN} from "bignumber.js"; -import MediaQuery from 'react-responsive'; -import ReactGA from 'react-ga'; -import { withNamespaces } from 'react-i18next'; -import { selectors, addPendingTx } from '../../ducks/web3connect'; -import Header from '../../components/Header'; -import NavigationTabs from '../../components/NavigationTabs'; -import CurrencyInputPanel from '../../components/CurrencyInputPanel'; -import ContextualInfo from '../../components/ContextualInfo'; -import OversizedPanel from '../../components/OversizedPanel'; -import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg'; -import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'; -import { getBlockDeadline } from '../../helpers/web3-utils'; -import { retry } from '../../helpers/promise-utils'; -import EXCHANGE_ABI from '../../abi/exchange'; - -import "./swap.scss"; - -const INPUT = 0; -const OUTPUT = 1; +import React, { Component } from 'react' +import { connect } from 'react-redux' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { BigNumber as BN } from 'bignumber.js' +import MediaQuery from 'react-responsive' +import ReactGA from 'react-ga' +import { withNamespaces } from 'react-i18next' +import { selectors, addPendingTx } from '../../ducks/web3connect' +import Header from '../../components/Header' +import NavigationTabs from '../../components/NavigationTabs' +import CurrencyInputPanel from '../../components/CurrencyInputPanel' +import ContextualInfo from '../../components/ContextualInfo' +import OversizedPanel from '../../components/OversizedPanel' +import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' +import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg' +import { getBlockDeadline } from '../../helpers/web3-utils' +import { retry } from '../../helpers/promise-utils' +import EXCHANGE_ABI from '../../abi/exchange' + +import './swap.scss' + +const INPUT = 0 +const OUTPUT = 1 class Swap extends Component { static propTypes = { @@ -29,8 +29,8 @@ class Swap extends Component { isConnected: PropTypes.bool.isRequired, selectors: PropTypes.func.isRequired, addPendingTx: PropTypes.func.isRequired, - web3: PropTypes.object.isRequired, - }; + web3: PropTypes.object.isRequired + } state = { inputValue: '', @@ -38,15 +38,15 @@ class Swap extends Component { inputCurrency: 'ETH', outputCurrency: '', inputAmountB: '', - lastEditedField: '', - }; + lastEditedField: '' + } componentWillMount() { - ReactGA.pageview(window.location.pathname + window.location.search); + ReactGA.pageview(window.location.pathname + window.location.search) } shouldComponentUpdate(nextProps, nextState) { - return true; + return true } reset() { @@ -54,115 +54,123 @@ class Swap extends Component { inputValue: '', outputValue: '', inputAmountB: '', - lastEditedField: '', - }); + lastEditedField: '' + }) } componentWillReceiveProps() { - this.recalcForm(); + this.recalcForm() } validate() { - const { selectors, account } = this.props; - const { - inputValue, outputValue, - inputCurrency, outputCurrency, - } = this.state; - - let inputError = ''; - let outputError = ''; - let isValid = true; - let isUnapproved = this.isUnapproved(); - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); - - if (!inputValue || inputIsZero || !outputValue || outputIsZero || !inputCurrency || !outputCurrency || isUnapproved) { - isValid = false; + const { selectors, account } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency } = this.state + + let inputError = '' + let outputError = '' + let isValid = true + let isUnapproved = this.isUnapproved() + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() + + if ( + !inputValue || + inputIsZero || + !outputValue || + outputIsZero || + !inputCurrency || + !outputCurrency || + isUnapproved + ) { + isValid = false } - const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); + const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) if (inputBalance.isLessThan(BN(inputValue * 10 ** inputDecimals))) { - inputError = this.props.t("insufficientBalance"); + inputError = this.props.t('insufficientBalance') } if (inputValue === 'N/A') { - inputError = this.props.t("inputNotValid"); + inputError = this.props.t('inputNotValid') } return { inputError, outputError, - isValid: isValid && !inputError && !outputError, - }; + isValid: isValid && !inputError && !outputError + } } flipInputOutput = () => { - const { state } = this; - this.setState({ - inputValue: state.outputValue, - outputValue: state.inputValue, - inputCurrency: state.outputCurrency, - outputCurrency: state.inputCurrency, - lastEditedField: state.lastEditedField === INPUT ? OUTPUT : INPUT - }, () => this.recalcForm()); + const { state } = this + this.setState( + { + inputValue: state.outputValue, + outputValue: state.inputValue, + inputCurrency: state.outputCurrency, + outputCurrency: state.inputCurrency, + lastEditedField: state.lastEditedField === INPUT ? OUTPUT : INPUT + }, + () => this.recalcForm() + ) } isUnapproved() { - const { account, exchangeAddresses, selectors } = this.props; - const { inputCurrency, inputValue } = this.state; + const { account, exchangeAddresses, selectors } = this.props + const { inputCurrency, inputValue } = this.state if (!inputCurrency || inputCurrency === 'ETH') { - return false; + return false } const { value: allowance, label, decimals } = selectors().getApprovals( inputCurrency, account, exchangeAddresses.fromToken[inputCurrency] - ); + ) if (label && allowance.isLessThan(BN(inputValue * 10 ** decimals || 0))) { - return true; + return true } - return false; + return false } recalcForm() { - const { inputCurrency, outputCurrency, lastEditedField } = this.state; + const { inputCurrency, outputCurrency, lastEditedField } = this.state if (!inputCurrency || !outputCurrency) { - return; + return } - const editedValue = lastEditedField === INPUT ? this.state.inputValue : this.state.outputValue; + const editedValue = lastEditedField === INPUT ? this.state.inputValue : this.state.outputValue if (BN(editedValue).isZero()) { - return; + return } if (inputCurrency === outputCurrency) { this.setState({ inputValue: '', - outputValue: '', - }); - return; + outputValue: '' + }) + return } if (inputCurrency !== 'ETH' && outputCurrency !== 'ETH') { - this.recalcTokenTokenForm(); - return; + this.recalcTokenTokenForm() + return } - this.recalcEthTokenForm(); + this.recalcEthTokenForm() } recalcTokenTokenForm = () => { const { exchangeAddresses: { fromToken }, - selectors, - } = this.props; + selectors + } = this.props const { inputValue: oldInputValue, @@ -171,109 +179,109 @@ class Swap extends Component { outputCurrency, lastEditedField, exchangeRate: oldExchangeRate, - inputAmountB: oldInputAmountB, - } = this.state; + inputAmountB: oldInputAmountB + } = this.state - const exchangeAddressA = fromToken[inputCurrency]; - const exchangeAddressB = fromToken[outputCurrency]; + const exchangeAddressA = fromToken[inputCurrency] + const exchangeAddressB = fromToken[outputCurrency] - const { value: inputReserveA, decimals: inputDecimalsA } = selectors().getBalance(exchangeAddressA, inputCurrency); - const { value: outputReserveA }= selectors().getBalance(exchangeAddressA, 'ETH'); - const { value: inputReserveB } = selectors().getBalance(exchangeAddressB, 'ETH'); - const { value: outputReserveB, decimals: outputDecimalsB }= selectors().getBalance(exchangeAddressB, outputCurrency); + const { value: inputReserveA, decimals: inputDecimalsA } = selectors().getBalance(exchangeAddressA, inputCurrency) + const { value: outputReserveA } = selectors().getBalance(exchangeAddressA, 'ETH') + const { value: inputReserveB } = selectors().getBalance(exchangeAddressB, 'ETH') + const { value: outputReserveB, decimals: outputDecimalsB } = selectors().getBalance( + exchangeAddressB, + outputCurrency + ) if (lastEditedField === INPUT) { if (!oldInputValue) { return this.setState({ outputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const inputAmountA = BN(oldInputValue).multipliedBy(10 ** inputDecimalsA); + const inputAmountA = BN(oldInputValue).multipliedBy(10 ** inputDecimalsA) const outputAmountA = calculateEtherTokenOutput({ inputAmount: inputAmountA, inputReserve: inputReserveA, - outputReserve: outputReserveA, - }); + outputReserve: outputReserveA + }) // Redundant Variable for readability of the formala // OutputAmount from the first swap becomes InputAmount of the second swap - const inputAmountB = outputAmountA; + const inputAmountB = outputAmountA const outputAmountB = calculateEtherTokenOutput({ inputAmount: inputAmountB, inputReserve: inputReserveB, - outputReserve: outputReserveB, - }); + outputReserve: outputReserveB + }) - const outputValue = outputAmountB.dividedBy(BN(10 ** outputDecimalsB)).toFixed(7); - const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)); + const outputValue = outputAmountB.dividedBy(BN(10 ** outputDecimalsB)).toFixed(7) + const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (outputValue !== oldOutputValue) { - appendState.outputValue = outputValue; + appendState.outputValue = outputValue } - this.setState(appendState); + this.setState(appendState) } if (lastEditedField === OUTPUT) { if (!oldOutputValue) { return this.setState({ inputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const outputAmountB = BN(oldOutputValue).multipliedBy(10 ** outputDecimalsB); + const outputAmountB = BN(oldOutputValue).multipliedBy(10 ** outputDecimalsB) const inputAmountB = calculateEtherTokenInput({ outputAmount: outputAmountB, inputReserve: inputReserveB, - outputReserve: outputReserveB, - }); + outputReserve: outputReserveB + }) // Redundant Variable for readability of the formala // InputAmount from the first swap becomes OutputAmount of the second swap - const outputAmountA = inputAmountB; + const outputAmountA = inputAmountB const inputAmountA = calculateEtherTokenInput({ outputAmount: outputAmountA, inputReserve: inputReserveA, - outputReserve: outputReserveA, - }); + outputReserve: outputReserveA + }) - const inputValue = inputAmountA.isNegative() - ? 'N/A' - : inputAmountA.dividedBy(BN(10 ** inputDecimalsA)).toFixed(7); - const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)); + const inputValue = inputAmountA.isNegative() ? 'N/A' : inputAmountA.dividedBy(BN(10 ** inputDecimalsA)).toFixed(7) + const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (inputValue !== oldInputValue) { - appendState.inputValue = inputValue; + appendState.inputValue = inputValue } if (!inputAmountB.isEqualTo(BN(oldInputAmountB))) { - appendState.inputAmountB = inputAmountB; + appendState.inputAmountB = inputAmountB } - this.setState(appendState); + this.setState(appendState) } - - }; + } recalcEthTokenForm = () => { const { exchangeAddresses: { fromToken }, - selectors, - } = this.props; + selectors + } = this.props const { inputValue: oldInputValue, @@ -281,83 +289,87 @@ class Swap extends Component { inputCurrency, outputCurrency, lastEditedField, - exchangeRate: oldExchangeRate, - } = this.state; + exchangeRate: oldExchangeRate + } = this.state - const tokenAddress = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0]; - const exchangeAddress = fromToken[tokenAddress]; + const tokenAddress = [inputCurrency, outputCurrency].filter(currency => currency !== 'ETH')[0] + const exchangeAddress = fromToken[tokenAddress] if (!exchangeAddress) { - return; + return } - const { value: inputReserve, decimals: inputDecimals } = selectors().getBalance(exchangeAddress, inputCurrency); - const { value: outputReserve, decimals: outputDecimals }= selectors().getBalance(exchangeAddress, outputCurrency); + const { value: inputReserve, decimals: inputDecimals } = selectors().getBalance(exchangeAddress, inputCurrency) + const { value: outputReserve, decimals: outputDecimals } = selectors().getBalance(exchangeAddress, outputCurrency) if (lastEditedField === INPUT) { if (!oldInputValue) { return this.setState({ outputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const inputAmount = BN(oldInputValue).multipliedBy(10 ** inputDecimals); - const outputAmount = calculateEtherTokenOutput({ inputAmount, inputReserve, outputReserve }); - const outputValue = outputAmount.dividedBy(BN(10 ** outputDecimals)).toFixed(7); - const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)); + const inputAmount = BN(oldInputValue).multipliedBy(10 ** inputDecimals) + const outputAmount = calculateEtherTokenOutput({ inputAmount, inputReserve, outputReserve }) + const outputValue = outputAmount.dividedBy(BN(10 ** outputDecimals)).toFixed(7) + const exchangeRate = BN(outputValue).dividedBy(BN(oldInputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (outputValue !== oldOutputValue) { - appendState.outputValue = outputValue; + appendState.outputValue = outputValue } - this.setState(appendState); + this.setState(appendState) } else if (lastEditedField === OUTPUT) { if (!oldOutputValue) { return this.setState({ inputValue: '', - exchangeRate: BN(0), - }); + exchangeRate: BN(0) + }) } - const outputAmount = BN(oldOutputValue).multipliedBy(10 ** outputDecimals); - const inputAmount = calculateEtherTokenInput({ outputAmount, inputReserve, outputReserve }); - const inputValue = inputAmount.isNegative() - ? 'N/A' - : inputAmount.dividedBy(BN(10 ** inputDecimals)).toFixed(7); - const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)); + const outputAmount = BN(oldOutputValue).multipliedBy(10 ** outputDecimals) + const inputAmount = calculateEtherTokenInput({ outputAmount, inputReserve, outputReserve }) + const inputValue = inputAmount.isNegative() ? 'N/A' : inputAmount.dividedBy(BN(10 ** inputDecimals)).toFixed(7) + const exchangeRate = BN(oldOutputValue).dividedBy(BN(inputValue)) - const appendState = {}; + const appendState = {} if (!exchangeRate.isEqualTo(BN(oldExchangeRate))) { - appendState.exchangeRate = exchangeRate; + appendState.exchangeRate = exchangeRate } if (inputValue !== oldInputValue) { - appendState.inputValue = inputValue; + appendState.inputValue = inputValue } - this.setState(appendState); + this.setState(appendState) } - }; + } updateInput = amount => { - this.setState({ - inputValue: amount, - lastEditedField: INPUT, - }, this.recalcForm); - }; + this.setState( + { + inputValue: amount, + lastEditedField: INPUT + }, + this.recalcForm + ) + } updateOutput = amount => { - this.setState({ - outputValue: amount, - lastEditedField: OUTPUT, - }, this.recalcForm); - }; + this.setState( + { + outputValue: amount, + lastEditedField: OUTPUT + }, + this.recalcForm + ) + } onSwap = async () => { const { @@ -365,89 +377,97 @@ class Swap extends Component { account, web3, selectors, - addPendingTx, - } = this.props; - const { - inputValue, - outputValue, - inputCurrency, - outputCurrency, - inputAmountB, - lastEditedField, - } = this.state; - const ALLOWED_SLIPPAGE = 0.025; - const TOKEN_ALLOWED_SLIPPAGE = 0.04; - - const type = getSwapType(inputCurrency, outputCurrency); - const { decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); - const { decimals: outputDecimals } = selectors().getBalance(account, outputCurrency); - let deadline; + addPendingTx + } = this.props + const { inputValue, outputValue, inputCurrency, outputCurrency, inputAmountB, lastEditedField } = this.state + const ALLOWED_SLIPPAGE = 0.025 + const TOKEN_ALLOWED_SLIPPAGE = 0.04 + + const type = getSwapType(inputCurrency, outputCurrency) + const { decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) + const { decimals: outputDecimals } = selectors().getBalance(account, outputCurrency) + let deadline try { - deadline = await retry(() => getBlockDeadline(web3, 600)); - } catch(e) { + deadline = await retry(() => getBlockDeadline(web3, 600)) + } catch (e) { // TODO: Handle error. - return; + return } if (lastEditedField === INPUT) { // swap input ReactGA.event({ category: type, - action: 'SwapInput', - }); - switch(type) { + action: 'SwapInput' + }) + switch (type) { case 'ETH_TO_TOKEN': // let exchange = new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]); - new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]).methods .ethToTokenSwapInput( - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(0), - deadline, + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(0), + deadline ) - .send({ - from: account, - value: BN(inputValue).multipliedBy(10 ** 18).toFixed(0), - }, (err, data) => { - if (!err) { - addPendingTx(data); - this.reset(); + .send( + { + from: account, + value: BN(inputValue) + .multipliedBy(10 ** 18) + .toFixed(0) + }, + (err, data) => { + if (!err) { + addPendingTx(data) + this.reset() + } } - }); - break; + ) + break case 'TOKEN_TO_ETH': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToEthSwapInput( - BN(inputValue).multipliedBy(10 ** inputDecimals).toFixed(0), - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(0), - deadline, + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(0), + deadline ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break case 'TOKEN_TO_TOKEN': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToTokenSwapInput( - BN(inputValue).multipliedBy(10 ** inputDecimals).toFixed(0), - BN(outputValue).multipliedBy(10 ** outputDecimals).multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE).toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE) + .toFixed(0), '1', deadline, - outputCurrency, + outputCurrency ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break default: - break; + break } } @@ -455,169 +475,187 @@ class Swap extends Component { // swap output ReactGA.event({ category: type, - action: 'SwapOutput', - }); + action: 'SwapOutput' + }) switch (type) { case 'ETH_TO_TOKEN': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]).methods .ethToTokenSwapOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), - deadline, + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), + deadline ) - .send({ - from: account, - value: BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(0), - }, (err, data) => { - if (!err) { - addPendingTx(data); - this.reset(); + .send( + { + from: account, + value: BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(0) + }, + (err, data) => { + if (!err) { + addPendingTx(data) + this.reset() + } } - }); - break; + ) + break case 'TOKEN_TO_ETH': - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToEthSwapOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), - BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(0), - deadline, + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(0), + deadline ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break case 'TOKEN_TO_TOKEN': if (!inputAmountB) { - return; + return } - new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]) - .methods + new web3.eth.Contract(EXCHANGE_ABI, fromToken[inputCurrency]).methods .tokenToTokenSwapOutput( - BN(outputValue).multipliedBy(10 ** outputDecimals).toFixed(0), - BN(inputValue).multipliedBy(10 ** inputDecimals).multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE).toFixed(0), + BN(outputValue) + .multipliedBy(10 ** outputDecimals) + .toFixed(0), + BN(inputValue) + .multipliedBy(10 ** inputDecimals) + .multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE) + .toFixed(0), inputAmountB.multipliedBy(1.2).toFixed(0), deadline, - outputCurrency, + outputCurrency ) .send({ from: account }, (err, data) => { if (!err) { - addPendingTx(data); - this.reset(); + addPendingTx(data) + this.reset() } - }); - break; + }) + break default: - break; + break } } - }; + } renderSummary(inputError, outputError) { - const { - inputValue, - inputCurrency, - outputValue, - outputCurrency, - } = this.state; - const t = this.props.t; + const { inputValue, inputCurrency, outputValue, outputCurrency } = this.state + const t = this.props.t - const inputIsZero = BN(inputValue).isZero(); - const outputIsZero = BN(outputValue).isZero(); - let contextualInfo = ''; - let isError = false; + const inputIsZero = BN(inputValue).isZero() + const outputIsZero = BN(outputValue).isZero() + let contextualInfo = '' + let isError = false if (!inputCurrency || !outputCurrency) { - contextualInfo = t("selectTokenCont"); + contextualInfo = t('selectTokenCont') } if (!inputValue || !outputValue) { - contextualInfo = t("enterValueCont"); + contextualInfo = t('enterValueCont') } if (inputError || outputError) { - contextualInfo = inputError || outputError; - isError = true; + contextualInfo = inputError || outputError + isError = true } if (inputIsZero || outputIsZero) { - contextualInfo = t("noLiquidity"); + contextualInfo = t('noLiquidity') } if (this.isUnapproved()) { - contextualInfo = t("unlockTokenCont"); + contextualInfo = t('unlockTokenCont') } return ( - ); + ) } renderTransactionDetails = () => { - const { - inputValue, - inputCurrency, - outputValue, - outputCurrency, - lastEditedField, - } = this.state; - const { t, selectors, account } = this.props; + const { inputValue, inputCurrency, outputValue, outputCurrency, lastEditedField } = this.state + const { t, selectors, account } = this.props ReactGA.event({ category: 'TransactionDetail', - action: 'Open', - }); + action: 'Open' + }) - const ALLOWED_SLIPPAGE = 0.025; - const TOKEN_ALLOWED_SLIPPAGE = 0.04; + const ALLOWED_SLIPPAGE = 0.025 + const TOKEN_ALLOWED_SLIPPAGE = 0.04 - const type = getSwapType(inputCurrency, outputCurrency); - const { label: inputLabel } = selectors().getBalance(account, inputCurrency); - const { label: outputLabel } = selectors().getBalance(account, outputCurrency); + const type = getSwapType(inputCurrency, outputCurrency) + const { label: inputLabel } = selectors().getBalance(account, inputCurrency) + const { label: outputLabel } = selectors().getBalance(account, outputCurrency) // const label = lastEditedField === INPUT ? outputLabel : inputLabel; - let minOutput; - let maxInput; + let minOutput + let maxInput if (lastEditedField === INPUT) { - switch(type) { + switch (type) { case 'ETH_TO_TOKEN': - minOutput = BN(outputValue).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(7).trim(); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(7) + .trim() + break case 'TOKEN_TO_ETH': - minOutput = BN(outputValue).multipliedBy(1 - ALLOWED_SLIPPAGE).toFixed(7); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_TOKEN': - minOutput = BN(outputValue).multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE).toFixed(7); - break; + minOutput = BN(outputValue) + .multipliedBy(1 - TOKEN_ALLOWED_SLIPPAGE) + .toFixed(7) + break default: - break; + break } } if (lastEditedField === OUTPUT) { switch (type) { case 'ETH_TO_TOKEN': - maxInput = BN(inputValue).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(7).trim(); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(7) + .trim() + break case 'TOKEN_TO_ETH': - maxInput = BN(inputValue).multipliedBy(1 + ALLOWED_SLIPPAGE).toFixed(7); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + ALLOWED_SLIPPAGE) + .toFixed(7) + break case 'TOKEN_TO_TOKEN': - maxInput = BN(inputValue).multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE).toFixed(7); - break; + maxInput = BN(inputValue) + .multipliedBy(1 + TOKEN_ALLOWED_SLIPPAGE) + .toFixed(7) + break default: - break; + break } } @@ -625,81 +663,72 @@ class Swap extends Component { return (
- {t("youAreSelling")} {b(`${+inputValue} ${inputLabel}`)} {t("orTransFail")} + {t('youAreSelling')} {b(`${+inputValue} ${inputLabel}`)} {t('orTransFail')}
- {t("youWillReceive")} {b(`${+minOutput} ${outputLabel}`)} {t("orTransFail")} + {t('youWillReceive')} {b(`${+minOutput} ${outputLabel}`)} {t('orTransFail')}
- ); + ) } else { return (
- {t("youAreBuying")} {b(`${+outputValue} ${outputLabel}`)}. + {t('youAreBuying')} {b(`${+outputValue} ${outputLabel}`)}.
- {t("itWillCost")} {b(`${+maxInput} ${inputLabel}`)} {t("orTransFail")} + {t('itWillCost')} {b(`${+maxInput} ${inputLabel}`)} {t('orTransFail')}
- ); + ) } } renderExchangeRate() { - const { t, account, selectors } = this.props; - const { exchangeRate, inputCurrency, outputCurrency } = this.state; - const { label: inputLabel } = selectors().getBalance(account, inputCurrency); - const { label: outputLabel } = selectors().getBalance(account, outputCurrency); + const { t, account, selectors } = this.props + const { exchangeRate, inputCurrency, outputCurrency } = this.state + const { label: inputLabel } = selectors().getBalance(account, inputCurrency) + const { label: outputLabel } = selectors().getBalance(account, outputCurrency) if (!exchangeRate || exchangeRate.isNaN() || !inputCurrency || !outputCurrency) { return (
- {t("exchangeRate")} + {t('exchangeRate')} -
- ); + ) } return (
- {t("exchangeRate")} - - {`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`} - + {t('exchangeRate')} + {`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`}
- ); + ) } renderBalance(currency, balance, decimals) { if (!currency || decimals === 0) { - return ''; + return '' } const balanceInput = balance.dividedBy(BN(10 ** decimals)).toFixed(4) - return this.props.t("balance", { balanceInput }) + return this.props.t('balance', { balanceInput }) } render() { - const { t, selectors, account } = this.props; - const { - lastEditedField, - inputCurrency, - outputCurrency, - inputValue, - outputValue, - } = this.state; - const estimatedText = `(${t("estimated")})`; + const { t, selectors, account } = this.props + const { lastEditedField, inputCurrency, outputCurrency, inputValue, outputValue } = this.state + const estimatedText = `(${t('estimated')})` - const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency); - const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency); - - const { inputError, outputError, isValid } = this.validate(); + const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency) + const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency) + const { inputError, outputError, isValid } = this.validate() return (
@@ -708,17 +737,17 @@ class Swap extends Component {
this.setState({ inputCurrency }, this.recalcForm)} @@ -730,11 +759,16 @@ class Swap extends Component { />
- swap + swap
this.setState({ outputCurrency }, this.recalcForm)} @@ -745,77 +779,85 @@ class Swap extends Component { errorMessage={outputError} disableUnlock /> - { this.renderExchangeRate() } - { this.renderSummary(inputError, outputError) } + {this.renderExchangeRate()} + {this.renderSummary(inputError, outputError)}
- ); + ) } } export default connect( state => ({ balances: state.web3connect.balances, - isConnected: !!state.web3connect.account && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID||1), + isConnected: !!state.web3connect.account && state.web3connect.networkId === (process.env.REACT_APP_NETWORK_ID || 1), account: state.web3connect.account, web3: state.web3connect.web3, - exchangeAddresses: state.addresses.exchangeAddresses, + exchangeAddresses: state.addresses.exchangeAddresses }), dispatch => ({ selectors: () => dispatch(selectors()), - addPendingTx: id => dispatch(addPendingTx(id)), - }), -)(withNamespaces()(Swap)); + addPendingTx: id => dispatch(addPendingTx(id)) + }) +)(withNamespaces()(Swap)) -const b = text => {text}; +const b = text => {text} -function calculateEtherTokenOutput({ inputAmount: rawInput, inputReserve: rawReserveIn, outputReserve: rawReserveOut }) { - const inputAmount = BN(rawInput); - const inputReserve = BN(rawReserveIn); - const outputReserve = BN(rawReserveOut); +function calculateEtherTokenOutput({ + inputAmount: rawInput, + inputReserve: rawReserveIn, + outputReserve: rawReserveOut +}) { + const inputAmount = BN(rawInput) + const inputReserve = BN(rawReserveIn) + const outputReserve = BN(rawReserveOut) if (inputAmount.isLessThan(BN(10 ** 9))) { - console.warn(`inputAmount is only ${inputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`); + console.warn(`inputAmount is only ${inputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`) } - const numerator = inputAmount.multipliedBy(outputReserve).multipliedBy(997); - const denominator = inputReserve.multipliedBy(1000).plus(inputAmount.multipliedBy(997)); + const numerator = inputAmount.multipliedBy(outputReserve).multipliedBy(997) + const denominator = inputReserve.multipliedBy(1000).plus(inputAmount.multipliedBy(997)) - return numerator.dividedBy(denominator); + return numerator.dividedBy(denominator) } -function calculateEtherTokenInput({ outputAmount: rawOutput, inputReserve: rawReserveIn, outputReserve: rawReserveOut }) { - const outputAmount = BN(rawOutput); - const inputReserve = BN(rawReserveIn); - const outputReserve = BN(rawReserveOut); +function calculateEtherTokenInput({ + outputAmount: rawOutput, + inputReserve: rawReserveIn, + outputReserve: rawReserveOut +}) { + const outputAmount = BN(rawOutput) + const inputReserve = BN(rawReserveIn) + const outputReserve = BN(rawReserveOut) if (outputAmount.isLessThan(BN(10 ** 9))) { - console.warn(`inputAmount is only ${outputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`); + console.warn(`inputAmount is only ${outputAmount.toFixed(0)}. Did you forget to multiply by 10 ** decimals?`) } - const numerator = outputAmount.multipliedBy(inputReserve).multipliedBy(1000); - const denominator = outputReserve.minus(outputAmount).multipliedBy(997); - return (numerator.dividedBy(denominator)).plus(1); + const numerator = outputAmount.multipliedBy(inputReserve).multipliedBy(1000) + const denominator = outputReserve.minus(outputAmount).multipliedBy(997) + return numerator.dividedBy(denominator).plus(1) } function getSwapType(inputCurrency, outputCurrency) { if (!inputCurrency || !outputCurrency) { - return; + return } if (inputCurrency === outputCurrency) { - return; + return } if (inputCurrency !== 'ETH' && outputCurrency !== 'ETH') { @@ -823,12 +865,12 @@ function getSwapType(inputCurrency, outputCurrency) { } if (inputCurrency === 'ETH') { - return 'ETH_TO_TOKEN'; + return 'ETH_TO_TOKEN' } if (outputCurrency === 'ETH') { - return 'TOKEN_TO_ETH'; + return 'TOKEN_TO_ETH' } - return; + return } diff --git a/src/pages/Swap/swap.scss b/src/pages/Swap/swap.scss index bdf849ed915..fd74521a238 100644 --- a/src/pages/Swap/swap.scss +++ b/src/pages/Swap/swap.scss @@ -1,4 +1,4 @@ -@import "../../variables.scss"; +@import '../../variables.scss'; .swap { @extend %col-nowrap; @@ -6,22 +6,22 @@ background-color: $white; &--inactive { - opacity: .5; + opacity: 0.5; } &__content { - padding: 1rem .75rem; + padding: 1rem 0.75rem; flex: 1 1 auto; height: 0; overflow-y: auto; } &__down-arrow { - width: .625rem; - height: .625rem; + width: 0.625rem; + height: 0.625rem; position: relative; z-index: 200; - padding: .875rem; + padding: 0.875rem; &--clickable { cursor: pointer; @@ -38,8 +38,8 @@ @extend %row-nowrap; align-items: center; color: $dove-gray; - font-size: .75rem; - padding: .5rem 1rem; + font-size: 0.75rem; + padding: 0.5rem 1rem; } &__exchange-rate { diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js index a3e6c0cfc10..5bdd344bc0f 100644 --- a/src/registerServiceWorker.js +++ b/src/registerServiceWorker.js @@ -13,28 +13,26 @@ const isLocalhost = Boolean( // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +) export default function register() { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + const publicUrl = new URL(process.env.PUBLIC_URL, window.location) if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; + return } window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` if (isLocalhost) { // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); + checkValidServiceWorker(swUrl) // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. @@ -42,13 +40,13 @@ export default function register() { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://goo.gl/SC7cgQ' - ); - }); + ) + }) } else { // Is not local host. Just register service worker - registerValidSW(swUrl); + registerValidSW(swUrl) } - }); + }) } } @@ -57,7 +55,7 @@ function registerValidSW(swUrl) { .register(swUrl) .then(registration => { registration.onupdatefound = () => { - const installingWorker = registration.installing; + const installingWorker = registration.installing installingWorker.onstatechange = () => { if (installingWorker.state === 'installed') { if (navigator.serviceWorker.controller) { @@ -65,20 +63,20 @@ function registerValidSW(swUrl) { // the fresh content will have been added to the cache. // It's the perfect time to display a "New content is // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); + console.log('New content is available; please refresh.') } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + console.log('Content is cached for offline use.') } } - }; - }; + } + } }) .catch(error => { - console.error('Error during service worker registration:', error); - }); + console.error('Error during service worker registration:', error) + }) } function checkValidServiceWorker(swUrl) { @@ -86,32 +84,27 @@ function checkValidServiceWorker(swUrl) { fetch(swUrl) .then(response => { // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { + if (response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { registration.unregister().then(() => { - window.location.reload(); - }); - }); + window.location.reload() + }) + }) } else { // Service worker found. Proceed as normal. - registerValidSW(swUrl); + registerValidSW(swUrl) } }) .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); + console.log('No internet connection found. App is running in offline mode.') + }) } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); + registration.unregister() + }) } } diff --git a/src/store/index.js b/src/store/index.js index 4d4f3021117..3de9addf45b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,3 +1,3 @@ -import store from './store.dev'; +import store from './store.dev' -export default store; \ No newline at end of file +export default store diff --git a/src/store/initial-state.js b/src/store/initial-state.js index ff8b4c56321..b1c6ea436a5 100644 --- a/src/store/initial-state.js +++ b/src/store/initial-state.js @@ -1 +1 @@ -export default {}; +export default {} diff --git a/src/store/store.dev.js b/src/store/store.dev.js index 43ae0528c3a..0da5b45ecdf 100644 --- a/src/store/store.dev.js +++ b/src/store/store.dev.js @@ -1,19 +1,12 @@ -import { applyMiddleware, compose, createStore } from 'redux'; +import { applyMiddleware, compose, createStore } from 'redux' import thunk from 'redux-thunk' -import initialState from './initial-state'; -import reducer from '../ducks'; +import initialState from './initial-state' +import reducer from '../ducks' -const middleware = [thunk]; -const enhancers = []; -const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; +const middleware = [thunk] +const enhancers = [] +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose -const store = createStore( - reducer, - initialState, - composeEnhancers( - applyMiddleware(...middleware), - ...enhancers, - ) -); +const store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(...middleware), ...enhancers)) -export default store; \ No newline at end of file +export default store diff --git a/src/variables.scss b/src/variables.scss index 8e4d6b7288e..cad2d73ed6f 100644 --- a/src/variables.scss +++ b/src/variables.scss @@ -2,26 +2,26 @@ $white: #fff; $black: #000; // Gray -$concrete-gray: #FAFAFA; -$mercury-gray: #E1E1E1; -$silver-gray: #C4C4C4; -$chalice-gray: #AEAEAE; +$concrete-gray: #fafafa; +$mercury-gray: #e1e1e1; +$silver-gray: #c4c4c4; +$chalice-gray: #aeaeae; $dove-gray: #737373; -$mine-shaft-gray: #2B2B2B; +$mine-shaft-gray: #2b2b2b; // Blue -$zumthor-blue: #EBF4FF; -$malibu-blue: #5CA2FF; -$royal-blue: #2F80ED; +$zumthor-blue: #ebf4ff; +$malibu-blue: #5ca2ff; +$royal-blue: #2f80ed; // Purple -$wisteria-purple: #DC6BE5; +$wisteria-purple: #dc6be5; // Red -$salmon-red: #FF6871; +$salmon-red: #ff6871; // Orange -$pizazz-orange: #FF8F05; +$pizazz-orange: #ff8f05; %col-nowrap { display: flex; @@ -57,7 +57,6 @@ $pizazz-orange: #FF8F05; &:disabled { background-color: $mercury-gray; } - } %borderless-input { @@ -68,7 +67,6 @@ $pizazz-orange: #FF8F05; flex: 1 1 auto; width: 0; - &::placeholder { color: $mercury-gray; } diff --git a/yarn.lock b/yarn.lock index d01f9e5072c..a20b01416c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9966,6 +9966,11 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +prettier@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008" + integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw== + pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"