diff --git a/common/actions/config/actionCreators.ts b/common/actions/config/actionCreators.ts index f1cb8b2a..1fd60c9b 100644 --- a/common/actions/config/actionCreators.ts +++ b/common/actions/config/actionCreators.ts @@ -1,6 +1,6 @@ import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; -import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data'; +import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config'; export type TToggleOfflineConfig = typeof toggleOfflineConfig; export function toggleOfflineConfig(): interfaces.ToggleOfflineAction { diff --git a/common/actions/config/actionTypes.ts b/common/actions/config/actionTypes.ts index cd147b74..5552661e 100644 --- a/common/actions/config/actionTypes.ts +++ b/common/actions/config/actionTypes.ts @@ -1,5 +1,5 @@ import { TypeKeys } from './constants'; -import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data'; +import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config'; /*** Toggle Offline ***/ export interface ToggleOfflineAction { diff --git a/common/actions/customTokens/actionCreators.ts b/common/actions/customTokens/actionCreators.ts index a18e8435..a95ed461 100644 --- a/common/actions/customTokens/actionCreators.ts +++ b/common/actions/customTokens/actionCreators.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; diff --git a/common/actions/customTokens/actionTypes.ts b/common/actions/customTokens/actionTypes.ts index 2d214915..7c4f0445 100644 --- a/common/actions/customTokens/actionTypes.ts +++ b/common/actions/customTokens/actionTypes.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; import { TypeKeys } from './constants'; /*** Add custom token ***/ export interface AddCustomTokenAction { diff --git a/common/api/bity.ts b/common/api/bity.ts index 979d47b4..8ba10d8d 100644 --- a/common/api/bity.ts +++ b/common/api/bity.ts @@ -1,4 +1,4 @@ -import bityConfig, { WhitelistedCoins } from 'config/bity'; +import { WhitelistedCoins, bityConfig } from 'config'; import { checkHttpStatus, parseJSON, filter } from './utils'; import bitcoinIcon from 'assets/images/bitcoin.png'; import repIcon from 'assets/images/augur.png'; diff --git a/common/components/AddressField.tsx b/common/components/AddressField.tsx index 28d97b5d..aff92573 100644 --- a/common/components/AddressField.tsx +++ b/common/components/AddressField.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { AddressFieldFactory } from './AddressFieldFactory'; -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; interface Props { isReadOnly?: boolean; diff --git a/common/components/BalanceSidebar/AccountInfo.tsx b/common/components/BalanceSidebar/AccountInfo.tsx index 059d1c6f..499da178 100644 --- a/common/components/BalanceSidebar/AccountInfo.tsx +++ b/common/components/BalanceSidebar/AccountInfo.tsx @@ -1,5 +1,5 @@ import { Identicon, UnitDisplay } from 'components/ui'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet'; import React from 'react'; import translate from 'translations'; @@ -127,11 +127,11 @@ export default class AccountInfo extends React.Component { {!!blockExplorer && (
  • - {`${network.name} (${blockExplorer.name})`} + {`${network.name} (${blockExplorer.origin})`}
  • )} diff --git a/common/components/BalanceSidebar/EquivalentValues.tsx b/common/components/BalanceSidebar/EquivalentValues.tsx index ed885a91..0a3add2a 100644 --- a/common/components/BalanceSidebar/EquivalentValues.tsx +++ b/common/components/BalanceSidebar/EquivalentValues.tsx @@ -5,7 +5,7 @@ import { State } from 'reducers/rates'; import { rateSymbols, TFetchCCRates } from 'actions/rates'; import { TokenBalance } from 'selectors/wallet'; import { Balance } from 'libs/wallet'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { ETH_DECIMAL, convertTokenBase } from 'libs/units'; import Spinner from 'components/ui/Spinner'; import UnitDisplay from 'components/ui/UnitDisplay'; diff --git a/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx b/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx index 6cfccea9..e14c30a2 100644 --- a/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx +++ b/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx @@ -1,6 +1,6 @@ import React from 'react'; import classnames from 'classnames'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators'; import translate from 'translations'; import NewTabLink from 'components/ui/NewTabLink'; diff --git a/common/components/BalanceSidebar/TokenBalances/Balances.tsx b/common/components/BalanceSidebar/TokenBalances/Balances.tsx index 7b5b9310..e8f08c7c 100644 --- a/common/components/BalanceSidebar/TokenBalances/Balances.tsx +++ b/common/components/BalanceSidebar/TokenBalances/Balances.tsx @@ -1,6 +1,6 @@ import React from 'react'; import translate from 'translations'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { TokenBalance } from 'selectors/wallet'; import AddCustomTokenForm from './AddCustomTokenForm'; import TokenRow from './TokenRow'; diff --git a/common/components/BalanceSidebar/TokenBalances/index.tsx b/common/components/BalanceSidebar/TokenBalances/index.tsx index 735498f4..7fca475d 100644 --- a/common/components/BalanceSidebar/TokenBalances/index.tsx +++ b/common/components/BalanceSidebar/TokenBalances/index.tsx @@ -15,7 +15,7 @@ import { } from 'actions/wallet'; import { getAllTokens } from 'selectors/config'; import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet'; -import { Token } from 'config/data'; +import { Token } from 'config'; import translate from 'translations'; import Balances from './Balances'; import Spinner from 'components/ui/Spinner'; diff --git a/common/components/BalanceSidebar/index.tsx b/common/components/BalanceSidebar/index.tsx index ade5e0f9..a44dd4a3 100644 --- a/common/components/BalanceSidebar/index.tsx +++ b/common/components/BalanceSidebar/index.tsx @@ -1,5 +1,5 @@ import { fetchCCRates, TFetchCCRates } from 'actions/rates'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { IWallet, Balance } from 'libs/wallet'; import React from 'react'; import { connect } from 'react-redux'; diff --git a/common/components/ConfirmationModal/components/Details/Node.tsx b/common/components/ConfirmationModal/components/Details/Node.tsx index c6e5f56a..2d29f2a5 100644 --- a/common/components/ConfirmationModal/components/Details/Node.tsx +++ b/common/components/ConfirmationModal/components/Details/Node.tsx @@ -1,4 +1,4 @@ -import { NodeConfig } from 'config/data'; +import { NodeConfig } from 'config'; import React, { Component } from 'react'; import { AppState } from 'reducers'; import { connect } from 'react-redux'; diff --git a/common/components/DataField.tsx b/common/components/DataField.tsx index c2bd4b92..14b14b9c 100644 --- a/common/components/DataField.tsx +++ b/common/components/DataField.tsx @@ -1,7 +1,7 @@ import { DataFieldFactory } from './DataFieldFactory'; import React from 'react'; import translate from 'translations'; -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; export const DataField: React.SFC<{}> = () => ( { // const checkTxLink = `https://www.myetherwallet.com?txHash=${txHash}/#check-tx-status`; - const txHashLink = blockExplorer.tx(txHash); + const txHashLink = blockExplorer.txUrl(txHash); return (
    diff --git a/common/components/Footer/index.tsx b/common/components/Footer/index.tsx index 26c054b3..f92ab839 100644 --- a/common/components/Footer/index.tsx +++ b/common/components/Footer/index.tsx @@ -7,7 +7,7 @@ import { donationAddressMap, VERSION, knowledgeBaseURL -} from 'config/data'; +} from 'config'; import React from 'react'; import translate from 'translations'; import './index.scss'; diff --git a/common/components/GasSlider/components/SimpleGas.tsx b/common/components/GasSlider/components/SimpleGas.tsx index 82bb6f54..580ff928 100644 --- a/common/components/GasSlider/components/SimpleGas.tsx +++ b/common/components/GasSlider/components/SimpleGas.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Slider from 'rc-slider'; import translate from 'translations'; -import { gasPriceDefaults } from 'config/data'; +import { gasPriceDefaults } from 'config'; import FeeSummary from './FeeSummary'; import { TInputGasPrice } from 'actions/transaction'; import './SimpleGas.scss'; diff --git a/common/components/GenerateKeystoreModal/index.tsx b/common/components/GenerateKeystoreModal/index.tsx index 8c581e9c..16ca30d2 100644 --- a/common/components/GenerateKeystoreModal/index.tsx +++ b/common/components/GenerateKeystoreModal/index.tsx @@ -3,7 +3,7 @@ import { generateKeystoreFileInfo, KeystoreFile } from 'utils/keystore'; import Modal from 'components/ui/Modal'; import Input from './Input'; import translate, { translateRaw } from 'translations'; -import { MINIMUM_PASSWORD_LENGTH } from 'config/data'; +import { MINIMUM_PASSWORD_LENGTH } from 'config'; import { isValidPrivKey } from 'libs/validators'; import './index.scss'; diff --git a/common/components/Header/components/CustomNodeModal.tsx b/common/components/Header/components/CustomNodeModal.tsx index 5a05dfba..0f78b798 100644 --- a/common/components/Header/components/CustomNodeModal.tsx +++ b/common/components/Header/components/CustomNodeModal.tsx @@ -2,7 +2,7 @@ import React from 'react'; import classnames from 'classnames'; import Modal, { IButton } from 'components/ui/Modal'; import translate from 'translations'; -import { NETWORKS, CustomNodeConfig, CustomNetworkConfig } from 'config/data'; +import { NETWORKS, CustomNodeConfig, CustomNetworkConfig } from 'config'; import { makeCustomNodeId } from 'utils/node'; import { makeCustomNetworkId } from 'utils/network'; @@ -303,10 +303,16 @@ export default class CustomNodeModal extends React.Component { } private makeCustomNetworkConfigFromState(): CustomNetworkConfig { + const similarNetworkConfig = Object.values(NETWORKS).find( + n => n.chainId === +this.state.customNetworkChainId + ); + const dPathFormats = similarNetworkConfig ? similarNetworkConfig.dPathFormats : null; + return { name: this.state.customNetworkName, unit: this.state.customNetworkUnit, - chainId: this.state.customNetworkChainId ? parseInt(this.state.customNetworkChainId, 10) : 0 + chainId: this.state.customNetworkChainId ? parseInt(this.state.customNetworkChainId, 10) : 0, + dPathFormats }; } @@ -352,6 +358,7 @@ export default class CustomNodeModal extends React.Component { if (this.state.network === CUSTOM) { const network = this.makeCustomNetworkConfigFromState(); + this.props.handleAddCustomNetwork(network); } diff --git a/common/components/Header/components/GasPriceDropdown.tsx b/common/components/Header/components/GasPriceDropdown.tsx index a2610815..ec9f5a3f 100644 --- a/common/components/Header/components/GasPriceDropdown.tsx +++ b/common/components/Header/components/GasPriceDropdown.tsx @@ -1,4 +1,4 @@ -import { gasPriceDefaults, knowledgeBaseURL } from 'config/data'; +import { gasPriceDefaults, knowledgeBaseURL } from 'config'; import throttle from 'lodash/throttle'; import React, { Component } from 'react'; import DropdownShell from 'components/ui/DropdownShell'; diff --git a/common/components/Header/components/Navigation.tsx b/common/components/Header/components/Navigation.tsx index 655b14f4..1f68c236 100644 --- a/common/components/Header/components/Navigation.tsx +++ b/common/components/Header/components/Navigation.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import NavigationLink from './NavigationLink'; -import { knowledgeBaseURL } from 'config/data'; +import { knowledgeBaseURL } from 'config'; import './Navigation.scss'; export interface TabLink { diff --git a/common/components/Header/index.tsx b/common/components/Header/index.tsx index 480fd72e..e4de9ee6 100644 --- a/common/components/Header/index.tsx +++ b/common/components/Header/index.tsx @@ -20,7 +20,7 @@ import { NodeConfig, CustomNodeConfig, CustomNetworkConfig -} from 'config/data'; +} from 'config'; import GasPriceDropdown from './components/GasPriceDropdown'; import Navigation from './components/Navigation'; import CustomNodeModal from './components/CustomNodeModal'; diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 1bee7f0b..1276e67c 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -19,7 +19,6 @@ import { import { reset, TReset } from 'actions/transaction'; import translate from 'translations'; import { - DigitalBitboxDecrypt, KeystoreDecrypt, LedgerNanoSDecrypt, MnemonicDecrypt, @@ -31,38 +30,53 @@ import { WalletButton } from './components'; import { AppState } from 'reducers'; -import { knowledgeBaseURL, isWeb3NodeAvailable } from 'config/data'; -import { IWallet } from 'libs/wallet'; -import DISABLES from './disables.json'; +import DISABLES from './disables'; import { showNotification, TShowNotification } from 'actions/notifications'; -import DigitalBitboxIcon from 'assets/images/wallets/digital-bitbox.svg'; import LedgerIcon from 'assets/images/wallets/ledger.svg'; import MetamaskIcon from 'assets/images/wallets/metamask.svg'; import MistIcon from 'assets/images/wallets/mist.svg'; import TrezorIcon from 'assets/images/wallets/trezor.svg'; import './WalletDecrypt.scss'; +import { + SecureWalletName, + InsecureWalletName, + MiscWalletName, + WalletName, + isWeb3NodeAvailable, + knowledgeBaseURL +} from 'config'; +import { unSupportedWalletFormatsOnNetwork } from 'utils/network'; +import { getNetworkConfig } from '../../selectors/config'; -interface Props { - resetTransactionState: TReset; +interface OwnProps { + hidden?: boolean; + disabledWallets?: WalletName[]; +} + +interface DispatchProps { unlockKeystore: TUnlockKeystore; unlockMnemonic: TUnlockMnemonic; unlockPrivateKey: TUnlockPrivateKey; - setWallet: TSetWallet; unlockWeb3: TUnlockWeb3; + setWallet: TSetWallet; resetWallet: TResetWallet; + resetTransactionState: TReset; showNotification: TShowNotification; - wallet: IWallet; - hidden?: boolean; +} + +interface StateProps { + computedDisabledWallets: WalletName[]; offline: boolean; - disabledWallets?: string[]; isWalletPending: AppState['wallet']['isWalletPending']; isPasswordPending: AppState['wallet']['isPasswordPending']; } +type Props = OwnProps & StateProps & DispatchProps; + type UnlockParams = {} | PrivateKeyValue; interface State { - selectedWalletKey: string | null; + selectedWalletKey: WalletName | null; value: UnlockParams | null; } @@ -71,13 +85,13 @@ interface BaseWalletInfo { component: any; initialParams: object; unlock: any; - helpLink?: string; + helpLink: string; isReadOnly?: boolean; attemptUnlock?: boolean; } export interface SecureWalletInfo extends BaseWalletInfo { - icon?: string | null; + icon?: string; description: string; } @@ -85,6 +99,9 @@ export interface InsecureWalletInfo extends BaseWalletInfo { example: string; } +// tslint:disable-next-line:no-empty-interface +interface MiscWalletInfo extends InsecureWalletInfo {} + const WEB3_TYPES = { MetamaskInpageProvider: { lid: 'x_MetaMask', @@ -95,15 +112,20 @@ const WEB3_TYPES = { icon: MistIcon } }; + +type SecureWallets = { [key in SecureWalletName]: SecureWalletInfo }; +type InsecureWallets = { [key in InsecureWalletName]: InsecureWalletInfo }; +type MiscWallet = { [key in MiscWalletName]: MiscWalletInfo }; +type Wallets = SecureWallets & InsecureWallets & MiscWallet; + const WEB3_TYPE: string | false = (window as any).web3 && (window as any).web3.currentProvider.constructor.name; -const SECURE_WALLETS = ['web3', 'ledger-nano-s', 'trezor']; -const INSECURE_WALLETS = ['private-key', 'keystore-file', 'mnemonic-phrase']; - export class WalletDecrypt extends Component { - public WALLETS: { [key: string]: SecureWalletInfo | InsecureWalletInfo } = { - web3: { + // https://github.com/Microsoft/TypeScript/issues/13042 + // index signature should become [key: Wallets] (from config) once typescript bug is fixed + public WALLETS: Wallets = { + [SecureWalletName.WEB3]: { lid: WEB3_TYPE ? WEB3_TYPES[WEB3_TYPE].lid : 'x_Web3', icon: WEB3_TYPE && WEB3_TYPES[WEB3_TYPE].icon, description: 'ADD_Web3Desc', @@ -113,7 +135,7 @@ export class WalletDecrypt extends Component { attemptUnlock: true, helpLink: `${knowledgeBaseURL}/migration/moving-from-private-key-to-metamask` }, - 'ledger-nano-s': { + [SecureWalletName.LEDGER_NANO_S]: { lid: 'x_Ledger', icon: LedgerIcon, description: 'ADD_HardwareDesc', @@ -123,7 +145,7 @@ export class WalletDecrypt extends Component { helpLink: 'https://ledger.zendesk.com/hc/en-us/articles/115005200009-How-to-use-MyEtherWallet-with-Ledger' }, - trezor: { + [SecureWalletName.TREZOR]: { lid: 'x_Trezor', icon: TrezorIcon, description: 'ADD_HardwareDesc', @@ -132,16 +154,7 @@ export class WalletDecrypt extends Component { unlock: this.props.setWallet, helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html' }, - 'digital-bitbox': { - lid: 'x_DigitalBitbox', - icon: DigitalBitboxIcon, - description: 'ADD_HardwareDesc', - component: DigitalBitboxDecrypt, - initialParams: {}, - unlock: this.props.setWallet, - helpLink: 'https://digitalbitbox.com/ethereum' - }, - 'keystore-file': { + [InsecureWalletName.KEYSTORE_FILE]: { lid: 'x_Keystore2', example: 'UTC--2017-12-15T17-35-22.547Z--6be6e49e82425a5aa56396db03512f2cc10e95e8', component: KeystoreDecrypt, @@ -152,7 +165,7 @@ export class WalletDecrypt extends Component { unlock: this.props.unlockKeystore, helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html` }, - 'mnemonic-phrase': { + [InsecureWalletName.MNEMONIC_PHRASE]: { lid: 'x_Mnemonic', example: 'brain surround have swap horror cheese file distinct', component: MnemonicDecrypt, @@ -160,7 +173,7 @@ export class WalletDecrypt extends Component { unlock: this.props.unlockMnemonic, helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html` }, - 'private-key': { + [InsecureWalletName.PRIVATE_KEY]: { lid: 'x_PrivKey2', example: 'f1d0e0789c6d40f399ca90cc674b7858de4c719e0d5752a60d5d2f6baa45d4c9', component: PrivateKeyDecrypt, @@ -171,7 +184,7 @@ export class WalletDecrypt extends Component { unlock: this.props.unlockPrivateKey, helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html` }, - 'view-only': { + [MiscWalletName.VIEW_ONLY]: { lid: 'View Address', example: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8', component: ViewOnlyDecrypt, @@ -181,12 +194,13 @@ export class WalletDecrypt extends Component { isReadOnly: true } }; + public state: State = { selectedWalletKey: null, value: null }; - public componentWillReceiveProps(nextProps) { + public componentWillReceiveProps(nextProps: Props) { // Reset state when unlock is hidden / revealed if (nextProps.hidden !== this.props.hidden) { this.setState({ @@ -218,10 +232,12 @@ export class WalletDecrypt extends Component { onUnlock={this.onUnlock} showNotification={this.props.showNotification} isWalletPending={ - this.state.selectedWalletKey === 'keystore-file' ? this.props.isWalletPending : undefined + this.state.selectedWalletKey === InsecureWalletName.KEYSTORE_FILE + ? this.props.isWalletPending + : undefined } isPasswordPending={ - this.state.selectedWalletKey === 'keystore-file' + this.state.selectedWalletKey === InsecureWalletName.KEYSTORE_FILE ? this.props.isPasswordPending : undefined } @@ -230,64 +246,72 @@ export class WalletDecrypt extends Component { } public buildWalletOptions() { - const viewOnly = this.WALLETS['view-only'] as InsecureWalletInfo; + const SECURE_WALLETS = Object.values(SecureWalletName); + const INSECURE_WALLETS = Object.values(InsecureWalletName); + const MISC_WALLETS = Object.values(MiscWalletName); return (

    {translate('decrypt_Access')}

    - {SECURE_WALLETS.map(type => { - const wallet = this.WALLETS[type] as SecureWalletInfo; + {SECURE_WALLETS.map((walletType: SecureWalletName) => { + const wallet = this.WALLETS[walletType]; return ( ); })}
    - {INSECURE_WALLETS.map(type => { - const wallet = this.WALLETS[type] as InsecureWalletInfo; + {INSECURE_WALLETS.map((walletType: InsecureWalletName) => { + const wallet = this.WALLETS[walletType]; return ( ); })} - + {MISC_WALLETS.map((walletType: MiscWalletName) => { + const wallet = this.WALLETS[walletType]; + return ( + + ); + })}
    ); } - public handleWalletChoice = async (walletType: string) => { + public handleWalletChoice = async (walletType: WalletName) => { const wallet = this.WALLETS[walletType]; + if (!wallet) { return; } @@ -301,7 +325,7 @@ export class WalletDecrypt extends Component { wallet.unlock(); } - setTimeout(() => { + window.setTimeout(() => { this.setState({ selectedWalletKey: walletType, value: wallet.initialParams @@ -375,28 +399,31 @@ export class WalletDecrypt extends Component { this.props.resetTransactionState(); }; - private isWalletDisabled = (walletKey: string) => { + private isWalletDisabled = (walletKey: WalletName) => { if (this.props.offline && DISABLES.ONLINE_ONLY.includes(walletKey)) { return true; } - if (!this.props.disabledWallets) { - return false; - } - return this.props.disabledWallets.indexOf(walletKey) !== -1; + return this.props.computedDisabledWallets.indexOf(walletKey) !== -1; }; } -function mapStateToProps(state: AppState) { +function mapStateToProps(state: AppState, ownProps: Props) { + const { disabledWallets } = ownProps; + const network = getNetworkConfig(state); + const networkDisabledFormats = unSupportedWalletFormatsOnNetwork(network); + const computedDisabledWallets = disabledWallets + ? disabledWallets.concat(networkDisabledFormats) + : networkDisabledFormats; return { + computedDisabledWallets, offline: state.config.offline, - wallet: state.wallet.inst, isWalletPending: state.wallet.isWalletPending, isPasswordPending: state.wallet.isPasswordPending }; } -export default connect(mapStateToProps, { +export default connect(mapStateToProps, { unlockKeystore, unlockMnemonic, unlockPrivateKey, diff --git a/common/components/WalletDecrypt/components/DeterministicWalletsModal.scss b/common/components/WalletDecrypt/components/DeterministicWalletsModal.scss index b6bf5f2d..a7c48142 100644 --- a/common/components/WalletDecrypt/components/DeterministicWalletsModal.scss +++ b/common/components/WalletDecrypt/components/DeterministicWalletsModal.scss @@ -1,15 +1,14 @@ -@import "common/sass/variables"; -@import "common/sass/mixins"; +@import 'common/sass/variables'; +@import 'common/sass/mixins'; .DWModal { - width: 690px; - &-path { - display: block; + display: flex; margin-bottom: 20px; &-label { font-size: $font-size-medium; + margin-right: 16px; } .form-control { @@ -17,12 +16,18 @@ width: auto; margin: 0 0 0 10px; } + + .Select { + flex-grow: 1; + } } &-addresses { + overflow-y: scroll; &-table { - width: 100%; + width: 695px; text-align: center; + margin: auto; margin-bottom: 10px; &-token { diff --git a/common/components/WalletDecrypt/components/DeterministicWalletsModal.tsx b/common/components/WalletDecrypt/components/DeterministicWalletsModal.tsx index 769d088a..e1653349 100644 --- a/common/components/WalletDecrypt/components/DeterministicWalletsModal.tsx +++ b/common/components/WalletDecrypt/components/DeterministicWalletsModal.tsx @@ -8,7 +8,7 @@ import { } from 'actions/deterministicWallets'; import Modal, { IButton } from 'components/ui/Modal'; import { AppState } from 'reducers'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { isValidPath } from 'libs/validators'; import React from 'react'; import { connect } from 'react-redux'; @@ -16,6 +16,8 @@ import { getNetworkConfig } from 'selectors/config'; import { getTokens, MergedToken } from 'selectors/wallet'; import { UnitDisplay } from 'components/ui'; import './DeterministicWalletsModal.scss'; +import { DPath } from 'config/dpaths'; +import Select from 'react-select'; const WALLETS_PER_PAGE = 5; @@ -24,7 +26,7 @@ interface Props { isOpen?: boolean; walletType?: string; dPath: string; - dPaths: { label: string; value: string }[]; + dPaths: DPath[]; publicKey?: string; chainCode?: string; seed?: string; @@ -45,6 +47,7 @@ interface Props { } interface State { + currentLabel: string; selectedAddress: string; selectedAddrIndex: number; isCustomPath: boolean; @@ -52,12 +55,18 @@ interface State { page: number; } +const customDPath: DPath = { + label: 'custom', + value: 'custom' +}; + class DeterministicWalletsModalClass extends React.Component { - public state = { + public state: State = { selectedAddress: '', selectedAddrIndex: 0, isCustomPath: false, customPath: '', + currentLabel: '', page: 0 }; @@ -88,7 +97,7 @@ class DeterministicWalletsModalClass extends React.Component { onCancel, walletType } = this.props; - const { selectedAddress, isCustomPath, customPath, page } = this.state; + const { selectedAddress, customPath, page } = this.state; const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid'; const buttons: IButton[] = [ @@ -113,21 +122,20 @@ class DeterministicWalletsModalClass extends React.Component { handleClose={onCancel} >
    + {/* TODO: replace styles for flexbox with flexbox classes in https://github.com/MyEtherWallet/MyEtherWallet/pull/850/files#diff-2150778b9391533fec7b8afd060c7672 */}
    - Addresses for - - {dPaths.map(dp => ( - - ))} - - - {isCustomPath && ( + options={dPaths} + clearable={false} + searchable={false} + /> + {/* TODO/Hack - Custom Paths are temporarily disabled. `false` is used for smallest diff */} + {false && ( { {wallets.map(wallet => this.renderWalletRow(wallet))} - -
    - - -
    +
    +
    + +
    @@ -200,16 +207,19 @@ class DeterministicWalletsModalClass extends React.Component { } } - private handleChangePath = (ev: React.FormEvent) => { - const { value } = ev.currentTarget; + private findDPath = (prop: keyof DPath, cmp: string) => { + return this.props.dPaths.find(d => d[prop] === cmp) || customDPath; + }; + + private handleChangePath = (newPath: DPath) => { + const { value: dPathLabel } = newPath; + const { value } = this.findDPath('value', dPathLabel); if (value === 'custom') { - this.setState({ isCustomPath: true }); + this.setState({ isCustomPath: true, currentLabel: dPathLabel }); } else { - this.setState({ isCustomPath: false }); - if (this.props.dPath !== value) { - this.props.onPathChange(value); - } + this.setState({ isCustomPath: false, currentLabel: dPathLabel }); + this.props.onPathChange(value); } }; diff --git a/common/components/WalletDecrypt/components/LedgerNano.tsx b/common/components/WalletDecrypt/components/LedgerNano.tsx index 7347ac45..01da246f 100644 --- a/common/components/WalletDecrypt/components/LedgerNano.tsx +++ b/common/components/WalletDecrypt/components/LedgerNano.tsx @@ -4,15 +4,22 @@ import translate, { translateRaw } from 'translations'; import DeterministicWalletsModal from './DeterministicWalletsModal'; import { LedgerWallet } from 'libs/wallet'; import ledger from 'ledgerco'; -import DPATHS from 'config/dpaths'; import { Spinner } from 'components/ui'; +import { connect } from 'react-redux'; +import { AppState } from 'reducers'; +import { getNetworkConfig } from 'selectors/config'; +import { SecureWalletName } from 'config'; +import { DPath } from 'config/dpaths'; +import { getPaths, getSingleDPath } from 'utils/network'; -const DEFAULT_PATH = DPATHS.LEDGER[0].value; - -interface Props { +interface OwnProps { onUnlock(param: any): void; } +interface StateProps { + dPath: DPath; +} + interface State { publicKey: string; chainCode: string; @@ -22,11 +29,13 @@ interface State { showTip: boolean; } -export class LedgerNanoSDecrypt extends Component { +type Props = OwnProps & StateProps; + +class LedgerNanoSDecryptClass extends Component { public state: State = { publicKey: '', chainCode: '', - dPath: DEFAULT_PATH, + dPath: this.props.dPath.value, error: null, isLoading: false, showTip: false @@ -114,7 +123,7 @@ export class LedgerNanoSDecrypt extends Component { publicKey={publicKey} chainCode={chainCode} dPath={dPath} - dPaths={DPATHS.LEDGER} + dPaths={getPaths(SecureWalletName.LEDGER_NANO_S)} onCancel={this.handleCancel} onConfirmAddress={this.handleUnlock} onPathChange={this.handlePathChange} @@ -146,11 +155,11 @@ export class LedgerNanoSDecrypt extends Component { }); }) .catch(err => { - if (err.metaData.code === 5) { + if (err.metaData && err.metaData.code === 5) { this.showTip(); } this.setState({ - error: err.metaData.type, + error: err.metaData ? err.metaData.type : err, isLoading: false }); }); @@ -174,7 +183,16 @@ export class LedgerNanoSDecrypt extends Component { this.setState({ publicKey: '', chainCode: '', - dPath: DEFAULT_PATH + dPath: this.props.dPath.value }); } } + +function mapStateToProps(state: AppState): StateProps { + const network = getNetworkConfig(state); + return { + dPath: getSingleDPath(SecureWalletName.LEDGER_NANO_S, network) + }; +} + +export const LedgerNanoSDecrypt = connect(mapStateToProps)(LedgerNanoSDecryptClass); diff --git a/common/components/WalletDecrypt/components/Mnemonic.tsx b/common/components/WalletDecrypt/components/Mnemonic.tsx index b9fd2ca8..fdb5d2b3 100644 --- a/common/components/WalletDecrypt/components/Mnemonic.tsx +++ b/common/components/WalletDecrypt/components/Mnemonic.tsx @@ -1,15 +1,23 @@ import { mnemonicToSeed, validateMnemonic } from 'bip39'; -import DPATHS from 'config/dpaths'; import React, { Component } from 'react'; import translate, { translateRaw } from 'translations'; import DeterministicWalletsModal from './DeterministicWalletsModal'; import { formatMnemonic } from 'utils/formatters'; - -const DEFAULT_PATH = DPATHS.MNEMONIC[0].value; +import { InsecureWalletName } from 'config'; +import { AppState } from 'reducers'; +import { getNetworkConfig } from 'selectors/config'; +import { connect } from 'react-redux'; +import { DPath } from 'config/dpaths'; +import { getPaths, getSingleDPath } from 'utils/network'; interface Props { onUnlock(param: any): void; } + +interface StateProps { + dPath: DPath; +} + interface State { phrase: string; formattedPhrase: string; @@ -18,13 +26,13 @@ interface State { dPath: string; } -export class MnemonicDecrypt extends Component { +class MnemonicDecryptClass extends Component { public state: State = { phrase: '', formattedPhrase: '', pass: '', seed: '', - dPath: DEFAULT_PATH + dPath: this.props.dPath.value }; public render() { @@ -70,7 +78,7 @@ export class MnemonicDecrypt extends Component { isOpen={!!seed} seed={seed} dPath={dPath} - dPaths={DPATHS.MNEMONIC} + dPaths={getPaths(InsecureWalletName.MNEMONIC_PHRASE)} onCancel={this.handleCancel} onConfirmAddress={this.handleUnlock} onPathChange={this.handlePathChange} @@ -135,3 +143,12 @@ export class MnemonicDecrypt extends Component { }); }; } + +function mapStateToProps(state: AppState): StateProps { + const network = getNetworkConfig(state); + return { + dPath: getSingleDPath(InsecureWalletName.MNEMONIC_PHRASE, network) + }; +} + +export const MnemonicDecrypt = connect(mapStateToProps)(MnemonicDecryptClass); diff --git a/common/components/WalletDecrypt/components/Trezor.tsx b/common/components/WalletDecrypt/components/Trezor.tsx index 236acef9..c74528f0 100644 --- a/common/components/WalletDecrypt/components/Trezor.tsx +++ b/common/components/WalletDecrypt/components/Trezor.tsx @@ -1,4 +1,3 @@ -import DPATHS from 'config/dpaths'; import { TrezorWallet, TREZOR_MINIMUM_FIRMWARE } from 'libs/wallet'; import React, { Component } from 'react'; import translate, { translateRaw } from 'translations'; @@ -6,11 +5,23 @@ import TrezorConnect from 'vendor/trezor-connect'; import DeterministicWalletsModal from './DeterministicWalletsModal'; import './Trezor.scss'; import { Spinner } from 'components/ui'; -const DEFAULT_PATH = DPATHS.TREZOR[0].value; +import { getNetworkConfig } from 'selectors/config'; +import { AppState } from 'reducers'; +import { connect } from 'react-redux'; +import { SecureWalletName } from 'config'; +import { DPath } from 'config/dpaths'; +import { getPaths, getSingleDPath } from 'utils/network'; -interface Props { +//todo: conflicts with comment in walletDecrypt -> onUnlock method +interface OwnProps { onUnlock(param: any): void; } + +interface StateProps { + dPath: DPath; +} + +// todo: nearly duplicates ledger component props interface State { publicKey: string; chainCode: string; @@ -19,11 +30,13 @@ interface State { isLoading: boolean; } -export class TrezorDecrypt extends Component { +type Props = OwnProps & StateProps; + +class TrezorDecryptClass extends Component { public state: State = { publicKey: '', chainCode: '', - dPath: DEFAULT_PATH, + dPath: this.props.dPath.value, error: null, isLoading: false }; @@ -76,7 +89,7 @@ export class TrezorDecrypt extends Component { publicKey={publicKey} chainCode={chainCode} dPath={dPath} - dPaths={DPATHS.TREZOR} + dPaths={getPaths(SecureWalletName.TREZOR)} onCancel={this.handleCancel} onConfirmAddress={this.handleUnlock} onPathChange={this.handlePathChange} @@ -133,7 +146,16 @@ export class TrezorDecrypt extends Component { this.setState({ publicKey: '', chainCode: '', - dPath: DEFAULT_PATH + dPath: this.props.dPath.value }); } } + +function mapStateToProps(state: AppState): StateProps { + const network = getNetworkConfig(state); + return { + dPath: getSingleDPath(SecureWalletName.TREZOR, network) + }; +} + +export const TrezorDecrypt = connect(mapStateToProps)(TrezorDecryptClass); diff --git a/common/components/WalletDecrypt/components/ViewOnly.tsx b/common/components/WalletDecrypt/components/ViewOnly.tsx index bd241245..4504c71f 100644 --- a/common/components/WalletDecrypt/components/ViewOnly.tsx +++ b/common/components/WalletDecrypt/components/ViewOnly.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import translate from 'translations'; -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; import { isValidETHAddress } from 'libs/validators'; import { AddressOnlyWallet } from 'libs/wallet'; diff --git a/common/components/WalletDecrypt/components/WalletButton.tsx b/common/components/WalletDecrypt/components/WalletButton.tsx index 6543a446..33b01dee 100644 --- a/common/components/WalletDecrypt/components/WalletButton.tsx +++ b/common/components/WalletDecrypt/components/WalletButton.tsx @@ -1,23 +1,31 @@ import React from 'react'; import classnames from 'classnames'; -import { translateRaw } from 'translations'; +import { translateRaw, TranslateType } from 'translations'; import { NewTabLink, Tooltip } from 'components/ui'; import './WalletButton.scss'; -interface Props { - name: React.ReactElement | string; - description?: React.ReactElement | string; - example?: React.ReactElement | string; - icon?: string | null; - helpLink?: string; - walletType: string; +import { WalletName } from 'config'; + +interface OwnProps { + name: TranslateType; + description?: TranslateType; + example?: TranslateType; + icon?: string; + helpLink: string; + walletType: WalletName; isSecure?: boolean; isReadOnly?: boolean; isDisabled?: boolean; onClick(walletType: string): void; } -export class WalletButton extends React.Component { +interface StateProps { + isFormatDisabled?: boolean; +} + +type Props = OwnProps & StateProps; + +export class WalletButton extends React.PureComponent { public render() { const { name, @@ -45,27 +53,29 @@ export class WalletButton extends React.Component { {icon && } {name} + {description &&
    {description}
    } {example &&
    {example}
    } +
    - {isSecure === true && ( + {isSecure ? ( {translateRaw('This wallet type is secure')} - )} - {isSecure === false && ( + ) : ( {translateRaw('This wallet type is insecure')} )} - {isReadOnly === true && ( + {isReadOnly && ( {translateRaw('You cannot send using address only')} )} + {helpLink && ( @@ -80,14 +90,14 @@ export class WalletButton extends React.Component { } private handleClick = () => { - if (this.props.isDisabled) { + if (this.props.isDisabled || this.props.isFormatDisabled) { return; } this.props.onClick(this.props.walletType); }; - private stopPropogation = (ev: React.SyntheticEvent) => { + private stopPropogation = (ev: React.FormEvent) => { ev.stopPropagation(); }; } diff --git a/common/components/WalletDecrypt/disables.json b/common/components/WalletDecrypt/disables.json deleted file mode 100644 index 947cd2b2..00000000 --- a/common/components/WalletDecrypt/disables.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "READ_ONLY": ["view-only"], - "UNABLE_TO_SIGN": ["trezor", "view-only"], - "ONLINE_ONLY": ["web3", "trezor"] -} diff --git a/common/components/WalletDecrypt/disables.ts b/common/components/WalletDecrypt/disables.ts new file mode 100644 index 00000000..2a7fcbb1 --- /dev/null +++ b/common/components/WalletDecrypt/disables.ts @@ -0,0 +1,15 @@ +import { MiscWalletName, SecureWalletName, WalletName } from 'config'; + +enum WalletMode { + READ_ONLY = 'READ_ONLY', + UNABLE_TO_SIGN = 'UNABLE_TO_SIGN', + ONLINE_ONLY = 'ONLINE_ONLY' +} + +const walletModes: { [key in WalletMode]: WalletName[] } = { + [WalletMode.READ_ONLY]: [MiscWalletName.VIEW_ONLY], + [WalletMode.UNABLE_TO_SIGN]: [SecureWalletName.TREZOR, MiscWalletName.VIEW_ONLY], + [WalletMode.ONLINE_ONLY]: [SecureWalletName.WEB3, SecureWalletName.TREZOR] +}; + +export default walletModes; diff --git a/common/components/WalletDecrypt/index.tsx b/common/components/WalletDecrypt/index.tsx index 9ae77c00..3fc3ad75 100644 --- a/common/components/WalletDecrypt/index.tsx +++ b/common/components/WalletDecrypt/index.tsx @@ -1,3 +1,3 @@ import WalletDecrypt from './WalletDecrypt'; export default WalletDecrypt; -export { default as DISABLE_WALLETS } from './disables.json'; +export { default as DISABLE_WALLETS } from './disables'; diff --git a/common/components/ui/UnlockHeader.tsx b/common/components/ui/UnlockHeader.tsx index df750cde..21762d2c 100644 --- a/common/components/ui/UnlockHeader.tsx +++ b/common/components/ui/UnlockHeader.tsx @@ -1,20 +1,23 @@ import React from 'react'; import { connect } from 'react-redux'; import { AppState } from 'reducers'; -import translate from 'translations'; +import translate, { TranslateType } from 'translations'; import WalletDecrypt from 'components/WalletDecrypt'; import { IWallet } from 'libs/wallet/IWallet'; import './UnlockHeader.scss'; +import { WalletName } from 'config'; interface Props { - title: React.ReactElement | string; + title: TranslateType; wallet: IWallet; - disabledWallets?: string[]; + disabledWallets?: WalletName[]; } + interface State { isExpanded: boolean; } -export class UnlockHeader extends React.Component { + +export class UnlockHeader extends React.PureComponent { public state = { isExpanded: !this.props.wallet }; @@ -57,7 +60,7 @@ export class UnlockHeader extends React.Component { ); } - public toggleisExpanded = () => { + public toggleisExpanded = (_: React.FormEvent) => { this.setState(state => { return { isExpanded: !state.isExpanded }; }); diff --git a/common/config/bity.ts b/common/config/bity.ts index a6a784e2..cdb9ce21 100644 --- a/common/config/bity.ts +++ b/common/config/bity.ts @@ -26,7 +26,7 @@ export function generateKindMax(BTCKINDRate: number, kind: keyof typeof buffers) return kindMax - kindMax * buffers[kind]; } -const info = { +export const bityConfig = { serverURL, bityURL, ETHTxExplorer, @@ -41,5 +41,3 @@ const info = { } } }; - -export default info; diff --git a/common/config/data.ts b/common/config/data.ts index b3f9c891..f975ad6c 100644 --- a/common/config/data.ts +++ b/common/config/data.ts @@ -1,6 +1,7 @@ -import { EtherscanNode, InfuraNode, RPCNode, Web3Node } from 'libs/nodes'; -import { networkIdToName } from 'libs/values'; +import { getValues } from '../utils/helpers'; + export const languages = require('./languages.json'); + // Displays in the header export const VERSION = '4.0.0 (Alpha 0.1.0)'; export const N_FACTOR = 1024; @@ -24,7 +25,7 @@ export const ANNOUNCEMENT_MESSAGE = ` const etherScan = 'https://etherscan.io'; const blockChainInfo = 'https://blockchain.info'; -const ethPlorer = 'https://ethplorer.io'; +export const ethPlorer = 'https://ethplorer.io'; export const ETHTxExplorer = (txHash: string): string => `${etherScan}/tx/${txHash}`; export const BTCTxExplorer = (txHash: string): string => `${blockChainInfo}/tx/${txHash}`; @@ -52,217 +53,32 @@ export const ledgerReferralURL = 'https://www.ledgerwallet.com/r/fa4b?path=/prod export const trezorReferralURL = 'https://trezor.io/?a=myetherwallet.com'; export const bitboxReferralURL = 'https://digitalbitbox.com/?ref=mew'; -export interface BlockExplorerConfig { - name: string; - tx(txHash: string): string; - address(address: string): string; +export enum SecureWalletName { + WEB3 = 'web3', + LEDGER_NANO_S = 'ledgerNanoS', + TREZOR = 'trezor' } -export interface Token { - address: string; - symbol: string; - decimal: number; - error?: string | null; +export enum HardwareWalletName { + LEDGER_NANO_S = 'ledgerNanoS', + TREZOR = 'trezor' } -export interface NetworkContract { - name: string; - address?: string; - abi: string; +export enum InsecureWalletName { + PRIVATE_KEY = 'privateKey', + KEYSTORE_FILE = 'keystoreFile', + MNEMONIC_PHRASE = 'mnemonicPhrase' } -export interface NetworkConfig { - name: string; - unit: string; - color?: string; - blockExplorer?: BlockExplorerConfig; - tokenExplorer?: { - name: string; - address(address: string): string; - }; - chainId: number; - tokens: Token[]; - contracts: NetworkContract[] | null; - isTestnet?: boolean; +export enum MiscWalletName { + VIEW_ONLY = 'viewOnly' } -export interface CustomNetworkConfig { - name: string; - unit: string; - chainId: number; -} +export const walletNames = getValues( + SecureWalletName, + HardwareWalletName, + InsecureWalletName, + MiscWalletName +); -export interface NodeConfig { - network: string; - lib: RPCNode | Web3Node; - service: string; - estimateGas?: boolean; - hidden?: boolean; -} - -export interface CustomNodeConfig { - name: string; - url: string; - port: number; - network: string; - auth?: { - username: string; - password: string; - }; -} - -// Must be a website that follows the ethplorer convention of /tx/[hash] and -// address/[address] to generate the correct functions. -function makeExplorer(url): BlockExplorerConfig { - return { - name: url, - tx: hash => `${url}/tx/${hash}`, - address: address => `${url}/address/${address}` - }; -} - -export const NETWORKS: { [key: string]: NetworkConfig } = { - ETH: { - name: 'ETH', - unit: 'ETH', - chainId: 1, - color: '#0e97c0', - blockExplorer: makeExplorer('https://etherscan.io'), - tokenExplorer: { - name: ethPlorer, - address: ETHTokenExplorer - }, - tokens: require('./tokens/eth.json'), - contracts: require('./contracts/eth.json') - }, - Ropsten: { - name: 'Ropsten', - unit: 'ETH', - chainId: 3, - color: '#adc101', - blockExplorer: makeExplorer('https://ropsten.etherscan.io'), - tokens: require('./tokens/ropsten.json'), - contracts: require('./contracts/ropsten.json'), - isTestnet: true - }, - Kovan: { - name: 'Kovan', - unit: 'ETH', - chainId: 42, - color: '#adc101', - blockExplorer: makeExplorer('https://kovan.etherscan.io'), - tokens: require('./tokens/ropsten.json'), - contracts: require('./contracts/ropsten.json'), - isTestnet: true - }, - Rinkeby: { - name: 'Rinkeby', - unit: 'ETH', - chainId: 4, - color: '#adc101', - blockExplorer: makeExplorer('https://rinkeby.etherscan.io'), - tokens: require('./tokens/rinkeby.json'), - contracts: require('./contracts/rinkeby.json'), - isTestnet: true - } -}; - -export const NODES: { [key: string]: NodeConfig } = { - eth_mew: { - network: 'ETH', - lib: new RPCNode('https://api.myetherapi.com/eth'), - service: 'MyEtherWallet', - estimateGas: true - }, - eth_ethscan: { - network: 'ETH', - service: 'Etherscan.io', - lib: new EtherscanNode('https://api.etherscan.io/api'), - estimateGas: false - }, - eth_infura: { - network: 'ETH', - service: 'infura.io', - lib: new InfuraNode('https://mainnet.infura.io/mew'), - estimateGas: false - }, - rop_mew: { - network: 'Ropsten', - service: 'MyEtherWallet', - lib: new RPCNode('https://api.myetherapi.com/rop'), - estimateGas: false - }, - rop_infura: { - network: 'Ropsten', - service: 'infura.io', - lib: new InfuraNode('https://ropsten.infura.io/mew'), - estimateGas: false - }, - kov_ethscan: { - network: 'Kovan', - service: 'Etherscan.io', - lib: new EtherscanNode('https://kovan.etherscan.io/api'), - estimateGas: false - }, - rin_ethscan: { - network: 'Rinkeby', - service: 'Etherscan.io', - lib: new EtherscanNode('https://rinkeby.etherscan.io/api'), - estimateGas: false - }, - rin_infura: { - network: 'Rinkeby', - service: 'infura.io', - lib: new InfuraNode('https://rinkeby.infura.io/mew'), - estimateGas: false - } -}; - -interface Web3NodeInfo { - networkId: string; - lib: Web3Node; -} - -export async function setupWeb3Node(): Promise { - const { web3 } = window as any; - - if (!web3 || !web3.currentProvider || !web3.currentProvider.sendAsync) { - throw new Error( - 'Web3 not found. Please check that MetaMask is installed, or that MyEtherWallet is open in Mist.' - ); - } - - const lib = new Web3Node(); - const networkId = await lib.getNetVersion(); - const accounts = await lib.getAccounts(); - - if (!accounts.length) { - throw new Error('No accounts found in MetaMask / Mist.'); - } - - if (networkId === 'loading') { - throw new Error('MetaMask / Mist is still loading. Please refresh the page and try again.'); - } - - return { networkId, lib }; -} - -export async function isWeb3NodeAvailable(): Promise { - try { - await setupWeb3Node(); - return true; - } catch (e) { - return false; - } -} - -export async function initWeb3Node(): Promise { - const { networkId, lib } = await setupWeb3Node(); - NODES.web3 = { - network: networkIdToName(networkId), - service: 'MetaMask / Mist', - lib, - estimateGas: false, - hidden: true - }; -} +export type WalletName = SecureWalletName | InsecureWalletName | MiscWalletName; diff --git a/common/config/dpaths.ts b/common/config/dpaths.ts index 14243fcd..c162025b 100644 --- a/common/config/dpaths.ts +++ b/common/config/dpaths.ts @@ -1,46 +1,52 @@ -const ETH_DEFAULT = { +export interface DPath { + label: string; + value: string; // TODO determine method for more precise typing for path +} + +export const ETH_DEFAULT: DPath = { label: 'Default (ETH)', value: "m/44'/60'/0'/0" }; -const ETH_TREZOR = { +export const ETH_TREZOR: DPath = { label: 'TREZOR (ETH)', value: "m/44'/60'/0'/0" }; -const ETH_LEDGER = { +export const ETH_LEDGER: DPath = { label: 'Ledger (ETH)', value: "m/44'/60'/0'" }; -const ETC_LEDGER = { +export const ETC_LEDGER: DPath = { label: 'Ledger (ETC)', value: "m/44'/60'/160720'/0'" }; -const ETC_TREZOR = { +export const ETC_TREZOR: DPath = { label: 'TREZOR (ETC)', value: "m/44'/61'/0'/0" }; -const TESTNET = { - label: 'Testnet', +export const ETH_TESTNET: DPath = { + label: 'Testnet (ETH)', value: "m/44'/1'/0'/0" }; -const EXPANSE = { - label: 'Expanse', +export const EXP_DEFAULT: DPath = { + label: 'Default (EXP)', value: "m/44'/40'/0'/0" }; -const TREZOR = [ETH_TREZOR, ETC_TREZOR, TESTNET]; - -const MNEMONIC = [ETH_DEFAULT, ETH_LEDGER, ETC_LEDGER, ETC_TREZOR, TESTNET, EXPANSE]; - -const LEDGER = [ETH_LEDGER, ETC_LEDGER, TESTNET]; - -export default { - TREZOR, - MNEMONIC, - LEDGER +export const UBQ_DEFAULT: DPath = { + label: 'Default (UBQ)', + value: "m/44'/108'/0'/0" }; + +export const ETH_SINGULAR: DPath = { + label: 'SingularDTV', + value: "m/0'/0'/0'" +}; + +// PATHS TO BE INCLUDED REGARDLESS OF WALLET FORMAT +export const EXTRA_PATHS = [ETH_SINGULAR]; diff --git a/common/config/index.ts b/common/config/index.ts new file mode 100644 index 00000000..d64aae8e --- /dev/null +++ b/common/config/index.ts @@ -0,0 +1,3 @@ +export * from './networks'; +export * from './data'; +export * from './bity'; diff --git a/common/config/networks.ts b/common/config/networks.ts new file mode 100644 index 00000000..8b25bffe --- /dev/null +++ b/common/config/networks.ts @@ -0,0 +1,364 @@ +import { ethPlorer, ETHTokenExplorer, SecureWalletName, InsecureWalletName } from './data'; +import { EtherscanNode, InfuraNode, RPCNode, Web3Node } from 'libs/nodes'; +import { networkIdToName } from 'libs/values'; +import { + ETH_DEFAULT, + ETH_TREZOR, + ETH_LEDGER, + ETC_LEDGER, + ETC_TREZOR, + ETH_TESTNET, + EXP_DEFAULT, + UBQ_DEFAULT, + DPath +} from 'config/dpaths'; + +export interface BlockExplorerConfig { + origin: string; + txUrl(txHash: string): string; + addressUrl(address: string): string; +} + +export interface Token { + address: string; + symbol: string; + decimal: number; + error?: string | null; +} + +export interface NetworkContract { + name: NetworkKeys; + address?: string; + abi: string; +} + +export interface DPathFormats { + trezor: DPath; + ledgerNanoS: DPath; + mnemonicPhrase: DPath; +} + +export interface NetworkConfig { + // TODO really try not to allow strings due to custom networks + name: NetworkKeys; + unit: string; + color?: string; + blockExplorer?: BlockExplorerConfig; + tokenExplorer?: { + name: string; + address(address: string): string; + }; + chainId: number; + tokens: Token[]; + contracts: NetworkContract[] | null; + dPathFormats: DPathFormats; + isTestnet?: boolean; +} + +export interface CustomNetworkConfig { + name: string; + unit: string; + chainId: number; + dPathFormats: DPathFormats | null; +} + +export interface NodeConfig { + network: NetworkKeys; + lib: RPCNode | Web3Node; + service: string; + estimateGas?: boolean; + hidden?: boolean; +} + +export interface CustomNodeConfig { + name: string; + url: string; + port: number; + network: string; + auth?: { + username: string; + password: string; + }; +} + +// Must be a website that follows the ethplorer convention of /tx/[hash] and +// address/[address] to generate the correct functions. +function makeExplorer(origin: string): BlockExplorerConfig { + return { + origin, + txUrl: hash => `${origin}/tx/${hash}`, + addressUrl: address => `${origin}/address/${address}` + }; +} + +const ETH: NetworkConfig = { + name: 'ETH', + unit: 'ETH', + chainId: 1, + color: '#0e97c0', + blockExplorer: makeExplorer('https://etherscan.io'), + tokenExplorer: { + name: ethPlorer, + address: ETHTokenExplorer + }, + tokens: require('./tokens/eth.json'), + contracts: require('./contracts/eth.json'), + dPathFormats: { + [SecureWalletName.TREZOR]: ETH_TREZOR, + [SecureWalletName.LEDGER_NANO_S]: ETH_LEDGER, + [InsecureWalletName.MNEMONIC_PHRASE]: ETH_DEFAULT + } +}; + +const Ropsten: NetworkConfig = { + name: 'Ropsten', + unit: 'ETH', + chainId: 3, + color: '#adc101', + blockExplorer: makeExplorer('https://ropsten.etherscan.io'), + tokens: require('./tokens/ropsten.json'), + contracts: require('./contracts/ropsten.json'), + isTestnet: true, + dPathFormats: { + [SecureWalletName.TREZOR]: ETH_TESTNET, + [SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET, + [InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET + } +}; + +const Kovan: NetworkConfig = { + name: 'Kovan', + unit: 'ETH', + chainId: 42, + color: '#adc101', + blockExplorer: makeExplorer('https://kovan.etherscan.io'), + tokens: require('./tokens/ropsten.json'), + contracts: require('./contracts/ropsten.json'), + isTestnet: true, + dPathFormats: { + [SecureWalletName.TREZOR]: ETH_TESTNET, + [SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET, + [InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET + } +}; + +const Rinkeby: NetworkConfig = { + name: 'Rinkeby', + unit: 'ETH', + chainId: 4, + color: '#adc101', + blockExplorer: makeExplorer('https://rinkeby.etherscan.io'), + tokens: require('./tokens/rinkeby.json'), + contracts: require('./contracts/rinkeby.json'), + isTestnet: true, + dPathFormats: { + [SecureWalletName.TREZOR]: ETH_TESTNET, + [SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET, + [InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET + } +}; + +const ETC: NetworkConfig = { + name: 'ETC', + unit: 'ETC', + chainId: 61, + color: '#669073', + blockExplorer: makeExplorer('https://gastracker.io'), + tokens: require('./tokens/etc.json'), + contracts: require('./contracts/etc.json'), + dPathFormats: { + [SecureWalletName.TREZOR]: ETC_TREZOR, + [SecureWalletName.LEDGER_NANO_S]: ETC_LEDGER, + [InsecureWalletName.MNEMONIC_PHRASE]: ETC_TREZOR + } +}; + +const UBQ: NetworkConfig = { + name: 'UBQ', + unit: 'UBQ', + chainId: 8, + color: '#b37aff', + blockExplorer: makeExplorer('https://ubiqscan.io/en'), + tokens: require('./tokens/ubq.json'), + contracts: require('./contracts/ubq.json'), + dPathFormats: { + [SecureWalletName.TREZOR]: UBQ_DEFAULT, + [SecureWalletName.LEDGER_NANO_S]: UBQ_DEFAULT, + [InsecureWalletName.MNEMONIC_PHRASE]: UBQ_DEFAULT + } +}; + +const EXP: NetworkConfig = { + name: 'EXP', + unit: 'EXP', + chainId: 2, + color: '#673ab7', + blockExplorer: makeExplorer('http://www.gander.tech'), + tokens: require('./tokens/exp.json'), + contracts: require('./contracts/exp.json'), + dPathFormats: { + [SecureWalletName.TREZOR]: EXP_DEFAULT, + [SecureWalletName.LEDGER_NANO_S]: EXP_DEFAULT, + [InsecureWalletName.MNEMONIC_PHRASE]: EXP_DEFAULT + } +}; + +export const NETWORKS = { + ETH, + Ropsten, + Kovan, + Rinkeby, + ETC, + UBQ, + EXP +}; + +export type NetworkKeys = keyof typeof NETWORKS; + +enum NodeName { + ETH_MEW = 'eth_mew', + ETH_ETHSCAN = 'eth_ethscan', + ETH_INFURA = 'eth_infura', + ROP_MEW = 'rop_mew', + ROP_INFURA = 'rop_infura', + KOV_ETHSCAN = 'kov_ethscan', + RIN_ETHSCAN = 'rin_ethscan', + RIN_INFURA = 'rin_infura', + ETC_EPOOL = 'etc_epool', + UBQ = 'ubq', + EXP_TECH = 'exp_tech' +} + +type NonWeb3NodeConfigs = { [key in NodeName]: NodeConfig }; + +interface Web3NodeConfig { + web3?: NodeConfig; +} + +type NodeConfigs = NonWeb3NodeConfigs & Web3NodeConfig; + +export const NODES: NodeConfigs = { + eth_mew: { + network: 'ETH', + lib: new RPCNode('https://api.myetherapi.com/eth'), + service: 'MyEtherWallet', + estimateGas: true + }, + eth_ethscan: { + network: 'ETH', + service: 'Etherscan.io', + lib: new EtherscanNode('https://api.etherscan.io/api'), + estimateGas: false + }, + eth_infura: { + network: 'ETH', + service: 'infura.io', + lib: new InfuraNode('https://mainnet.infura.io/mew'), + estimateGas: false + }, + rop_mew: { + network: 'Ropsten', + service: 'MyEtherWallet', + lib: new RPCNode('https://api.myetherapi.com/rop'), + estimateGas: false + }, + rop_infura: { + network: 'Ropsten', + service: 'infura.io', + lib: new InfuraNode('https://ropsten.infura.io/mew'), + estimateGas: false + }, + kov_ethscan: { + network: 'Kovan', + service: 'Etherscan.io', + lib: new EtherscanNode('https://kovan.etherscan.io/api'), + estimateGas: false + }, + rin_ethscan: { + network: 'Rinkeby', + service: 'Etherscan.io', + lib: new EtherscanNode('https://rinkeby.etherscan.io/api'), + estimateGas: false + }, + rin_infura: { + network: 'Rinkeby', + service: 'infura.io', + lib: new InfuraNode('https://rinkeby.infura.io/mew'), + estimateGas: false + }, + etc_epool: { + network: 'ETC', + service: 'Epool.io', + lib: new RPCNode('https://mewapi.epool.io'), + estimateGas: false + }, + ubq: { + network: 'UBQ', + service: 'ubiqscan.io', + lib: new RPCNode('https://pyrus2.ubiqscan.io'), + estimateGas: true + }, + exp_tech: { + network: 'EXP', + service: 'Expanse.tech', + lib: new RPCNode('https://node.expanse.tech/'), + estimateGas: true + } +}; + +interface Web3NodeInfo { + networkId: string; + lib: Web3Node; +} + +export async function setupWeb3Node(): Promise { + const { web3 } = window as any; + + if (!web3 || !web3.currentProvider || !web3.currentProvider.sendAsync) { + throw new Error( + 'Web3 not found. Please check that MetaMask is installed, or that MyEtherWallet is open in Mist.' + ); + } + + const lib = new Web3Node(); + const networkId = await lib.getNetVersion(); + const accounts = await lib.getAccounts(); + + if (!accounts.length) { + throw new Error('No accounts found in MetaMask / Mist.'); + } + + if (networkId === 'loading') { + throw new Error('MetaMask / Mist is still loading. Please refresh the page and try again.'); + } + + return { networkId, lib }; +} + +export async function isWeb3NodeAvailable(): Promise { + try { + await setupWeb3Node(); + return true; + } catch (e) { + return false; + } +} + +export const Web3Service = 'MetaMask / Mist'; + +export interface NodeConfigOverride extends NodeConfig { + network: any; +} + +export async function initWeb3Node(): Promise { + const { networkId, lib } = await setupWeb3Node(); + const web3: NodeConfigOverride = { + network: networkIdToName(networkId), + service: Web3Service, + lib, + estimateGas: false, + hidden: true + }; + + NODES.web3 = web3; +} diff --git a/common/containers/Tabs/Contracts/components/Interact/components/InteractForm/index.tsx b/common/containers/Tabs/Contracts/components/Interact/components/InteractForm/index.tsx index 65723922..d1759c38 100644 --- a/common/containers/Tabs/Contracts/components/Interact/components/InteractForm/index.tsx +++ b/common/containers/Tabs/Contracts/components/Interact/components/InteractForm/index.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import translate from 'translations'; -import { NetworkContract } from 'config/data'; +import { NetworkContract } from 'config'; import { getNetworkContracts } from 'selectors/config'; import { connect } from 'react-redux'; import { AppState } from 'reducers'; diff --git a/common/containers/Tabs/ENS/components/GeneralInfoPanel/index.tsx b/common/containers/Tabs/ENS/components/GeneralInfoPanel/index.tsx index a446ede5..e45614ea 100644 --- a/common/containers/Tabs/ENS/components/GeneralInfoPanel/index.tsx +++ b/common/containers/Tabs/ENS/components/GeneralInfoPanel/index.tsx @@ -1,7 +1,7 @@ import { NewTabLink } from 'components/ui'; import React from 'react'; import GeneralInfoNode from './GeneralInfoNode'; -import { knowledgeBaseURL } from 'config/data'; +import { knowledgeBaseURL } from 'config'; import { InfoNode } from './types'; const generalInfoNodes: InfoNode[] = [ diff --git a/common/containers/Tabs/GenerateWallet/components/Keystore/DownloadWallet.tsx b/common/containers/Tabs/GenerateWallet/components/Keystore/DownloadWallet.tsx index 8a88e572..ac72b46c 100644 --- a/common/containers/Tabs/GenerateWallet/components/Keystore/DownloadWallet.tsx +++ b/common/containers/Tabs/GenerateWallet/components/Keystore/DownloadWallet.tsx @@ -5,7 +5,7 @@ import translate from 'translations'; import { makeBlob } from 'utils/blob'; import './DownloadWallet.scss'; import Template from '../Template'; -import { N_FACTOR } from 'config/data'; +import { N_FACTOR } from 'config'; interface Props { wallet: IFullWallet; diff --git a/common/containers/Tabs/GenerateWallet/components/Keystore/EnterPassword.tsx b/common/containers/Tabs/GenerateWallet/components/Keystore/EnterPassword.tsx index dc485212..ae537ee3 100644 --- a/common/containers/Tabs/GenerateWallet/components/Keystore/EnterPassword.tsx +++ b/common/containers/Tabs/GenerateWallet/components/Keystore/EnterPassword.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import translate from 'translations'; -import { MINIMUM_PASSWORD_LENGTH } from 'config/data'; +import { MINIMUM_PASSWORD_LENGTH } from 'config'; import './EnterPassword.scss'; import PasswordInput from './PasswordInput'; import Template from '../Template'; diff --git a/common/containers/Tabs/Help/index.tsx b/common/containers/Tabs/Help/index.tsx index 6bba17dc..2ba8601a 100644 --- a/common/containers/Tabs/Help/index.tsx +++ b/common/containers/Tabs/Help/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import translate from 'translations'; import TabSection from 'containers/TabSection'; -import { knowledgeBaseURL } from 'config/data'; +import { knowledgeBaseURL } from 'config'; const Help = () => ( diff --git a/common/containers/Tabs/RestoreKeystore/components/KeystoreDetails.tsx b/common/containers/Tabs/RestoreKeystore/components/KeystoreDetails.tsx index e804f589..a2ab64af 100644 --- a/common/containers/Tabs/RestoreKeystore/components/KeystoreDetails.tsx +++ b/common/containers/Tabs/RestoreKeystore/components/KeystoreDetails.tsx @@ -7,7 +7,7 @@ import { isValidPrivKey } from 'libs/validators'; import { stripHexPrefix } from 'libs/values'; import translate from 'translations'; import './KeystoreDetails.scss'; -import { N_FACTOR } from 'config/data'; +import { N_FACTOR } from 'config'; interface State { secretKey: string; diff --git a/common/containers/Tabs/SendTransaction/components/Donate.tsx b/common/containers/Tabs/SendTransaction/components/Donate.tsx index 6effd42f..9ccdb8ae 100644 --- a/common/containers/Tabs/SendTransaction/components/Donate.tsx +++ b/common/containers/Tabs/SendTransaction/components/Donate.tsx @@ -1,4 +1,4 @@ -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; import React from 'react'; import translate from 'translations'; diff --git a/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx b/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx index dfd6b7a7..ce83de5f 100644 --- a/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx +++ b/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx @@ -12,7 +12,7 @@ import { ICurrentValue } from 'selectors/transaction/current'; import BN from 'bn.js'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { validNumber, validDecimal } from 'libs/validators'; import { getGasLimit } from 'selectors/transaction'; import { AddressField, AmountField, GasLimitField } from 'components'; diff --git a/common/containers/Tabs/SendTransaction/index.tsx b/common/containers/Tabs/SendTransaction/index.tsx index d75d8e28..1ce0b928 100644 --- a/common/containers/Tabs/SendTransaction/index.tsx +++ b/common/containers/Tabs/SendTransaction/index.tsx @@ -3,24 +3,28 @@ import { connect } from 'react-redux'; import translate from 'translations'; import TabSection from 'containers/TabSection'; import { UnlockHeader } from 'components/ui'; -import { SideBar } from './components/index'; +import { SideBar } from './components'; import { IReadOnlyWallet, IFullWallet } from 'libs/wallet'; import { getWalletInst } from 'selectors/wallet'; import { AppState } from 'reducers'; import tabs from './tabs'; import SubTabs, { Props as TabProps } from 'components/SubTabs'; import { RouteComponentProps } from 'react-router'; +import { NetworkConfig } from 'config'; +import { getNetworkConfig } from 'selectors/config'; + +export type WalletTypes = IReadOnlyWallet | IFullWallet | undefined | null; interface StateProps { wallet: AppState['wallet']['inst']; + network: AppState['config']['network']; } export interface SubTabProps { wallet: WalletTypes; + network: NetworkConfig; } -export type WalletTypes = IReadOnlyWallet | IFullWallet | undefined | null; - type Props = StateProps & RouteComponentProps<{}>; const determineActiveTab = (wallet: WalletTypes, activeTab: string) => { @@ -33,7 +37,7 @@ const determineActiveTab = (wallet: WalletTypes, activeTab: string) => { class SendTransaction extends React.Component { public render() { - const { wallet, location } = this.props; + const { wallet, location, network } = this.props; const activeTab = location.pathname.split('/')[2]; const tabProps: TabProps = { @@ -41,7 +45,7 @@ class SendTransaction extends React.Component { activeTab: determineActiveTab(wallet, activeTab), sideBar: , tabs, - subTabProps: { wallet } + subTabProps: { wallet, network } }; interface IWalletTabs { @@ -61,4 +65,7 @@ class SendTransaction extends React.Component { } } -export default connect((state: AppState) => ({ wallet: getWalletInst(state) }))(SendTransaction); +export default connect((state: AppState) => ({ + wallet: getWalletInst(state), + network: getNetworkConfig(state) +}))(SendTransaction); diff --git a/common/containers/Tabs/SendTransaction/tabs.tsx b/common/containers/Tabs/SendTransaction/tabs.tsx index 63346a1d..a38090e6 100644 --- a/common/containers/Tabs/SendTransaction/tabs.tsx +++ b/common/containers/Tabs/SendTransaction/tabs.tsx @@ -5,6 +5,7 @@ import translate from 'translations'; import { Fields, UnavailableWallets, WalletInfo, RequestPayment } from './components/index'; import { Tab } from 'components/SubTabs'; import { SubTabProps } from 'containers/Tabs/SendTransaction'; +import { isNetworkUnit } from 'utils/network'; const SendTab: Tab = { path: 'send', @@ -41,6 +42,10 @@ const InfoTab: Tab = { const RequestTab: Tab = { path: 'request', name: translate('Request Payment'), + isDisabled: (props: SubTabProps) => { + const isETHNetwork = isNetworkUnit(props.network, 'ETH'); + return !isETHNetwork; + }, render(props: SubTabProps) { const content = props && props.wallet ? : null; return
    {content}
    ; diff --git a/common/containers/Tabs/Swap/components/CurrencySwap.tsx b/common/containers/Tabs/Swap/components/CurrencySwap.tsx index 368f4a25..8624d94e 100644 --- a/common/containers/Tabs/Swap/components/CurrencySwap.tsx +++ b/common/containers/Tabs/Swap/components/CurrencySwap.tsx @@ -6,7 +6,7 @@ import { SwapInput } from 'reducers/swap/types'; import SimpleButton from 'components/ui/SimpleButton'; -import bityConfig, { generateKindMax, generateKindMin, WhitelistedCoins } from 'config/bity'; +import { generateKindMax, generateKindMin, WhitelistedCoins, bityConfig } from 'config/bity'; import React, { Component } from 'react'; import translate from 'translations'; import { combineAndUpper } from 'utils/formatters'; diff --git a/common/containers/Tabs/Swap/components/CurrentRates.tsx b/common/containers/Tabs/Swap/components/CurrentRates.tsx index 58ca0540..8a68ffd8 100644 --- a/common/containers/Tabs/Swap/components/CurrentRates.tsx +++ b/common/containers/Tabs/Swap/components/CurrentRates.tsx @@ -6,7 +6,7 @@ import { import bityLogoWhite from 'assets/images/logo-bity-white.svg'; import shapeshiftLogoWhite from 'assets/images/logo-shapeshift.svg'; import Spinner from 'components/ui/Spinner'; -import { bityReferralURL, shapeshiftReferralURL } from 'config/data'; +import { bityReferralURL, shapeshiftReferralURL } from 'config'; import React, { Component } from 'react'; import translate from 'translations'; import './CurrentRates.scss'; diff --git a/common/containers/Tabs/Swap/components/LiteSend/LiteSend.tsx b/common/containers/Tabs/Swap/components/LiteSend/LiteSend.tsx index 0820ce6e..08491a93 100644 --- a/common/containers/Tabs/Swap/components/LiteSend/LiteSend.tsx +++ b/common/containers/Tabs/Swap/components/LiteSend/LiteSend.tsx @@ -8,7 +8,7 @@ import { configureLiteSend, TConfigureLiteSend } from 'actions/swap'; import { connect } from 'react-redux'; import { AppState } from 'reducers'; import { shouldDisplayLiteSend } from 'selectors/swap'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; interface DispatchProps { configureLiteSend: TConfigureLiteSend; @@ -36,10 +36,7 @@ class LiteSendClass extends Component { renderMe = (
    -
    - WARNING: You are currently not on the Ethereum Mainnet. Please switch nodes in order - for the token swap to function as intended. -
    +
    Note: Send is only supported on Ethereum Mainnet.
    ); diff --git a/common/containers/Tabs/Swap/components/ReceivingAddress.tsx b/common/containers/Tabs/Swap/components/ReceivingAddress.tsx index 302c6587..3c7c7c26 100644 --- a/common/containers/Tabs/Swap/components/ReceivingAddress.tsx +++ b/common/containers/Tabs/Swap/components/ReceivingAddress.tsx @@ -8,7 +8,7 @@ import { import { SwapInput } from 'reducers/swap/types'; import classnames from 'classnames'; import SimpleButton from 'components/ui/SimpleButton'; -import { donationAddressMap } from 'config/data'; +import { donationAddressMap } from 'config'; import { isValidBTCAddress, isValidETHAddress } from 'libs/validators'; import React, { Component } from 'react'; import translate from 'translations'; diff --git a/common/containers/Tabs/Swap/components/SwapInfoHeaderTitle.tsx b/common/containers/Tabs/Swap/components/SwapInfoHeaderTitle.tsx index 27e86ed8..4f325c39 100644 --- a/common/containers/Tabs/Swap/components/SwapInfoHeaderTitle.tsx +++ b/common/containers/Tabs/Swap/components/SwapInfoHeaderTitle.tsx @@ -1,7 +1,7 @@ import { RestartSwapAction } from 'actions/swap'; import bityLogo from 'assets/images/logo-bity.svg'; import shapeshiftLogo from 'assets/images/shapeshift-dark.svg'; -import { bityReferralURL } from 'config/data'; +import { bityReferralURL } from 'config'; import React, { Component } from 'react'; import translate from 'translations'; import './SwapInfoHeader.scss'; diff --git a/common/containers/Tabs/Swap/components/SwapProgress.tsx b/common/containers/Tabs/Swap/components/SwapProgress.tsx index a84789ab..4fcf4abb 100644 --- a/common/containers/Tabs/Swap/components/SwapProgress.tsx +++ b/common/containers/Tabs/Swap/components/SwapProgress.tsx @@ -1,5 +1,5 @@ import { TShowNotification } from 'actions/notifications'; -import bityConfig from 'config/bity'; +import { bityConfig } from 'config/bity'; import React, { Component } from 'react'; import translate, { translateRaw } from 'translations'; import './SwapProgress.scss'; diff --git a/common/libs/nodes/INode.ts b/common/libs/nodes/INode.ts index 1edf0d90..2d29b87d 100644 --- a/common/libs/nodes/INode.ts +++ b/common/libs/nodes/INode.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; import { Wei, TokenValue } from 'libs/units'; import { IHexStrTransaction } from 'libs/transaction'; diff --git a/common/libs/nodes/custom/index.ts b/common/libs/nodes/custom/index.ts index 7d5a8e48..91f9d9ca 100644 --- a/common/libs/nodes/custom/index.ts +++ b/common/libs/nodes/custom/index.ts @@ -1,6 +1,6 @@ import RPCNode from '../rpc'; import RPCClient from '../rpc/client'; -import { CustomNodeConfig } from 'config/data'; +import { CustomNodeConfig } from 'config'; export default class CustomNode extends RPCNode { constructor(config: CustomNodeConfig) { diff --git a/common/libs/nodes/etherscan/requests.ts b/common/libs/nodes/etherscan/requests.ts index c87107fa..96e0a1d0 100644 --- a/common/libs/nodes/etherscan/requests.ts +++ b/common/libs/nodes/etherscan/requests.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; import ERC20 from 'libs/erc20'; import RPCRequests from '../rpc/requests'; import { diff --git a/common/libs/nodes/rpc/index.ts b/common/libs/nodes/rpc/index.ts index ac4eb764..8bb52f68 100644 --- a/common/libs/nodes/rpc/index.ts +++ b/common/libs/nodes/rpc/index.ts @@ -1,5 +1,5 @@ import BN from 'bn.js'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { IHexStrTransaction } from 'libs/transaction'; import { Wei, TokenValue } from 'libs/units'; import { stripHexPrefix } from 'libs/values'; diff --git a/common/libs/nodes/rpc/requests.ts b/common/libs/nodes/rpc/requests.ts index 4422bd34..3db0bb17 100644 --- a/common/libs/nodes/rpc/requests.ts +++ b/common/libs/nodes/rpc/requests.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; import ERC20 from 'libs/erc20'; import { CallRequest, diff --git a/common/libs/values.ts b/common/libs/values.ts index bc665d0f..d8e791b2 100644 --- a/common/libs/values.ts +++ b/common/libs/values.ts @@ -1,6 +1,7 @@ import { Wei, toTokenBase } from 'libs/units'; import { addHexPrefix } from 'ethereumjs-util'; import BN from 'bn.js'; +import { NetworkKeys } from 'config'; export function stripHexPrefix(value: string) { return value.replace('0x', ''); @@ -23,7 +24,7 @@ export function sanitizeHex(hex: string) { return hex !== '' ? `0x${padLeftEven(hexStr)}` : ''; } -export function networkIdToName(networkId: string | number): string { +export function networkIdToName(networkId: string | number): NetworkKeys { switch (networkId.toString()) { case '1': return 'ETH'; diff --git a/common/reducers/config.ts b/common/reducers/config.ts index a03a8dda..bf73c880 100644 --- a/common/reducers/config.ts +++ b/common/reducers/config.ts @@ -16,7 +16,7 @@ import { CustomNodeConfig, NetworkConfig, CustomNetworkConfig -} from '../config/data'; +} from 'config'; import { makeCustomNodeId } from 'utils/node'; import { makeCustomNetworkId } from 'utils/network'; diff --git a/common/reducers/customTokens.ts b/common/reducers/customTokens.ts index 137758dd..3ebca005 100644 --- a/common/reducers/customTokens.ts +++ b/common/reducers/customTokens.ts @@ -4,7 +4,7 @@ import { RemoveCustomTokenAction } from 'actions/customTokens'; import { TypeKeys } from 'actions/customTokens/constants'; -import { Token } from 'config/data'; +import { Token } from 'config'; export type State = Token[]; diff --git a/common/reducers/swap/types.ts b/common/reducers/swap/types.ts index 363988bf..863304e5 100644 --- a/common/reducers/swap/types.ts +++ b/common/reducers/swap/types.ts @@ -1,5 +1,5 @@ import { Option } from 'actions/swap/actionTypes'; -import { WhitelistedCoins } from 'config/bity'; +import { WhitelistedCoins } from 'config'; export interface SwapInput { id: WhitelistedCoins; diff --git a/common/sagas/config.ts b/common/sagas/config.ts index ba72d093..db184fce 100644 --- a/common/sagas/config.ts +++ b/common/sagas/config.ts @@ -10,7 +10,14 @@ import { select, race } from 'redux-saga/effects'; -import { NODES, NETWORKS, NodeConfig, CustomNodeConfig, CustomNetworkConfig } from 'config/data'; +import { + NODES, + NETWORKS, + NodeConfig, + CustomNodeConfig, + CustomNetworkConfig, + Web3Service +} from 'config'; import { makeCustomNodeId, getCustomNodeConfigFromId, @@ -37,12 +44,9 @@ import { } from 'actions/config'; import { showNotification } from 'actions/notifications'; import { translateRaw } from 'translations'; -import { IWallet, Web3Wallet } from 'libs/wallet'; -import { getWalletInst } from 'selectors/wallet'; +import { Web3Wallet } from 'libs/wallet'; import { TypeKeys as WalletTypeKeys } from 'actions/wallet/constants'; import { State as ConfigState, INITIAL_STATE as configInitialState } from 'reducers/config'; -import { resetWallet } from 'actions/wallet'; -import { reset as resetTransaction } from 'actions/transaction'; export const getConfig = (state: AppState): ConfigState => state.config; @@ -171,12 +175,18 @@ export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIte yield put(setLatestBlock(latestBlock)); yield put(changeNode(action.payload, actionConfig, actionNetwork)); - const currentWallet: IWallet | null = yield select(getWalletInst); + // TODO - re-enable once DeterministicWallet state is fixed to flush properly. + // DeterministicWallet keeps path related state we need to flush before we can stop reloading + // const currentWallet: IWallet | null = yield select(getWalletInst); // if there's no wallet, do not reload as there's no component state to resync - if (currentWallet && currentConfig.network !== actionConfig.network) { - yield put(resetWallet()); - yield put(resetTransaction()); + // if (currentWallet && currentConfig.network !== actionConfig.network) { + + const isNewNetwork = currentConfig.network !== actionConfig.network; + const newIsWeb3 = actionConfig.service === Web3Service; + // don't reload when web3 is selected; node will automatically re-set and state is not an issue here + if (isNewNetwork && !newIsWeb3) { + yield call(reload); } } diff --git a/common/sagas/deterministicWallets.ts b/common/sagas/deterministicWallets.ts index afb9f990..a6818c6f 100644 --- a/common/sagas/deterministicWallets.ts +++ b/common/sagas/deterministicWallets.ts @@ -5,7 +5,7 @@ import { updateDeterministicWallet } from 'actions/deterministicWallets'; import { showNotification } from 'actions/notifications'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { publicToAddress, toChecksumAddress } from 'ethereumjs-util'; import HDKey from 'hdkey'; import { INode } from 'libs/nodes/INode'; diff --git a/common/sagas/transaction/signing/helpers.ts b/common/sagas/transaction/signing/helpers.ts index 82adb63b..a13de4ec 100644 --- a/common/sagas/transaction/signing/helpers.ts +++ b/common/sagas/transaction/signing/helpers.ts @@ -12,7 +12,7 @@ import { TypeKeys as TK } from 'actions/transaction'; import Tx from 'ethereumjs-tx'; -import { NetworkConfig } from 'config/data'; +import { NetworkConfig } from 'config'; import { SagaIterator } from 'redux-saga'; import { showNotification } from 'actions/notifications'; diff --git a/common/sagas/wallet/helpers.ts b/common/sagas/wallet/helpers.ts index 55684542..65044417 100644 --- a/common/sagas/wallet/helpers.ts +++ b/common/sagas/wallet/helpers.ts @@ -1,6 +1,6 @@ import { apply, select, call } from 'redux-saga/effects'; import { AppState } from 'reducers'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { INode } from 'libs/nodes/INode'; import { IWallet, WalletConfig } from 'libs/wallet'; import { TokenBalance } from 'selectors/wallet'; diff --git a/common/sagas/wallet/wallet.ts b/common/sagas/wallet/wallet.ts index 44fbe114..62c2dfa9 100644 --- a/common/sagas/wallet/wallet.ts +++ b/common/sagas/wallet/wallet.ts @@ -36,7 +36,7 @@ import { Web3Wallet, WalletConfig } from 'libs/wallet'; -import { NODES, initWeb3Node, Token } from 'config/data'; +import { NODES, initWeb3Node, Token } from 'config'; import { SagaIterator, delay, Task } from 'redux-saga'; import { apply, call, fork, put, select, takeEvery, take, cancel } from 'redux-saga/effects'; import { getNodeLib, getAllTokens, getOffline } from 'selectors/config'; @@ -261,6 +261,9 @@ export function* unlockWeb3(): SagaIterator { action.type === ConfigTypeKeys.CONFIG_NODE_CHANGE && action.payload.nodeSelection === 'web3' ); + if (!NODES.web3) { + throw Error('Web3 node config not found!'); + } const network = NODES.web3.network; const nodeLib: INode | Web3Node = yield select(getNodeLib); diff --git a/common/sass/styles.scss b/common/sass/styles.scss index 6a4bcf3d..0afe5b7e 100644 --- a/common/sass/styles.scss +++ b/common/sass/styles.scss @@ -24,6 +24,9 @@ // --- RC SLIDER --- @import "~rc-slider/assets/index.css"; +// --- React Select --- +@import '~react-select/dist/react-select.css'; + // --- CUSTOM --- @import "./styles/badbrowser"; @import "./styles/noscript"; diff --git a/common/selectors/config.ts b/common/selectors/config.ts index 9039d386..e9fd8f4e 100644 --- a/common/selectors/config.ts +++ b/common/selectors/config.ts @@ -1,11 +1,11 @@ import { + CustomNetworkConfig, + CustomNodeConfig, NetworkConfig, NetworkContract, NodeConfig, - CustomNodeConfig, - CustomNetworkConfig, Token -} from 'config/data'; +} from 'config'; import { INode } from 'libs/nodes/INode'; import { AppState } from 'reducers'; import { getUnit } from 'selectors/transaction/meta'; diff --git a/common/selectors/wallet.ts b/common/selectors/wallet.ts index 90f9f4cd..ca0e4763 100644 --- a/common/selectors/wallet.ts +++ b/common/selectors/wallet.ts @@ -1,5 +1,5 @@ import { TokenValue, Wei } from 'libs/units'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { AppState } from 'reducers'; import { getNetworkConfig } from 'selectors/config'; import { IWallet, Web3Wallet, LedgerWallet, TrezorWallet, WalletConfig } from 'libs/wallet'; diff --git a/common/utils/helpers.ts b/common/utils/helpers.ts index 82f561ae..ddeb5009 100644 --- a/common/utils/helpers.ts +++ b/common/utils/helpers.ts @@ -22,3 +22,6 @@ export function getParam(query: { [key: string]: string }, key: string) { export function isPositiveInteger(n: number) { return Number.isInteger(n) && n > 0; } + +export const getValues = (...args) => + args.reduce((acc, currArg) => [...acc, ...Object.values(currArg)], []); diff --git a/common/utils/keystore.ts b/common/utils/keystore.ts index 73c99e35..1eeb7a53 100644 --- a/common/utils/keystore.ts +++ b/common/utils/keystore.ts @@ -2,7 +2,7 @@ import { fromPrivateKey, IFullWallet, fromV3 } from 'ethereumjs-wallet'; import { isValidPrivKey } from 'libs/validators'; import { stripHexPrefix } from 'libs/values'; import { makeBlob } from 'utils/blob'; -import { N_FACTOR } from 'config/data'; +import { N_FACTOR } from 'config'; export interface KeystoreFile { filename: string; diff --git a/common/utils/network.ts b/common/utils/network.ts index c983b026..82e8291f 100644 --- a/common/utils/network.ts +++ b/common/utils/network.ts @@ -1,16 +1,43 @@ -import { NETWORKS, NetworkConfig, CustomNetworkConfig } from 'config/data'; +import { + CustomNetworkConfig, + DPathFormats, + InsecureWalletName, + NetworkConfig, + NETWORKS, + SecureWalletName, + WalletName, + walletNames +} from 'config'; +import { DPath, EXTRA_PATHS } from 'config/dpaths'; +import sortedUniq from 'lodash/sortedUniq'; +import difference from 'lodash/difference'; export function makeCustomNetworkId(config: CustomNetworkConfig): string { return config.chainId ? `${config.chainId}` : `${config.name}:${config.unit}`; } export function makeNetworkConfigFromCustomConfig(config: CustomNetworkConfig): NetworkConfig { - return { + // TODO - re-enable this block and classify customConfig after user-inputted dPaths are implemented + // ------------------------------------------------- + // this still provides the type safety we want + // as we know config coming in is CustomNetworkConfig + // meaning name will be a string + // then we cast it as any to keep it as a network key + // interface Override extends NetworkConfig { + // name: any; + // } + // ------------------------------------------------- + + // TODO - allow for user-inputted dPaths so we don't need to use any below and can use supplied dPaths + // In the meantime, networks with an unknown chainId will have HD wallets disabled + const customConfig: any = { ...config, color: '#000', tokens: [], contracts: [] }; + + return customConfig; } export function getNetworkConfigFromId( @@ -26,3 +53,68 @@ export function getNetworkConfigFromId( return makeNetworkConfigFromCustomConfig(customConfig); } } + +type PathType = keyof DPathFormats; + +type DPathFormat = + | SecureWalletName.TREZOR + | SecureWalletName.LEDGER_NANO_S + | InsecureWalletName.MNEMONIC_PHRASE; + +export function getPaths(pathType: PathType): DPath[] { + const networkPaths: DPath[] = []; + Object.values(NETWORKS).forEach(networkConfig => { + const path = networkConfig.dPathFormats ? networkConfig.dPathFormats[pathType] : []; + if (path) { + networkPaths.push(path as DPath); + } + }); + const paths = networkPaths.concat(EXTRA_PATHS); + return sortedUniq(paths); +} + +export function getSingleDPath(format: DPathFormat, network: NetworkConfig): DPath { + const dPathFormats = network.dPathFormats; + return dPathFormats[format]; +} + +export function isNetworkUnit(network: NetworkConfig, unit: string) { + const validNetworks = Object.values(NETWORKS).filter((n: NetworkConfig) => n.unit === unit); + return validNetworks.includes(network); +} + +export function isWalletFormatSupportedOnNetwork( + format: WalletName, + network: NetworkConfig +): boolean { + const CHECK_FORMATS: DPathFormat[] = [ + SecureWalletName.LEDGER_NANO_S, + SecureWalletName.TREZOR, + InsecureWalletName.MNEMONIC_PHRASE + ]; + + const isHDFormat = (f: string): f is DPathFormat => CHECK_FORMATS.includes(f as DPathFormat); + + // Ensure DPath's are found + if (isHDFormat(format)) { + const dPath = network.dPathFormats && network.dPathFormats[format]; + return !!dPath; + } + + // Ensure Web3 is only enabled on ETH or ETH Testnets (MetaMask does not support other networks) + if (format === SecureWalletName.WEB3) { + return isNetworkUnit(network, 'ETH'); + } + + // All other wallet formats are supported + return true; +} + +export function allWalletFormatsSupportedOnNetwork(network: NetworkConfig): WalletName[] { + return walletNames.filter(walletName => isWalletFormatSupportedOnNetwork(walletName, network)); +} + +export function unSupportedWalletFormatsOnNetwork(network: NetworkConfig): WalletName[] { + const supportedFormats = allWalletFormatsSupportedOnNetwork(network); + return difference(walletNames, supportedFormats); +} diff --git a/common/utils/node.ts b/common/utils/node.ts index 2973f82c..948f934f 100644 --- a/common/utils/node.ts +++ b/common/utils/node.ts @@ -1,5 +1,5 @@ import { CustomNode } from 'libs/nodes'; -import { NODES, NodeConfig, CustomNodeConfig } from 'config/data'; +import { NODES, NodeConfig, CustomNodeConfig } from 'config'; export function makeCustomNodeId(config: CustomNodeConfig): string { return `${config.url}:${config.port}`; @@ -27,10 +27,16 @@ export function getNodeConfigFromId( } export function makeNodeConfigFromCustomConfig(config: CustomNodeConfig): NodeConfig { - return { + interface Override extends NodeConfig { + network: any; + } + + const customConfig: Override = { network: config.network, lib: new CustomNode(config), service: 'your custom node', estimateGas: true }; + + return customConfig; } diff --git a/common/utils/tokens.ts b/common/utils/tokens.ts index 23d44baf..f39a482e 100644 --- a/common/utils/tokens.ts +++ b/common/utils/tokens.ts @@ -1,4 +1,4 @@ -import { Token } from 'config/data'; +import { Token } from 'config'; export function dedupeCustomTokens(networkTokens: Token[], customTokens: Token[]): Token[] { if (!customTokens.length) { diff --git a/package.json b/package.json index 5f9ca568..f8ebb0bc 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "react-redux": "5.0.6", "react-router-dom": "4.2.2", "react-router-redux": "4.0.8", + "react-select": "1.2.1", "react-stepper-horizontal": "1.0.9", "react-transition-group": "2.2.1", "redux": "3.7.2", @@ -62,6 +63,7 @@ "@types/react-redux": "5.0.14", "@types/react-router-dom": "4.2.3", "@types/react-router-redux": "5.0.11", + "@types/react-select": "1.1.0", "@types/redux-logger": "3.0.5", "@types/redux-promise-middleware": "0.0.9", "@types/uuid": "3.4.3", diff --git a/spec/config/networks.spec.ts b/spec/config/networks.spec.ts new file mode 100644 index 00000000..5227ce5a --- /dev/null +++ b/spec/config/networks.spec.ts @@ -0,0 +1,19 @@ +import { NETWORKS, NetworkConfig } from 'config'; + +describe('Networks', () => { + Object.keys(NETWORKS).forEach(networkId => { + it(`${networkId} contains non-null dPathFormats`, () => { + const network: NetworkConfig = NETWORKS[networkId]; + Object.values(network.dPathFormats).forEach(dPathFormat => { + expect(dPathFormat).toBeTruthy(); + }); + }); + }); + + it(`contain unique chainIds`, () => { + const networkValues = Object.values(NETWORKS); + const chainIds = networkValues.map(a => a.chainId); + const chainIdsSet = new Set(chainIds); + expect(Array.from(chainIdsSet).length).toEqual(chainIds.length); + }); +}); diff --git a/spec/integration/data.int.ts b/spec/integration/data.int.ts index 4287eca0..8cd56153 100644 --- a/spec/integration/data.int.ts +++ b/spec/integration/data.int.ts @@ -1,4 +1,4 @@ -import { NODES, NodeConfig } from '../../common/config/data'; +import { NODES, NodeConfig } from 'config'; import { RPCNode } from '../../common/libs/nodes'; import { Validator } from 'jsonschema'; import { schema } from '../../common/libs/validators'; diff --git a/spec/pages/SendTransaction.spec.tsx b/spec/pages/SendTransaction.spec.tsx index 4acd06b6..cd48a794 100644 --- a/spec/pages/SendTransaction.spec.tsx +++ b/spec/pages/SendTransaction.spec.tsx @@ -4,7 +4,7 @@ import Adapter from 'enzyme-adapter-react-16'; import SendTransaction from 'containers/Tabs/SendTransaction'; import shallowWithStore from '../utils/shallowWithStore'; import { createMockStore } from 'redux-test-utils'; -import { NODES } from 'config/data'; +import { NODES } from 'config'; import { RouteComponentProps } from 'react-router'; import { createMockRouteComponentProps } from '../utils/mockRouteComponentProps'; diff --git a/spec/reducers/config.spec.ts b/spec/reducers/config.spec.ts index 0b6a6679..8eebec9d 100644 --- a/spec/reducers/config.spec.ts +++ b/spec/reducers/config.spec.ts @@ -1,6 +1,6 @@ import { config, INITIAL_STATE } from 'reducers/config'; import * as configActions from 'actions/config'; -import { NODES, NETWORKS } from 'config/data'; +import { NODES, NETWORKS } from 'config'; import { makeCustomNodeId, makeNodeConfigFromCustomConfig } from 'utils/node'; const custNode = { diff --git a/spec/reducers/customTokens.spec.ts b/spec/reducers/customTokens.spec.ts index 2b597e98..a52df732 100644 --- a/spec/reducers/customTokens.spec.ts +++ b/spec/reducers/customTokens.spec.ts @@ -1,5 +1,5 @@ import { customTokens } from 'reducers/customTokens'; -import { Token } from 'config/data'; +import { Token } from 'config'; import * as customTokensActions from 'actions/customTokens'; describe('customTokens reducer', () => { diff --git a/spec/sagas/config.spec.ts b/spec/sagas/config.spec.ts index 478a33a2..1ce5bed8 100644 --- a/spec/sagas/config.spec.ts +++ b/spec/sagas/config.spec.ts @@ -9,9 +9,10 @@ import { handleNodeChangeIntent, unsetWeb3Node, unsetWeb3NodeOnWalletEvent, - equivalentNodeOrDefault + equivalentNodeOrDefault, + reload } from 'sagas/config'; -import { NODES, NodeConfig, NETWORKS } from 'config/data'; +import { NODES, NodeConfig, NETWORKS } from 'config'; import { getNode, getNodeConfig, @@ -20,13 +21,10 @@ import { getCustomNetworkConfigs } from 'selectors/config'; import { INITIAL_STATE as configInitialState } from 'reducers/config'; -import { getWalletInst } from 'selectors/wallet'; import { Web3Wallet } from 'libs/wallet'; import { RPCNode } from 'libs/nodes'; import { showNotification } from 'actions/notifications'; import { translateRaw } from 'translations'; -import { resetWallet } from 'actions/wallet'; -import { reset as resetTransaction } from 'actions/transaction'; // init module configuredStore.getState(); @@ -143,12 +141,12 @@ describe('handleNodeChangeIntent*', () => { const customNetworkConfigs = []; const defaultNodeNetwork = NETWORKS[defaultNodeConfig.network]; const newNode = Object.keys(NODES).reduce( - (acc, cur) => (NODES[acc].network === defaultNodeConfig.network ? cur : acc) + (acc, cur) => (NODES[cur].network !== defaultNodeConfig.network ? cur : acc) ); const newNodeConfig = NODES[newNode]; const newNodeNetwork = NETWORKS[newNodeConfig.network]; + const changeNodeIntentAction = changeNodeIntent(newNode); - const truthyWallet = true; const latestBlock = '0xa'; const raceSuccess = { lb: latestBlock @@ -208,15 +206,9 @@ describe('handleNodeChangeIntent*', () => { ); }); - it('should select getWalletInst', () => { - expect(data.gen.next().value).toEqual(select(getWalletInst)); - }); - - it('should call reload if wallet exists and network is new', () => { - data.clone2 = data.gen.clone(); - expect(data.clone2.next(truthyWallet).value).toEqual(put(resetWallet())); - expect(data.clone2.next(truthyWallet).value).toEqual(put(resetTransaction())); - expect(data.clone2.next().done).toEqual(true); + it('should call reload if network is new', () => { + expect(data.gen.next().value).toEqual(call(reload)); + expect(data.gen.next().done).toEqual(true); }); it('should be done', () => { @@ -366,7 +358,7 @@ describe('equivalentNodeOrDefault', () => { const node = equivalentNodeOrDefault({ ...mockNodeConfig, network: 'noEqivalentExists' - }); + } as any); expect(node).toEqual(appDefaultNode); }); @@ -374,12 +366,12 @@ describe('equivalentNodeOrDefault', () => { NODES.web3 = { ...mockNodeConfig, network: 'uniqueToWeb3' - }; + } as any; const node = equivalentNodeOrDefault({ ...mockNodeConfig, network: 'uniqueToWeb3' - }); + } as any); expect(node).toEqual(appDefaultNode); }); }); diff --git a/spec/sagas/deterministicWallets.spec.ts b/spec/sagas/deterministicWallets.spec.ts index 2965309c..630815fe 100644 --- a/spec/sagas/deterministicWallets.spec.ts +++ b/spec/sagas/deterministicWallets.spec.ts @@ -7,7 +7,7 @@ import { getDesiredToken, getWallets } from 'selectors/deterministicWallets'; import { getTokens } from 'selectors/wallet'; import { getNodeLib } from 'selectors/config'; import * as dWalletActions from 'actions/deterministicWallets'; -import { Token } from 'config/data'; +import { Token } from 'config'; import { getDeterministicWallets, updateWalletValues, diff --git a/spec/sagas/wallet.spec.tsx b/spec/sagas/wallet.spec.tsx index 17fc1d4f..b6f8e3ae 100644 --- a/spec/sagas/wallet.spec.tsx +++ b/spec/sagas/wallet.spec.tsx @@ -13,7 +13,7 @@ import { import { Wei } from 'libs/units'; import { changeNodeIntent, web3UnsetNode } from 'actions/config'; import { INode } from 'libs/nodes/INode'; -import { initWeb3Node, Token, N_FACTOR } from 'config/data'; +import { initWeb3Node, Token, N_FACTOR } from 'config'; import { apply, call, fork, put, select, take } from 'redux-saga/effects'; import { getNodeLib, getOffline } from 'selectors/config'; import { getWalletInst, getWalletConfigTokens } from 'selectors/wallet';