mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-02-24 16:58:07 +00:00
Support Non-Ethereum Networks (#849)
* Make UnlockHeader a PureComponent * MVP * actually disable wallet format if not determined to be valid format for wallet * default to correct derivation in mnemonic modal * cleanup * fix tslint * use enums for HD wallet getPath * Add stricter typing * Fix labels not updating on selector * Ban hardware wallet support for custom network unsupported chainIds * Fix type error * Fix custom node dPath not being saved * Fix mnemonic modal * default path bugfixes * add react-select * misc fixes; rabbit holing hard. * fix tslint * revert identicon changes * reload on network change :/ * actually reload on network change * really really reload on network change * tslint fixes * Update styles * set table width * fix package versioning * push broken sagas * Fix saga test * fix tslint * address round of review * move non-selectors out to utilty; adjust reload timer * cleanup network util comments * manage wallet disable at WalletDecrypt instead of in both WalletDecrypt and WalletButton * Separate WalletDecrypt props into ownProps / StateProps * disable payment requests on non-eth networks * specialize connect; separate props * remove unused state prop * remove bad import * create tests for networks * Clarify Lite-Send error on non-ethereum networkS * remove string option for network config name * Create concept of always-on 'EXTRA_PATHS'; include SINGULAR_DTV legacy dPath in 'EXTRA_PATHS' * fix multiple imports * address PR comments
This commit is contained in:
parent
2420f5488b
commit
ab5fa1a799
@ -1,6 +1,6 @@
|
|||||||
import * as interfaces from './actionTypes';
|
import * as interfaces from './actionTypes';
|
||||||
import { TypeKeys } from './constants';
|
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 type TToggleOfflineConfig = typeof toggleOfflineConfig;
|
||||||
export function toggleOfflineConfig(): interfaces.ToggleOfflineAction {
|
export function toggleOfflineConfig(): interfaces.ToggleOfflineAction {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config/data';
|
import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config';
|
||||||
|
|
||||||
/*** Toggle Offline ***/
|
/*** Toggle Offline ***/
|
||||||
export interface ToggleOfflineAction {
|
export interface ToggleOfflineAction {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import * as interfaces from './actionTypes';
|
import * as interfaces from './actionTypes';
|
||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
/*** Add custom token ***/
|
/*** Add custom token ***/
|
||||||
export interface AddCustomTokenAction {
|
export interface AddCustomTokenAction {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import bityConfig, { WhitelistedCoins } from 'config/bity';
|
import { WhitelistedCoins, bityConfig } from 'config';
|
||||||
import { checkHttpStatus, parseJSON, filter } from './utils';
|
import { checkHttpStatus, parseJSON, filter } from './utils';
|
||||||
import bitcoinIcon from 'assets/images/bitcoin.png';
|
import bitcoinIcon from 'assets/images/bitcoin.png';
|
||||||
import repIcon from 'assets/images/augur.png';
|
import repIcon from 'assets/images/augur.png';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AddressFieldFactory } from './AddressFieldFactory';
|
import { AddressFieldFactory } from './AddressFieldFactory';
|
||||||
import { donationAddressMap } from 'config/data';
|
import { donationAddressMap } from 'config';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Identicon, UnitDisplay } from 'components/ui';
|
import { Identicon, UnitDisplay } from 'components/ui';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
|
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
@ -127,11 +127,11 @@ export default class AccountInfo extends React.Component<Props, State> {
|
|||||||
{!!blockExplorer && (
|
{!!blockExplorer && (
|
||||||
<li className="AccountInfo-list-item">
|
<li className="AccountInfo-list-item">
|
||||||
<a
|
<a
|
||||||
href={blockExplorer.address(address)}
|
href={blockExplorer.addressUrl(address)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{`${network.name} (${blockExplorer.name})`}
|
{`${network.name} (${blockExplorer.origin})`}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
@ -5,7 +5,7 @@ import { State } from 'reducers/rates';
|
|||||||
import { rateSymbols, TFetchCCRates } from 'actions/rates';
|
import { rateSymbols, TFetchCCRates } from 'actions/rates';
|
||||||
import { TokenBalance } from 'selectors/wallet';
|
import { TokenBalance } from 'selectors/wallet';
|
||||||
import { Balance } from 'libs/wallet';
|
import { Balance } from 'libs/wallet';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { ETH_DECIMAL, convertTokenBase } from 'libs/units';
|
import { ETH_DECIMAL, convertTokenBase } from 'libs/units';
|
||||||
import Spinner from 'components/ui/Spinner';
|
import Spinner from 'components/ui/Spinner';
|
||||||
import UnitDisplay from 'components/ui/UnitDisplay';
|
import UnitDisplay from 'components/ui/UnitDisplay';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
|
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import NewTabLink from 'components/ui/NewTabLink';
|
import NewTabLink from 'components/ui/NewTabLink';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { TokenBalance } from 'selectors/wallet';
|
import { TokenBalance } from 'selectors/wallet';
|
||||||
import AddCustomTokenForm from './AddCustomTokenForm';
|
import AddCustomTokenForm from './AddCustomTokenForm';
|
||||||
import TokenRow from './TokenRow';
|
import TokenRow from './TokenRow';
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from 'actions/wallet';
|
} from 'actions/wallet';
|
||||||
import { getAllTokens } from 'selectors/config';
|
import { getAllTokens } from 'selectors/config';
|
||||||
import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet';
|
import { getTokenBalances, getWalletInst, getWalletConfig, TokenBalance } from 'selectors/wallet';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import Balances from './Balances';
|
import Balances from './Balances';
|
||||||
import Spinner from 'components/ui/Spinner';
|
import Spinner from 'components/ui/Spinner';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { IWallet, Balance } from 'libs/wallet';
|
import { IWallet, Balance } from 'libs/wallet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NodeConfig } from 'config/data';
|
import { NodeConfig } from 'config';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DataFieldFactory } from './DataFieldFactory';
|
import { DataFieldFactory } from './DataFieldFactory';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { donationAddressMap } from 'config/data';
|
import { donationAddressMap } from 'config';
|
||||||
|
|
||||||
export const DataField: React.SFC<{}> = () => (
|
export const DataField: React.SFC<{}> = () => (
|
||||||
<DataFieldFactory
|
<DataFieldFactory
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BlockExplorerConfig } from 'config/data';
|
import { BlockExplorerConfig } from 'config';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export interface TransactionSucceededProps {
|
|||||||
|
|
||||||
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
|
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
|
||||||
// const checkTxLink = `https://www.myetherwallet.com?txHash=${txHash}/#check-tx-status`;
|
// const checkTxLink = `https://www.myetherwallet.com?txHash=${txHash}/#check-tx-status`;
|
||||||
const txHashLink = blockExplorer.tx(txHash);
|
const txHashLink = blockExplorer.txUrl(txHash);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
donationAddressMap,
|
donationAddressMap,
|
||||||
VERSION,
|
VERSION,
|
||||||
knowledgeBaseURL
|
knowledgeBaseURL
|
||||||
} from 'config/data';
|
} from 'config';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Slider from 'rc-slider';
|
import Slider from 'rc-slider';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { gasPriceDefaults } from 'config/data';
|
import { gasPriceDefaults } from 'config';
|
||||||
import FeeSummary from './FeeSummary';
|
import FeeSummary from './FeeSummary';
|
||||||
import { TInputGasPrice } from 'actions/transaction';
|
import { TInputGasPrice } from 'actions/transaction';
|
||||||
import './SimpleGas.scss';
|
import './SimpleGas.scss';
|
||||||
|
@ -3,7 +3,7 @@ import { generateKeystoreFileInfo, KeystoreFile } from 'utils/keystore';
|
|||||||
import Modal from 'components/ui/Modal';
|
import Modal from 'components/ui/Modal';
|
||||||
import Input from './Input';
|
import Input from './Input';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { MINIMUM_PASSWORD_LENGTH } from 'config/data';
|
import { MINIMUM_PASSWORD_LENGTH } from 'config';
|
||||||
import { isValidPrivKey } from 'libs/validators';
|
import { isValidPrivKey } from 'libs/validators';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Modal, { IButton } from 'components/ui/Modal';
|
import Modal, { IButton } from 'components/ui/Modal';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { NETWORKS, CustomNodeConfig, CustomNetworkConfig } from 'config/data';
|
import { NETWORKS, CustomNodeConfig, CustomNetworkConfig } from 'config';
|
||||||
import { makeCustomNodeId } from 'utils/node';
|
import { makeCustomNodeId } from 'utils/node';
|
||||||
import { makeCustomNetworkId } from 'utils/network';
|
import { makeCustomNetworkId } from 'utils/network';
|
||||||
|
|
||||||
@ -303,10 +303,16 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private makeCustomNetworkConfigFromState(): CustomNetworkConfig {
|
private makeCustomNetworkConfigFromState(): CustomNetworkConfig {
|
||||||
|
const similarNetworkConfig = Object.values(NETWORKS).find(
|
||||||
|
n => n.chainId === +this.state.customNetworkChainId
|
||||||
|
);
|
||||||
|
const dPathFormats = similarNetworkConfig ? similarNetworkConfig.dPathFormats : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this.state.customNetworkName,
|
name: this.state.customNetworkName,
|
||||||
unit: this.state.customNetworkUnit,
|
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<Props, State> {
|
|||||||
|
|
||||||
if (this.state.network === CUSTOM) {
|
if (this.state.network === CUSTOM) {
|
||||||
const network = this.makeCustomNetworkConfigFromState();
|
const network = this.makeCustomNetworkConfigFromState();
|
||||||
|
|
||||||
this.props.handleAddCustomNetwork(network);
|
this.props.handleAddCustomNetwork(network);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { gasPriceDefaults, knowledgeBaseURL } from 'config/data';
|
import { gasPriceDefaults, knowledgeBaseURL } from 'config';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import DropdownShell from 'components/ui/DropdownShell';
|
import DropdownShell from 'components/ui/DropdownShell';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import NavigationLink from './NavigationLink';
|
import NavigationLink from './NavigationLink';
|
||||||
import { knowledgeBaseURL } from 'config/data';
|
import { knowledgeBaseURL } from 'config';
|
||||||
import './Navigation.scss';
|
import './Navigation.scss';
|
||||||
|
|
||||||
export interface TabLink {
|
export interface TabLink {
|
||||||
|
@ -20,7 +20,7 @@ import {
|
|||||||
NodeConfig,
|
NodeConfig,
|
||||||
CustomNodeConfig,
|
CustomNodeConfig,
|
||||||
CustomNetworkConfig
|
CustomNetworkConfig
|
||||||
} from 'config/data';
|
} from 'config';
|
||||||
import GasPriceDropdown from './components/GasPriceDropdown';
|
import GasPriceDropdown from './components/GasPriceDropdown';
|
||||||
import Navigation from './components/Navigation';
|
import Navigation from './components/Navigation';
|
||||||
import CustomNodeModal from './components/CustomNodeModal';
|
import CustomNodeModal from './components/CustomNodeModal';
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
import { reset, TReset } from 'actions/transaction';
|
import { reset, TReset } from 'actions/transaction';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import {
|
import {
|
||||||
DigitalBitboxDecrypt,
|
|
||||||
KeystoreDecrypt,
|
KeystoreDecrypt,
|
||||||
LedgerNanoSDecrypt,
|
LedgerNanoSDecrypt,
|
||||||
MnemonicDecrypt,
|
MnemonicDecrypt,
|
||||||
@ -31,38 +30,53 @@ import {
|
|||||||
WalletButton
|
WalletButton
|
||||||
} from './components';
|
} from './components';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { knowledgeBaseURL, isWeb3NodeAvailable } from 'config/data';
|
import DISABLES from './disables';
|
||||||
import { IWallet } from 'libs/wallet';
|
|
||||||
import DISABLES from './disables.json';
|
|
||||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||||
|
|
||||||
import DigitalBitboxIcon from 'assets/images/wallets/digital-bitbox.svg';
|
|
||||||
import LedgerIcon from 'assets/images/wallets/ledger.svg';
|
import LedgerIcon from 'assets/images/wallets/ledger.svg';
|
||||||
import MetamaskIcon from 'assets/images/wallets/metamask.svg';
|
import MetamaskIcon from 'assets/images/wallets/metamask.svg';
|
||||||
import MistIcon from 'assets/images/wallets/mist.svg';
|
import MistIcon from 'assets/images/wallets/mist.svg';
|
||||||
import TrezorIcon from 'assets/images/wallets/trezor.svg';
|
import TrezorIcon from 'assets/images/wallets/trezor.svg';
|
||||||
import './WalletDecrypt.scss';
|
import './WalletDecrypt.scss';
|
||||||
|
import {
|
||||||
|
SecureWalletName,
|
||||||
|
InsecureWalletName,
|
||||||
|
MiscWalletName,
|
||||||
|
WalletName,
|
||||||
|
isWeb3NodeAvailable,
|
||||||
|
knowledgeBaseURL
|
||||||
|
} from 'config';
|
||||||
|
import { unSupportedWalletFormatsOnNetwork } from 'utils/network';
|
||||||
|
import { getNetworkConfig } from '../../selectors/config';
|
||||||
|
|
||||||
interface Props {
|
interface OwnProps {
|
||||||
resetTransactionState: TReset;
|
hidden?: boolean;
|
||||||
|
disabledWallets?: WalletName[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
unlockKeystore: TUnlockKeystore;
|
unlockKeystore: TUnlockKeystore;
|
||||||
unlockMnemonic: TUnlockMnemonic;
|
unlockMnemonic: TUnlockMnemonic;
|
||||||
unlockPrivateKey: TUnlockPrivateKey;
|
unlockPrivateKey: TUnlockPrivateKey;
|
||||||
setWallet: TSetWallet;
|
|
||||||
unlockWeb3: TUnlockWeb3;
|
unlockWeb3: TUnlockWeb3;
|
||||||
|
setWallet: TSetWallet;
|
||||||
resetWallet: TResetWallet;
|
resetWallet: TResetWallet;
|
||||||
|
resetTransactionState: TReset;
|
||||||
showNotification: TShowNotification;
|
showNotification: TShowNotification;
|
||||||
wallet: IWallet;
|
}
|
||||||
hidden?: boolean;
|
|
||||||
|
interface StateProps {
|
||||||
|
computedDisabledWallets: WalletName[];
|
||||||
offline: boolean;
|
offline: boolean;
|
||||||
disabledWallets?: string[];
|
|
||||||
isWalletPending: AppState['wallet']['isWalletPending'];
|
isWalletPending: AppState['wallet']['isWalletPending'];
|
||||||
isPasswordPending: AppState['wallet']['isPasswordPending'];
|
isPasswordPending: AppState['wallet']['isPasswordPending'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
|
||||||
type UnlockParams = {} | PrivateKeyValue;
|
type UnlockParams = {} | PrivateKeyValue;
|
||||||
interface State {
|
interface State {
|
||||||
selectedWalletKey: string | null;
|
selectedWalletKey: WalletName | null;
|
||||||
value: UnlockParams | null;
|
value: UnlockParams | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,13 +85,13 @@ interface BaseWalletInfo {
|
|||||||
component: any;
|
component: any;
|
||||||
initialParams: object;
|
initialParams: object;
|
||||||
unlock: any;
|
unlock: any;
|
||||||
helpLink?: string;
|
helpLink: string;
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
attemptUnlock?: boolean;
|
attemptUnlock?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SecureWalletInfo extends BaseWalletInfo {
|
export interface SecureWalletInfo extends BaseWalletInfo {
|
||||||
icon?: string | null;
|
icon?: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +99,9 @@ export interface InsecureWalletInfo extends BaseWalletInfo {
|
|||||||
example: string;
|
example: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface MiscWalletInfo extends InsecureWalletInfo {}
|
||||||
|
|
||||||
const WEB3_TYPES = {
|
const WEB3_TYPES = {
|
||||||
MetamaskInpageProvider: {
|
MetamaskInpageProvider: {
|
||||||
lid: 'x_MetaMask',
|
lid: 'x_MetaMask',
|
||||||
@ -95,15 +112,20 @@ const WEB3_TYPES = {
|
|||||||
icon: MistIcon
|
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 =
|
const WEB3_TYPE: string | false =
|
||||||
(window as any).web3 && (window as any).web3.currentProvider.constructor.name;
|
(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<Props, State> {
|
export class WalletDecrypt extends Component<Props, State> {
|
||||||
public WALLETS: { [key: string]: SecureWalletInfo | InsecureWalletInfo } = {
|
// https://github.com/Microsoft/TypeScript/issues/13042
|
||||||
web3: {
|
// 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',
|
lid: WEB3_TYPE ? WEB3_TYPES[WEB3_TYPE].lid : 'x_Web3',
|
||||||
icon: WEB3_TYPE && WEB3_TYPES[WEB3_TYPE].icon,
|
icon: WEB3_TYPE && WEB3_TYPES[WEB3_TYPE].icon,
|
||||||
description: 'ADD_Web3Desc',
|
description: 'ADD_Web3Desc',
|
||||||
@ -113,7 +135,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
attemptUnlock: true,
|
attemptUnlock: true,
|
||||||
helpLink: `${knowledgeBaseURL}/migration/moving-from-private-key-to-metamask`
|
helpLink: `${knowledgeBaseURL}/migration/moving-from-private-key-to-metamask`
|
||||||
},
|
},
|
||||||
'ledger-nano-s': {
|
[SecureWalletName.LEDGER_NANO_S]: {
|
||||||
lid: 'x_Ledger',
|
lid: 'x_Ledger',
|
||||||
icon: LedgerIcon,
|
icon: LedgerIcon,
|
||||||
description: 'ADD_HardwareDesc',
|
description: 'ADD_HardwareDesc',
|
||||||
@ -123,7 +145,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
helpLink:
|
helpLink:
|
||||||
'https://ledger.zendesk.com/hc/en-us/articles/115005200009-How-to-use-MyEtherWallet-with-Ledger'
|
'https://ledger.zendesk.com/hc/en-us/articles/115005200009-How-to-use-MyEtherWallet-with-Ledger'
|
||||||
},
|
},
|
||||||
trezor: {
|
[SecureWalletName.TREZOR]: {
|
||||||
lid: 'x_Trezor',
|
lid: 'x_Trezor',
|
||||||
icon: TrezorIcon,
|
icon: TrezorIcon,
|
||||||
description: 'ADD_HardwareDesc',
|
description: 'ADD_HardwareDesc',
|
||||||
@ -132,16 +154,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
unlock: this.props.setWallet,
|
unlock: this.props.setWallet,
|
||||||
helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html'
|
helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html'
|
||||||
},
|
},
|
||||||
'digital-bitbox': {
|
[InsecureWalletName.KEYSTORE_FILE]: {
|
||||||
lid: 'x_DigitalBitbox',
|
|
||||||
icon: DigitalBitboxIcon,
|
|
||||||
description: 'ADD_HardwareDesc',
|
|
||||||
component: DigitalBitboxDecrypt,
|
|
||||||
initialParams: {},
|
|
||||||
unlock: this.props.setWallet,
|
|
||||||
helpLink: 'https://digitalbitbox.com/ethereum'
|
|
||||||
},
|
|
||||||
'keystore-file': {
|
|
||||||
lid: 'x_Keystore2',
|
lid: 'x_Keystore2',
|
||||||
example: 'UTC--2017-12-15T17-35-22.547Z--6be6e49e82425a5aa56396db03512f2cc10e95e8',
|
example: 'UTC--2017-12-15T17-35-22.547Z--6be6e49e82425a5aa56396db03512f2cc10e95e8',
|
||||||
component: KeystoreDecrypt,
|
component: KeystoreDecrypt,
|
||||||
@ -152,7 +165,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
unlock: this.props.unlockKeystore,
|
unlock: this.props.unlockKeystore,
|
||||||
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||||
},
|
},
|
||||||
'mnemonic-phrase': {
|
[InsecureWalletName.MNEMONIC_PHRASE]: {
|
||||||
lid: 'x_Mnemonic',
|
lid: 'x_Mnemonic',
|
||||||
example: 'brain surround have swap horror cheese file distinct',
|
example: 'brain surround have swap horror cheese file distinct',
|
||||||
component: MnemonicDecrypt,
|
component: MnemonicDecrypt,
|
||||||
@ -160,7 +173,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
unlock: this.props.unlockMnemonic,
|
unlock: this.props.unlockMnemonic,
|
||||||
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||||
},
|
},
|
||||||
'private-key': {
|
[InsecureWalletName.PRIVATE_KEY]: {
|
||||||
lid: 'x_PrivKey2',
|
lid: 'x_PrivKey2',
|
||||||
example: 'f1d0e0789c6d40f399ca90cc674b7858de4c719e0d5752a60d5d2f6baa45d4c9',
|
example: 'f1d0e0789c6d40f399ca90cc674b7858de4c719e0d5752a60d5d2f6baa45d4c9',
|
||||||
component: PrivateKeyDecrypt,
|
component: PrivateKeyDecrypt,
|
||||||
@ -171,7 +184,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
unlock: this.props.unlockPrivateKey,
|
unlock: this.props.unlockPrivateKey,
|
||||||
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||||
},
|
},
|
||||||
'view-only': {
|
[MiscWalletName.VIEW_ONLY]: {
|
||||||
lid: 'View Address',
|
lid: 'View Address',
|
||||||
example: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
|
example: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
|
||||||
component: ViewOnlyDecrypt,
|
component: ViewOnlyDecrypt,
|
||||||
@ -181,12 +194,13 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
isReadOnly: true
|
isReadOnly: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public state: State = {
|
public state: State = {
|
||||||
selectedWalletKey: null,
|
selectedWalletKey: null,
|
||||||
value: null
|
value: null
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
// Reset state when unlock is hidden / revealed
|
// Reset state when unlock is hidden / revealed
|
||||||
if (nextProps.hidden !== this.props.hidden) {
|
if (nextProps.hidden !== this.props.hidden) {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -218,10 +232,12 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
onUnlock={this.onUnlock}
|
onUnlock={this.onUnlock}
|
||||||
showNotification={this.props.showNotification}
|
showNotification={this.props.showNotification}
|
||||||
isWalletPending={
|
isWalletPending={
|
||||||
this.state.selectedWalletKey === 'keystore-file' ? this.props.isWalletPending : undefined
|
this.state.selectedWalletKey === InsecureWalletName.KEYSTORE_FILE
|
||||||
|
? this.props.isWalletPending
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
isPasswordPending={
|
isPasswordPending={
|
||||||
this.state.selectedWalletKey === 'keystore-file'
|
this.state.selectedWalletKey === InsecureWalletName.KEYSTORE_FILE
|
||||||
? this.props.isPasswordPending
|
? this.props.isPasswordPending
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
@ -230,64 +246,72 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public buildWalletOptions() {
|
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 (
|
return (
|
||||||
<div className="WalletDecrypt-wallets">
|
<div className="WalletDecrypt-wallets">
|
||||||
<h2 className="WalletDecrypt-wallets-title">{translate('decrypt_Access')}</h2>
|
<h2 className="WalletDecrypt-wallets-title">{translate('decrypt_Access')}</h2>
|
||||||
|
|
||||||
<div className="WalletDecrypt-wallets-row">
|
<div className="WalletDecrypt-wallets-row">
|
||||||
{SECURE_WALLETS.map(type => {
|
{SECURE_WALLETS.map((walletType: SecureWalletName) => {
|
||||||
const wallet = this.WALLETS[type] as SecureWalletInfo;
|
const wallet = this.WALLETS[walletType];
|
||||||
return (
|
return (
|
||||||
<WalletButton
|
<WalletButton
|
||||||
key={type}
|
key={walletType}
|
||||||
name={translate(wallet.lid)}
|
name={translate(wallet.lid)}
|
||||||
description={translate(wallet.description)}
|
description={translate(wallet.description)}
|
||||||
icon={wallet.icon}
|
icon={wallet.icon}
|
||||||
helpLink={wallet.helpLink}
|
helpLink={wallet.helpLink}
|
||||||
walletType={type}
|
walletType={walletType}
|
||||||
isSecure={true}
|
isSecure={true}
|
||||||
isDisabled={this.isWalletDisabled(type)}
|
isDisabled={this.isWalletDisabled(walletType)}
|
||||||
onClick={this.handleWalletChoice}
|
onClick={this.handleWalletChoice}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="WalletDecrypt-wallets-row">
|
<div className="WalletDecrypt-wallets-row">
|
||||||
{INSECURE_WALLETS.map(type => {
|
{INSECURE_WALLETS.map((walletType: InsecureWalletName) => {
|
||||||
const wallet = this.WALLETS[type] as InsecureWalletInfo;
|
const wallet = this.WALLETS[walletType];
|
||||||
return (
|
return (
|
||||||
<WalletButton
|
<WalletButton
|
||||||
key={type}
|
key={walletType}
|
||||||
name={translate(wallet.lid)}
|
name={translate(wallet.lid)}
|
||||||
example={wallet.example}
|
example={wallet.example}
|
||||||
helpLink={wallet.helpLink}
|
helpLink={wallet.helpLink}
|
||||||
walletType={type}
|
walletType={walletType}
|
||||||
isSecure={false}
|
isSecure={false}
|
||||||
isDisabled={this.isWalletDisabled(type)}
|
isDisabled={this.isWalletDisabled(walletType)}
|
||||||
onClick={this.handleWalletChoice}
|
onClick={this.handleWalletChoice}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<WalletButton
|
{MISC_WALLETS.map((walletType: MiscWalletName) => {
|
||||||
key="view-only"
|
const wallet = this.WALLETS[walletType];
|
||||||
name={translate(viewOnly.lid)}
|
return (
|
||||||
example={viewOnly.example}
|
<WalletButton
|
||||||
helpLink={viewOnly.helpLink}
|
key={walletType}
|
||||||
walletType="view-only"
|
name={translate(wallet.lid)}
|
||||||
isReadOnly={true}
|
example={wallet.example}
|
||||||
isDisabled={this.isWalletDisabled('view-only')}
|
helpLink={wallet.helpLink}
|
||||||
onClick={this.handleWalletChoice}
|
walletType={walletType}
|
||||||
/>
|
isReadOnly={true}
|
||||||
|
isDisabled={this.isWalletDisabled(walletType)}
|
||||||
|
onClick={this.handleWalletChoice}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleWalletChoice = async (walletType: string) => {
|
public handleWalletChoice = async (walletType: WalletName) => {
|
||||||
const wallet = this.WALLETS[walletType];
|
const wallet = this.WALLETS[walletType];
|
||||||
|
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -301,7 +325,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
wallet.unlock();
|
wallet.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedWalletKey: walletType,
|
selectedWalletKey: walletType,
|
||||||
value: wallet.initialParams
|
value: wallet.initialParams
|
||||||
@ -375,28 +399,31 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||||||
this.props.resetTransactionState();
|
this.props.resetTransactionState();
|
||||||
};
|
};
|
||||||
|
|
||||||
private isWalletDisabled = (walletKey: string) => {
|
private isWalletDisabled = (walletKey: WalletName) => {
|
||||||
if (this.props.offline && DISABLES.ONLINE_ONLY.includes(walletKey)) {
|
if (this.props.offline && DISABLES.ONLINE_ONLY.includes(walletKey)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.props.disabledWallets) {
|
return this.props.computedDisabledWallets.indexOf(walletKey) !== -1;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.props.disabledWallets.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 {
|
return {
|
||||||
|
computedDisabledWallets,
|
||||||
offline: state.config.offline,
|
offline: state.config.offline,
|
||||||
wallet: state.wallet.inst,
|
|
||||||
isWalletPending: state.wallet.isWalletPending,
|
isWalletPending: state.wallet.isWalletPending,
|
||||||
isPasswordPending: state.wallet.isPasswordPending
|
isPasswordPending: state.wallet.isPasswordPending
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
export default connect<StateProps, DispatchProps>(mapStateToProps, {
|
||||||
unlockKeystore,
|
unlockKeystore,
|
||||||
unlockMnemonic,
|
unlockMnemonic,
|
||||||
unlockPrivateKey,
|
unlockPrivateKey,
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
@import "common/sass/variables";
|
@import 'common/sass/variables';
|
||||||
@import "common/sass/mixins";
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
.DWModal {
|
.DWModal {
|
||||||
width: 690px;
|
|
||||||
|
|
||||||
&-path {
|
&-path {
|
||||||
display: block;
|
display: flex;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
font-size: $font-size-medium;
|
font-size: $font-size-medium;
|
||||||
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
@ -17,12 +16,18 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
margin: 0 0 0 10px;
|
margin: 0 0 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Select {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-addresses {
|
&-addresses {
|
||||||
|
overflow-y: scroll;
|
||||||
&-table {
|
&-table {
|
||||||
width: 100%;
|
width: 695px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
&-token {
|
&-token {
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'actions/deterministicWallets';
|
} from 'actions/deterministicWallets';
|
||||||
import Modal, { IButton } from 'components/ui/Modal';
|
import Modal, { IButton } from 'components/ui/Modal';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { isValidPath } from 'libs/validators';
|
import { isValidPath } from 'libs/validators';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@ -16,6 +16,8 @@ import { getNetworkConfig } from 'selectors/config';
|
|||||||
import { getTokens, MergedToken } from 'selectors/wallet';
|
import { getTokens, MergedToken } from 'selectors/wallet';
|
||||||
import { UnitDisplay } from 'components/ui';
|
import { UnitDisplay } from 'components/ui';
|
||||||
import './DeterministicWalletsModal.scss';
|
import './DeterministicWalletsModal.scss';
|
||||||
|
import { DPath } from 'config/dpaths';
|
||||||
|
import Select from 'react-select';
|
||||||
|
|
||||||
const WALLETS_PER_PAGE = 5;
|
const WALLETS_PER_PAGE = 5;
|
||||||
|
|
||||||
@ -24,7 +26,7 @@ interface Props {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
walletType?: string;
|
walletType?: string;
|
||||||
dPath: string;
|
dPath: string;
|
||||||
dPaths: { label: string; value: string }[];
|
dPaths: DPath[];
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
chainCode?: string;
|
chainCode?: string;
|
||||||
seed?: string;
|
seed?: string;
|
||||||
@ -45,6 +47,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
currentLabel: string;
|
||||||
selectedAddress: string;
|
selectedAddress: string;
|
||||||
selectedAddrIndex: number;
|
selectedAddrIndex: number;
|
||||||
isCustomPath: boolean;
|
isCustomPath: boolean;
|
||||||
@ -52,12 +55,18 @@ interface State {
|
|||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customDPath: DPath = {
|
||||||
|
label: 'custom',
|
||||||
|
value: 'custom'
|
||||||
|
};
|
||||||
|
|
||||||
class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
||||||
public state = {
|
public state: State = {
|
||||||
selectedAddress: '',
|
selectedAddress: '',
|
||||||
selectedAddrIndex: 0,
|
selectedAddrIndex: 0,
|
||||||
isCustomPath: false,
|
isCustomPath: false,
|
||||||
customPath: '',
|
customPath: '',
|
||||||
|
currentLabel: '',
|
||||||
page: 0
|
page: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,7 +97,7 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||||||
onCancel,
|
onCancel,
|
||||||
walletType
|
walletType
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { selectedAddress, isCustomPath, customPath, page } = this.state;
|
const { selectedAddress, customPath, page } = this.state;
|
||||||
const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid';
|
const validPathClass = isValidPath(customPath) ? 'is-valid' : 'is-invalid';
|
||||||
|
|
||||||
const buttons: IButton[] = [
|
const buttons: IButton[] = [
|
||||||
@ -113,21 +122,20 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||||||
handleClose={onCancel}
|
handleClose={onCancel}
|
||||||
>
|
>
|
||||||
<div className="DWModal">
|
<div className="DWModal">
|
||||||
|
{/* TODO: replace styles for flexbox with flexbox classes in https://github.com/MyEtherWallet/MyEtherWallet/pull/850/files#diff-2150778b9391533fec7b8afd060c7672 */}
|
||||||
<form className="DWModal-path form-group-sm" onSubmit={this.handleSubmitCustomPath}>
|
<form className="DWModal-path form-group-sm" onSubmit={this.handleSubmitCustomPath}>
|
||||||
<span className="DWModal-path-label">Addresses for</span>
|
<span className="DWModal-path-label">Addresses </span>
|
||||||
<select
|
<Select
|
||||||
className="form-control"
|
name="fieldDPath"
|
||||||
|
className=""
|
||||||
|
value={this.state.currentLabel || this.findDPath('value', dPath).value}
|
||||||
onChange={this.handleChangePath}
|
onChange={this.handleChangePath}
|
||||||
value={isCustomPath ? 'custom' : dPath}
|
options={dPaths}
|
||||||
>
|
clearable={false}
|
||||||
{dPaths.map(dp => (
|
searchable={false}
|
||||||
<option key={dp.value} value={dp.value}>
|
/>
|
||||||
{dp.label}
|
{/* TODO/Hack - Custom Paths are temporarily disabled. `false` is used for smallest diff */}
|
||||||
</option>
|
{false && (
|
||||||
))}
|
|
||||||
<option value="custom">Custom path...</option>
|
|
||||||
</select>
|
|
||||||
{isCustomPath && (
|
|
||||||
<input
|
<input
|
||||||
className={`form-control ${validPathClass}`}
|
className={`form-control ${validPathClass}`}
|
||||||
value={customPath}
|
value={customPath}
|
||||||
@ -163,22 +171,21 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>{wallets.map(wallet => this.renderWalletRow(wallet))}</tbody>
|
<tbody>{wallets.map(wallet => this.renderWalletRow(wallet))}</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
<div className="DWModal-addresses-nav">
|
<div className="DWModal-addresses-nav">
|
||||||
<button
|
<button
|
||||||
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
|
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
|
||||||
disabled={page === 0}
|
disabled={page === 0}
|
||||||
onClick={this.prevPage}
|
onClick={this.prevPage}
|
||||||
>
|
>
|
||||||
← Back
|
← Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
|
className="DWModal-addresses-nav-btn btn btn-sm btn-default"
|
||||||
onClick={this.nextPage}
|
onClick={this.nextPage}
|
||||||
>
|
>
|
||||||
More →
|
More →
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
@ -200,16 +207,19 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleChangePath = (ev: React.FormEvent<HTMLSelectElement>) => {
|
private findDPath = (prop: keyof DPath, cmp: string) => {
|
||||||
const { value } = ev.currentTarget;
|
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') {
|
if (value === 'custom') {
|
||||||
this.setState({ isCustomPath: true });
|
this.setState({ isCustomPath: true, currentLabel: dPathLabel });
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isCustomPath: false });
|
this.setState({ isCustomPath: false, currentLabel: dPathLabel });
|
||||||
if (this.props.dPath !== value) {
|
this.props.onPathChange(value);
|
||||||
this.props.onPathChange(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,15 +4,22 @@ import translate, { translateRaw } from 'translations';
|
|||||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||||
import { LedgerWallet } from 'libs/wallet';
|
import { LedgerWallet } from 'libs/wallet';
|
||||||
import ledger from 'ledgerco';
|
import ledger from 'ledgerco';
|
||||||
import DPATHS from 'config/dpaths';
|
|
||||||
import { Spinner } from 'components/ui';
|
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 OwnProps {
|
||||||
|
|
||||||
interface Props {
|
|
||||||
onUnlock(param: any): void;
|
onUnlock(param: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
dPath: DPath;
|
||||||
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
chainCode: string;
|
chainCode: string;
|
||||||
@ -22,11 +29,13 @@ interface State {
|
|||||||
showTip: boolean;
|
showTip: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LedgerNanoSDecrypt extends Component<Props, State> {
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
|
class LedgerNanoSDecryptClass extends Component<Props, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: DEFAULT_PATH,
|
dPath: this.props.dPath.value,
|
||||||
error: null,
|
error: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
showTip: false
|
showTip: false
|
||||||
@ -114,7 +123,7 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||||||
publicKey={publicKey}
|
publicKey={publicKey}
|
||||||
chainCode={chainCode}
|
chainCode={chainCode}
|
||||||
dPath={dPath}
|
dPath={dPath}
|
||||||
dPaths={DPATHS.LEDGER}
|
dPaths={getPaths(SecureWalletName.LEDGER_NANO_S)}
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
@ -146,11 +155,11 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err.metaData.code === 5) {
|
if (err.metaData && err.metaData.code === 5) {
|
||||||
this.showTip();
|
this.showTip();
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
error: err.metaData.type,
|
error: err.metaData ? err.metaData.type : err,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -174,7 +183,16 @@ export class LedgerNanoSDecrypt extends Component<Props, State> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
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);
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
import { mnemonicToSeed, validateMnemonic } from 'bip39';
|
import { mnemonicToSeed, validateMnemonic } from 'bip39';
|
||||||
import DPATHS from 'config/dpaths';
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||||
import { formatMnemonic } from 'utils/formatters';
|
import { formatMnemonic } from 'utils/formatters';
|
||||||
|
import { InsecureWalletName } from 'config';
|
||||||
const DEFAULT_PATH = DPATHS.MNEMONIC[0].value;
|
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 {
|
interface Props {
|
||||||
onUnlock(param: any): void;
|
onUnlock(param: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
dPath: DPath;
|
||||||
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
phrase: string;
|
phrase: string;
|
||||||
formattedPhrase: string;
|
formattedPhrase: string;
|
||||||
@ -18,13 +26,13 @@ interface State {
|
|||||||
dPath: string;
|
dPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MnemonicDecrypt extends Component<Props, State> {
|
class MnemonicDecryptClass extends Component<Props & StateProps, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
phrase: '',
|
phrase: '',
|
||||||
formattedPhrase: '',
|
formattedPhrase: '',
|
||||||
pass: '',
|
pass: '',
|
||||||
seed: '',
|
seed: '',
|
||||||
dPath: DEFAULT_PATH
|
dPath: this.props.dPath.value
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
@ -70,7 +78,7 @@ export class MnemonicDecrypt extends Component<Props, State> {
|
|||||||
isOpen={!!seed}
|
isOpen={!!seed}
|
||||||
seed={seed}
|
seed={seed}
|
||||||
dPath={dPath}
|
dPath={dPath}
|
||||||
dPaths={DPATHS.MNEMONIC}
|
dPaths={getPaths(InsecureWalletName.MNEMONIC_PHRASE)}
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
@ -135,3 +143,12 @@ export class MnemonicDecrypt extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState): StateProps {
|
||||||
|
const network = getNetworkConfig(state);
|
||||||
|
return {
|
||||||
|
dPath: getSingleDPath(InsecureWalletName.MNEMONIC_PHRASE, network)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MnemonicDecrypt = connect(mapStateToProps)(MnemonicDecryptClass);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import DPATHS from 'config/dpaths';
|
|
||||||
import { TrezorWallet, TREZOR_MINIMUM_FIRMWARE } from 'libs/wallet';
|
import { TrezorWallet, TREZOR_MINIMUM_FIRMWARE } from 'libs/wallet';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
@ -6,11 +5,23 @@ import TrezorConnect from 'vendor/trezor-connect';
|
|||||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||||
import './Trezor.scss';
|
import './Trezor.scss';
|
||||||
import { Spinner } from 'components/ui';
|
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;
|
onUnlock(param: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
dPath: DPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: nearly duplicates ledger component props
|
||||||
interface State {
|
interface State {
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
chainCode: string;
|
chainCode: string;
|
||||||
@ -19,11 +30,13 @@ interface State {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TrezorDecrypt extends Component<Props, State> {
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
|
class TrezorDecryptClass extends Component<Props, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: DEFAULT_PATH,
|
dPath: this.props.dPath.value,
|
||||||
error: null,
|
error: null,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
};
|
};
|
||||||
@ -76,7 +89,7 @@ export class TrezorDecrypt extends Component<Props, State> {
|
|||||||
publicKey={publicKey}
|
publicKey={publicKey}
|
||||||
chainCode={chainCode}
|
chainCode={chainCode}
|
||||||
dPath={dPath}
|
dPath={dPath}
|
||||||
dPaths={DPATHS.TREZOR}
|
dPaths={getPaths(SecureWalletName.TREZOR)}
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
onConfirmAddress={this.handleUnlock}
|
onConfirmAddress={this.handleUnlock}
|
||||||
onPathChange={this.handlePathChange}
|
onPathChange={this.handlePathChange}
|
||||||
@ -133,7 +146,16 @@ export class TrezorDecrypt extends Component<Props, State> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
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);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { donationAddressMap } from 'config/data';
|
import { donationAddressMap } from 'config';
|
||||||
import { isValidETHAddress } from 'libs/validators';
|
import { isValidETHAddress } from 'libs/validators';
|
||||||
import { AddressOnlyWallet } from 'libs/wallet';
|
import { AddressOnlyWallet } from 'libs/wallet';
|
||||||
|
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw, TranslateType } from 'translations';
|
||||||
import { NewTabLink, Tooltip } from 'components/ui';
|
import { NewTabLink, Tooltip } from 'components/ui';
|
||||||
import './WalletButton.scss';
|
import './WalletButton.scss';
|
||||||
|
|
||||||
interface Props {
|
import { WalletName } from 'config';
|
||||||
name: React.ReactElement<string> | string;
|
|
||||||
description?: React.ReactElement<string> | string;
|
interface OwnProps {
|
||||||
example?: React.ReactElement<string> | string;
|
name: TranslateType;
|
||||||
icon?: string | null;
|
description?: TranslateType;
|
||||||
helpLink?: string;
|
example?: TranslateType;
|
||||||
walletType: string;
|
icon?: string;
|
||||||
|
helpLink: string;
|
||||||
|
walletType: WalletName;
|
||||||
isSecure?: boolean;
|
isSecure?: boolean;
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
onClick(walletType: string): void;
|
onClick(walletType: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WalletButton extends React.Component<Props, {}> {
|
interface StateProps {
|
||||||
|
isFormatDisabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
|
export class WalletButton extends React.PureComponent<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
@ -45,27 +53,29 @@ export class WalletButton extends React.Component<Props, {}> {
|
|||||||
{icon && <img className="WalletButton-title-icon" src={icon} />}
|
{icon && <img className="WalletButton-title-icon" src={icon} />}
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{description && <div className="WalletButton-description">{description}</div>}
|
{description && <div className="WalletButton-description">{description}</div>}
|
||||||
{example && <div className="WalletButton-example">{example}</div>}
|
{example && <div className="WalletButton-example">{example}</div>}
|
||||||
|
|
||||||
<div className="WalletButton-icons">
|
<div className="WalletButton-icons">
|
||||||
{isSecure === true && (
|
{isSecure ? (
|
||||||
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
||||||
<i className="fa fa-shield" />
|
<i className="fa fa-shield" />
|
||||||
<Tooltip>{translateRaw('This wallet type is secure')}</Tooltip>
|
<Tooltip>{translateRaw('This wallet type is secure')}</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)}
|
) : (
|
||||||
{isSecure === false && (
|
|
||||||
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
||||||
<i className="fa fa-exclamation-triangle" />
|
<i className="fa fa-exclamation-triangle" />
|
||||||
<Tooltip>{translateRaw('This wallet type is insecure')}</Tooltip>
|
<Tooltip>{translateRaw('This wallet type is insecure')}</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{isReadOnly === true && (
|
{isReadOnly && (
|
||||||
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
<span className="WalletButton-icons-icon" onClick={this.stopPropogation}>
|
||||||
<i className="fa fa-eye" />
|
<i className="fa fa-eye" />
|
||||||
<Tooltip>{translateRaw('You cannot send using address only')}</Tooltip>
|
<Tooltip>{translateRaw('You cannot send using address only')}</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{helpLink && (
|
{helpLink && (
|
||||||
<span className="WalletButton-icons-icon">
|
<span className="WalletButton-icons-icon">
|
||||||
<NewTabLink href={helpLink} onClick={this.stopPropogation}>
|
<NewTabLink href={helpLink} onClick={this.stopPropogation}>
|
||||||
@ -80,14 +90,14 @@ export class WalletButton extends React.Component<Props, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleClick = () => {
|
private handleClick = () => {
|
||||||
if (this.props.isDisabled) {
|
if (this.props.isDisabled || this.props.isFormatDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onClick(this.props.walletType);
|
this.props.onClick(this.props.walletType);
|
||||||
};
|
};
|
||||||
|
|
||||||
private stopPropogation = (ev: React.SyntheticEvent<any>) => {
|
private stopPropogation = (ev: React.FormEvent<HTMLAnchorElement | HTMLSpanElement>) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"READ_ONLY": ["view-only"],
|
|
||||||
"UNABLE_TO_SIGN": ["trezor", "view-only"],
|
|
||||||
"ONLINE_ONLY": ["web3", "trezor"]
|
|
||||||
}
|
|
15
common/components/WalletDecrypt/disables.ts
Normal file
15
common/components/WalletDecrypt/disables.ts
Normal file
@ -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;
|
@ -1,3 +1,3 @@
|
|||||||
import WalletDecrypt from './WalletDecrypt';
|
import WalletDecrypt from './WalletDecrypt';
|
||||||
export default WalletDecrypt;
|
export default WalletDecrypt;
|
||||||
export { default as DISABLE_WALLETS } from './disables.json';
|
export { default as DISABLE_WALLETS } from './disables';
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import translate from 'translations';
|
import translate, { TranslateType } from 'translations';
|
||||||
import WalletDecrypt from 'components/WalletDecrypt';
|
import WalletDecrypt from 'components/WalletDecrypt';
|
||||||
import { IWallet } from 'libs/wallet/IWallet';
|
import { IWallet } from 'libs/wallet/IWallet';
|
||||||
import './UnlockHeader.scss';
|
import './UnlockHeader.scss';
|
||||||
|
import { WalletName } from 'config';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: React.ReactElement<string> | string;
|
title: TranslateType;
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
disabledWallets?: string[];
|
disabledWallets?: WalletName[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
}
|
}
|
||||||
export class UnlockHeader extends React.Component<Props, State> {
|
|
||||||
|
export class UnlockHeader extends React.PureComponent<Props, State> {
|
||||||
public state = {
|
public state = {
|
||||||
isExpanded: !this.props.wallet
|
isExpanded: !this.props.wallet
|
||||||
};
|
};
|
||||||
@ -57,7 +60,7 @@ export class UnlockHeader extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleisExpanded = () => {
|
public toggleisExpanded = (_: React.FormEvent<HTMLButtonElement>) => {
|
||||||
this.setState(state => {
|
this.setState(state => {
|
||||||
return { isExpanded: !state.isExpanded };
|
return { isExpanded: !state.isExpanded };
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ export function generateKindMax(BTCKINDRate: number, kind: keyof typeof buffers)
|
|||||||
return kindMax - kindMax * buffers[kind];
|
return kindMax - kindMax * buffers[kind];
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = {
|
export const bityConfig = {
|
||||||
serverURL,
|
serverURL,
|
||||||
bityURL,
|
bityURL,
|
||||||
ETHTxExplorer,
|
ETHTxExplorer,
|
||||||
@ -41,5 +41,3 @@ const info = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default info;
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { EtherscanNode, InfuraNode, RPCNode, Web3Node } from 'libs/nodes';
|
import { getValues } from '../utils/helpers';
|
||||||
import { networkIdToName } from 'libs/values';
|
|
||||||
export const languages = require('./languages.json');
|
export const languages = require('./languages.json');
|
||||||
|
|
||||||
// Displays in the header
|
// Displays in the header
|
||||||
export const VERSION = '4.0.0 (Alpha 0.1.0)';
|
export const VERSION = '4.0.0 (Alpha 0.1.0)';
|
||||||
export const N_FACTOR = 1024;
|
export const N_FACTOR = 1024;
|
||||||
@ -24,7 +25,7 @@ export const ANNOUNCEMENT_MESSAGE = `
|
|||||||
|
|
||||||
const etherScan = 'https://etherscan.io';
|
const etherScan = 'https://etherscan.io';
|
||||||
const blockChainInfo = 'https://blockchain.info';
|
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 ETHTxExplorer = (txHash: string): string => `${etherScan}/tx/${txHash}`;
|
||||||
export const BTCTxExplorer = (txHash: string): string => `${blockChainInfo}/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 trezorReferralURL = 'https://trezor.io/?a=myetherwallet.com';
|
||||||
export const bitboxReferralURL = 'https://digitalbitbox.com/?ref=mew';
|
export const bitboxReferralURL = 'https://digitalbitbox.com/?ref=mew';
|
||||||
|
|
||||||
export interface BlockExplorerConfig {
|
export enum SecureWalletName {
|
||||||
name: string;
|
WEB3 = 'web3',
|
||||||
tx(txHash: string): string;
|
LEDGER_NANO_S = 'ledgerNanoS',
|
||||||
address(address: string): string;
|
TREZOR = 'trezor'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Token {
|
export enum HardwareWalletName {
|
||||||
address: string;
|
LEDGER_NANO_S = 'ledgerNanoS',
|
||||||
symbol: string;
|
TREZOR = 'trezor'
|
||||||
decimal: number;
|
|
||||||
error?: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NetworkContract {
|
export enum InsecureWalletName {
|
||||||
name: string;
|
PRIVATE_KEY = 'privateKey',
|
||||||
address?: string;
|
KEYSTORE_FILE = 'keystoreFile',
|
||||||
abi: string;
|
MNEMONIC_PHRASE = 'mnemonicPhrase'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NetworkConfig {
|
export enum MiscWalletName {
|
||||||
name: string;
|
VIEW_ONLY = 'viewOnly'
|
||||||
unit: string;
|
|
||||||
color?: string;
|
|
||||||
blockExplorer?: BlockExplorerConfig;
|
|
||||||
tokenExplorer?: {
|
|
||||||
name: string;
|
|
||||||
address(address: string): string;
|
|
||||||
};
|
|
||||||
chainId: number;
|
|
||||||
tokens: Token[];
|
|
||||||
contracts: NetworkContract[] | null;
|
|
||||||
isTestnet?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomNetworkConfig {
|
export const walletNames = getValues(
|
||||||
name: string;
|
SecureWalletName,
|
||||||
unit: string;
|
HardwareWalletName,
|
||||||
chainId: number;
|
InsecureWalletName,
|
||||||
}
|
MiscWalletName
|
||||||
|
);
|
||||||
|
|
||||||
export interface NodeConfig {
|
export type WalletName = SecureWalletName | InsecureWalletName | MiscWalletName;
|
||||||
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<Web3NodeInfo> {
|
|
||||||
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<boolean> {
|
|
||||||
try {
|
|
||||||
await setupWeb3Node();
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function initWeb3Node(): Promise<void> {
|
|
||||||
const { networkId, lib } = await setupWeb3Node();
|
|
||||||
NODES.web3 = {
|
|
||||||
network: networkIdToName(networkId),
|
|
||||||
service: 'MetaMask / Mist',
|
|
||||||
lib,
|
|
||||||
estimateGas: false,
|
|
||||||
hidden: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -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)',
|
label: 'Default (ETH)',
|
||||||
value: "m/44'/60'/0'/0"
|
value: "m/44'/60'/0'/0"
|
||||||
};
|
};
|
||||||
|
|
||||||
const ETH_TREZOR = {
|
export const ETH_TREZOR: DPath = {
|
||||||
label: 'TREZOR (ETH)',
|
label: 'TREZOR (ETH)',
|
||||||
value: "m/44'/60'/0'/0"
|
value: "m/44'/60'/0'/0"
|
||||||
};
|
};
|
||||||
|
|
||||||
const ETH_LEDGER = {
|
export const ETH_LEDGER: DPath = {
|
||||||
label: 'Ledger (ETH)',
|
label: 'Ledger (ETH)',
|
||||||
value: "m/44'/60'/0'"
|
value: "m/44'/60'/0'"
|
||||||
};
|
};
|
||||||
|
|
||||||
const ETC_LEDGER = {
|
export const ETC_LEDGER: DPath = {
|
||||||
label: 'Ledger (ETC)',
|
label: 'Ledger (ETC)',
|
||||||
value: "m/44'/60'/160720'/0'"
|
value: "m/44'/60'/160720'/0'"
|
||||||
};
|
};
|
||||||
|
|
||||||
const ETC_TREZOR = {
|
export const ETC_TREZOR: DPath = {
|
||||||
label: 'TREZOR (ETC)',
|
label: 'TREZOR (ETC)',
|
||||||
value: "m/44'/61'/0'/0"
|
value: "m/44'/61'/0'/0"
|
||||||
};
|
};
|
||||||
|
|
||||||
const TESTNET = {
|
export const ETH_TESTNET: DPath = {
|
||||||
label: 'Testnet',
|
label: 'Testnet (ETH)',
|
||||||
value: "m/44'/1'/0'/0"
|
value: "m/44'/1'/0'/0"
|
||||||
};
|
};
|
||||||
|
|
||||||
const EXPANSE = {
|
export const EXP_DEFAULT: DPath = {
|
||||||
label: 'Expanse',
|
label: 'Default (EXP)',
|
||||||
value: "m/44'/40'/0'/0"
|
value: "m/44'/40'/0'/0"
|
||||||
};
|
};
|
||||||
|
|
||||||
const TREZOR = [ETH_TREZOR, ETC_TREZOR, TESTNET];
|
export const UBQ_DEFAULT: DPath = {
|
||||||
|
label: 'Default (UBQ)',
|
||||||
const MNEMONIC = [ETH_DEFAULT, ETH_LEDGER, ETC_LEDGER, ETC_TREZOR, TESTNET, EXPANSE];
|
value: "m/44'/108'/0'/0"
|
||||||
|
|
||||||
const LEDGER = [ETH_LEDGER, ETC_LEDGER, TESTNET];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
TREZOR,
|
|
||||||
MNEMONIC,
|
|
||||||
LEDGER
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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];
|
||||||
|
3
common/config/index.ts
Normal file
3
common/config/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './networks';
|
||||||
|
export * from './data';
|
||||||
|
export * from './bity';
|
364
common/config/networks.ts
Normal file
364
common/config/networks.ts
Normal file
@ -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<Web3NodeInfo> {
|
||||||
|
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<boolean> {
|
||||||
|
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<void> {
|
||||||
|
const { networkId, lib } = await setupWeb3Node();
|
||||||
|
const web3: NodeConfigOverride = {
|
||||||
|
network: networkIdToName(networkId),
|
||||||
|
service: Web3Service,
|
||||||
|
lib,
|
||||||
|
estimateGas: false,
|
||||||
|
hidden: true
|
||||||
|
};
|
||||||
|
|
||||||
|
NODES.web3 = web3;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { NetworkContract } from 'config/data';
|
import { NetworkContract } from 'config';
|
||||||
import { getNetworkContracts } from 'selectors/config';
|
import { getNetworkContracts } from 'selectors/config';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { NewTabLink } from 'components/ui';
|
import { NewTabLink } from 'components/ui';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import GeneralInfoNode from './GeneralInfoNode';
|
import GeneralInfoNode from './GeneralInfoNode';
|
||||||
import { knowledgeBaseURL } from 'config/data';
|
import { knowledgeBaseURL } from 'config';
|
||||||
import { InfoNode } from './types';
|
import { InfoNode } from './types';
|
||||||
|
|
||||||
const generalInfoNodes: InfoNode[] = [
|
const generalInfoNodes: InfoNode[] = [
|
||||||
|
@ -5,7 +5,7 @@ import translate from 'translations';
|
|||||||
import { makeBlob } from 'utils/blob';
|
import { makeBlob } from 'utils/blob';
|
||||||
import './DownloadWallet.scss';
|
import './DownloadWallet.scss';
|
||||||
import Template from '../Template';
|
import Template from '../Template';
|
||||||
import { N_FACTOR } from 'config/data';
|
import { N_FACTOR } from 'config';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IFullWallet;
|
wallet: IFullWallet;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { MINIMUM_PASSWORD_LENGTH } from 'config/data';
|
import { MINIMUM_PASSWORD_LENGTH } from 'config';
|
||||||
import './EnterPassword.scss';
|
import './EnterPassword.scss';
|
||||||
import PasswordInput from './PasswordInput';
|
import PasswordInput from './PasswordInput';
|
||||||
import Template from '../Template';
|
import Template from '../Template';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import TabSection from 'containers/TabSection';
|
import TabSection from 'containers/TabSection';
|
||||||
import { knowledgeBaseURL } from 'config/data';
|
import { knowledgeBaseURL } from 'config';
|
||||||
|
|
||||||
const Help = () => (
|
const Help = () => (
|
||||||
<TabSection>
|
<TabSection>
|
||||||
|
@ -7,7 +7,7 @@ import { isValidPrivKey } from 'libs/validators';
|
|||||||
import { stripHexPrefix } from 'libs/values';
|
import { stripHexPrefix } from 'libs/values';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './KeystoreDetails.scss';
|
import './KeystoreDetails.scss';
|
||||||
import { N_FACTOR } from 'config/data';
|
import { N_FACTOR } from 'config';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { donationAddressMap } from 'config/data';
|
import { donationAddressMap } from 'config';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
ICurrentValue
|
ICurrentValue
|
||||||
} from 'selectors/transaction/current';
|
} from 'selectors/transaction/current';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { validNumber, validDecimal } from 'libs/validators';
|
import { validNumber, validDecimal } from 'libs/validators';
|
||||||
import { getGasLimit } from 'selectors/transaction';
|
import { getGasLimit } from 'selectors/transaction';
|
||||||
import { AddressField, AmountField, GasLimitField } from 'components';
|
import { AddressField, AmountField, GasLimitField } from 'components';
|
||||||
|
@ -3,24 +3,28 @@ import { connect } from 'react-redux';
|
|||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import TabSection from 'containers/TabSection';
|
import TabSection from 'containers/TabSection';
|
||||||
import { UnlockHeader } from 'components/ui';
|
import { UnlockHeader } from 'components/ui';
|
||||||
import { SideBar } from './components/index';
|
import { SideBar } from './components';
|
||||||
import { IReadOnlyWallet, IFullWallet } from 'libs/wallet';
|
import { IReadOnlyWallet, IFullWallet } from 'libs/wallet';
|
||||||
import { getWalletInst } from 'selectors/wallet';
|
import { getWalletInst } from 'selectors/wallet';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import tabs from './tabs';
|
import tabs from './tabs';
|
||||||
import SubTabs, { Props as TabProps } from 'components/SubTabs';
|
import SubTabs, { Props as TabProps } from 'components/SubTabs';
|
||||||
import { RouteComponentProps } from 'react-router';
|
import { RouteComponentProps } from 'react-router';
|
||||||
|
import { NetworkConfig } from 'config';
|
||||||
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
|
|
||||||
|
export type WalletTypes = IReadOnlyWallet | IFullWallet | undefined | null;
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
wallet: AppState['wallet']['inst'];
|
wallet: AppState['wallet']['inst'];
|
||||||
|
network: AppState['config']['network'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SubTabProps {
|
export interface SubTabProps {
|
||||||
wallet: WalletTypes;
|
wallet: WalletTypes;
|
||||||
|
network: NetworkConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WalletTypes = IReadOnlyWallet | IFullWallet | undefined | null;
|
|
||||||
|
|
||||||
type Props = StateProps & RouteComponentProps<{}>;
|
type Props = StateProps & RouteComponentProps<{}>;
|
||||||
|
|
||||||
const determineActiveTab = (wallet: WalletTypes, activeTab: string) => {
|
const determineActiveTab = (wallet: WalletTypes, activeTab: string) => {
|
||||||
@ -33,7 +37,7 @@ const determineActiveTab = (wallet: WalletTypes, activeTab: string) => {
|
|||||||
|
|
||||||
class SendTransaction extends React.Component<Props> {
|
class SendTransaction extends React.Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const { wallet, location } = this.props;
|
const { wallet, location, network } = this.props;
|
||||||
const activeTab = location.pathname.split('/')[2];
|
const activeTab = location.pathname.split('/')[2];
|
||||||
|
|
||||||
const tabProps: TabProps<SubTabProps> = {
|
const tabProps: TabProps<SubTabProps> = {
|
||||||
@ -41,7 +45,7 @@ class SendTransaction extends React.Component<Props> {
|
|||||||
activeTab: determineActiveTab(wallet, activeTab),
|
activeTab: determineActiveTab(wallet, activeTab),
|
||||||
sideBar: <SideBar />,
|
sideBar: <SideBar />,
|
||||||
tabs,
|
tabs,
|
||||||
subTabProps: { wallet }
|
subTabProps: { wallet, network }
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IWalletTabs {
|
interface IWalletTabs {
|
||||||
@ -61,4 +65,7 @@ class SendTransaction extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect((state: AppState) => ({ wallet: getWalletInst(state) }))(SendTransaction);
|
export default connect((state: AppState) => ({
|
||||||
|
wallet: getWalletInst(state),
|
||||||
|
network: getNetworkConfig(state)
|
||||||
|
}))(SendTransaction);
|
||||||
|
@ -5,6 +5,7 @@ import translate from 'translations';
|
|||||||
import { Fields, UnavailableWallets, WalletInfo, RequestPayment } from './components/index';
|
import { Fields, UnavailableWallets, WalletInfo, RequestPayment } from './components/index';
|
||||||
import { Tab } from 'components/SubTabs';
|
import { Tab } from 'components/SubTabs';
|
||||||
import { SubTabProps } from 'containers/Tabs/SendTransaction';
|
import { SubTabProps } from 'containers/Tabs/SendTransaction';
|
||||||
|
import { isNetworkUnit } from 'utils/network';
|
||||||
|
|
||||||
const SendTab: Tab<SubTabProps> = {
|
const SendTab: Tab<SubTabProps> = {
|
||||||
path: 'send',
|
path: 'send',
|
||||||
@ -41,6 +42,10 @@ const InfoTab: Tab<SubTabProps> = {
|
|||||||
const RequestTab: Tab<SubTabProps> = {
|
const RequestTab: Tab<SubTabProps> = {
|
||||||
path: 'request',
|
path: 'request',
|
||||||
name: translate('Request Payment'),
|
name: translate('Request Payment'),
|
||||||
|
isDisabled: (props: SubTabProps) => {
|
||||||
|
const isETHNetwork = isNetworkUnit(props.network, 'ETH');
|
||||||
|
return !isETHNetwork;
|
||||||
|
},
|
||||||
render(props: SubTabProps) {
|
render(props: SubTabProps) {
|
||||||
const content = props && props.wallet ? <RequestPayment wallet={props.wallet} /> : null;
|
const content = props && props.wallet ? <RequestPayment wallet={props.wallet} /> : null;
|
||||||
return <div>{content}</div>;
|
return <div>{content}</div>;
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
SwapInput
|
SwapInput
|
||||||
} from 'reducers/swap/types';
|
} from 'reducers/swap/types';
|
||||||
import SimpleButton from 'components/ui/SimpleButton';
|
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 React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { combineAndUpper } from 'utils/formatters';
|
import { combineAndUpper } from 'utils/formatters';
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
import bityLogoWhite from 'assets/images/logo-bity-white.svg';
|
import bityLogoWhite from 'assets/images/logo-bity-white.svg';
|
||||||
import shapeshiftLogoWhite from 'assets/images/logo-shapeshift.svg';
|
import shapeshiftLogoWhite from 'assets/images/logo-shapeshift.svg';
|
||||||
import Spinner from 'components/ui/Spinner';
|
import Spinner from 'components/ui/Spinner';
|
||||||
import { bityReferralURL, shapeshiftReferralURL } from 'config/data';
|
import { bityReferralURL, shapeshiftReferralURL } from 'config';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './CurrentRates.scss';
|
import './CurrentRates.scss';
|
||||||
|
@ -8,7 +8,7 @@ import { configureLiteSend, TConfigureLiteSend } from 'actions/swap';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { shouldDisplayLiteSend } from 'selectors/swap';
|
import { shouldDisplayLiteSend } from 'selectors/swap';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
configureLiteSend: TConfigureLiteSend;
|
configureLiteSend: TConfigureLiteSend;
|
||||||
@ -36,10 +36,7 @@ class LiteSendClass extends Component<Props> {
|
|||||||
renderMe = (
|
renderMe = (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-xs-8 col-xs-push-2 text-center">
|
<div className="col-xs-8 col-xs-push-2 text-center">
|
||||||
<h5 style={{ color: 'red' }}>
|
<h5>Note: Send is only supported on Ethereum Mainnet.</h5>
|
||||||
WARNING: You are currently not on the Ethereum Mainnet. Please switch nodes in order
|
|
||||||
for the token swap to function as intended.
|
|
||||||
</h5>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
import { SwapInput } from 'reducers/swap/types';
|
import { SwapInput } from 'reducers/swap/types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import SimpleButton from 'components/ui/SimpleButton';
|
import SimpleButton from 'components/ui/SimpleButton';
|
||||||
import { donationAddressMap } from 'config/data';
|
import { donationAddressMap } from 'config';
|
||||||
import { isValidBTCAddress, isValidETHAddress } from 'libs/validators';
|
import { isValidBTCAddress, isValidETHAddress } from 'libs/validators';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { RestartSwapAction } from 'actions/swap';
|
import { RestartSwapAction } from 'actions/swap';
|
||||||
import bityLogo from 'assets/images/logo-bity.svg';
|
import bityLogo from 'assets/images/logo-bity.svg';
|
||||||
import shapeshiftLogo from 'assets/images/shapeshift-dark.svg';
|
import shapeshiftLogo from 'assets/images/shapeshift-dark.svg';
|
||||||
import { bityReferralURL } from 'config/data';
|
import { bityReferralURL } from 'config';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import './SwapInfoHeader.scss';
|
import './SwapInfoHeader.scss';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TShowNotification } from 'actions/notifications';
|
import { TShowNotification } from 'actions/notifications';
|
||||||
import bityConfig from 'config/bity';
|
import { bityConfig } from 'config/bity';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import './SwapProgress.scss';
|
import './SwapProgress.scss';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { Wei, TokenValue } from 'libs/units';
|
import { Wei, TokenValue } from 'libs/units';
|
||||||
import { IHexStrTransaction } from 'libs/transaction';
|
import { IHexStrTransaction } from 'libs/transaction';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import RPCNode from '../rpc';
|
import RPCNode from '../rpc';
|
||||||
import RPCClient from '../rpc/client';
|
import RPCClient from '../rpc/client';
|
||||||
import { CustomNodeConfig } from 'config/data';
|
import { CustomNodeConfig } from 'config';
|
||||||
|
|
||||||
export default class CustomNode extends RPCNode {
|
export default class CustomNode extends RPCNode {
|
||||||
constructor(config: CustomNodeConfig) {
|
constructor(config: CustomNodeConfig) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import ERC20 from 'libs/erc20';
|
import ERC20 from 'libs/erc20';
|
||||||
import RPCRequests from '../rpc/requests';
|
import RPCRequests from '../rpc/requests';
|
||||||
import {
|
import {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { IHexStrTransaction } from 'libs/transaction';
|
import { IHexStrTransaction } from 'libs/transaction';
|
||||||
import { Wei, TokenValue } from 'libs/units';
|
import { Wei, TokenValue } from 'libs/units';
|
||||||
import { stripHexPrefix } from 'libs/values';
|
import { stripHexPrefix } from 'libs/values';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import ERC20 from 'libs/erc20';
|
import ERC20 from 'libs/erc20';
|
||||||
import {
|
import {
|
||||||
CallRequest,
|
CallRequest,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Wei, toTokenBase } from 'libs/units';
|
import { Wei, toTokenBase } from 'libs/units';
|
||||||
import { addHexPrefix } from 'ethereumjs-util';
|
import { addHexPrefix } from 'ethereumjs-util';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
import { NetworkKeys } from 'config';
|
||||||
|
|
||||||
export function stripHexPrefix(value: string) {
|
export function stripHexPrefix(value: string) {
|
||||||
return value.replace('0x', '');
|
return value.replace('0x', '');
|
||||||
@ -23,7 +24,7 @@ export function sanitizeHex(hex: string) {
|
|||||||
return hex !== '' ? `0x${padLeftEven(hexStr)}` : '';
|
return hex !== '' ? `0x${padLeftEven(hexStr)}` : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function networkIdToName(networkId: string | number): string {
|
export function networkIdToName(networkId: string | number): NetworkKeys {
|
||||||
switch (networkId.toString()) {
|
switch (networkId.toString()) {
|
||||||
case '1':
|
case '1':
|
||||||
return 'ETH';
|
return 'ETH';
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
CustomNodeConfig,
|
CustomNodeConfig,
|
||||||
NetworkConfig,
|
NetworkConfig,
|
||||||
CustomNetworkConfig
|
CustomNetworkConfig
|
||||||
} from '../config/data';
|
} from 'config';
|
||||||
import { makeCustomNodeId } from 'utils/node';
|
import { makeCustomNodeId } from 'utils/node';
|
||||||
import { makeCustomNetworkId } from 'utils/network';
|
import { makeCustomNetworkId } from 'utils/network';
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
RemoveCustomTokenAction
|
RemoveCustomTokenAction
|
||||||
} from 'actions/customTokens';
|
} from 'actions/customTokens';
|
||||||
import { TypeKeys } from 'actions/customTokens/constants';
|
import { TypeKeys } from 'actions/customTokens/constants';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
|
|
||||||
export type State = Token[];
|
export type State = Token[];
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Option } from 'actions/swap/actionTypes';
|
import { Option } from 'actions/swap/actionTypes';
|
||||||
import { WhitelistedCoins } from 'config/bity';
|
import { WhitelistedCoins } from 'config';
|
||||||
|
|
||||||
export interface SwapInput {
|
export interface SwapInput {
|
||||||
id: WhitelistedCoins;
|
id: WhitelistedCoins;
|
||||||
|
@ -10,7 +10,14 @@ import {
|
|||||||
select,
|
select,
|
||||||
race
|
race
|
||||||
} from 'redux-saga/effects';
|
} from 'redux-saga/effects';
|
||||||
import { NODES, NETWORKS, NodeConfig, CustomNodeConfig, CustomNetworkConfig } from 'config/data';
|
import {
|
||||||
|
NODES,
|
||||||
|
NETWORKS,
|
||||||
|
NodeConfig,
|
||||||
|
CustomNodeConfig,
|
||||||
|
CustomNetworkConfig,
|
||||||
|
Web3Service
|
||||||
|
} from 'config';
|
||||||
import {
|
import {
|
||||||
makeCustomNodeId,
|
makeCustomNodeId,
|
||||||
getCustomNodeConfigFromId,
|
getCustomNodeConfigFromId,
|
||||||
@ -37,12 +44,9 @@ import {
|
|||||||
} from 'actions/config';
|
} from 'actions/config';
|
||||||
import { showNotification } from 'actions/notifications';
|
import { showNotification } from 'actions/notifications';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
import { IWallet, Web3Wallet } from 'libs/wallet';
|
import { Web3Wallet } from 'libs/wallet';
|
||||||
import { getWalletInst } from 'selectors/wallet';
|
|
||||||
import { TypeKeys as WalletTypeKeys } from 'actions/wallet/constants';
|
import { TypeKeys as WalletTypeKeys } from 'actions/wallet/constants';
|
||||||
import { State as ConfigState, INITIAL_STATE as configInitialState } from 'reducers/config';
|
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;
|
export const getConfig = (state: AppState): ConfigState => state.config;
|
||||||
|
|
||||||
@ -171,12 +175,18 @@ export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIte
|
|||||||
yield put(setLatestBlock(latestBlock));
|
yield put(setLatestBlock(latestBlock));
|
||||||
yield put(changeNode(action.payload, actionConfig, actionNetwork));
|
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 there's no wallet, do not reload as there's no component state to resync
|
||||||
if (currentWallet && currentConfig.network !== actionConfig.network) {
|
// if (currentWallet && currentConfig.network !== actionConfig.network) {
|
||||||
yield put(resetWallet());
|
|
||||||
yield put(resetTransaction());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
updateDeterministicWallet
|
updateDeterministicWallet
|
||||||
} from 'actions/deterministicWallets';
|
} from 'actions/deterministicWallets';
|
||||||
import { showNotification } from 'actions/notifications';
|
import { showNotification } from 'actions/notifications';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { publicToAddress, toChecksumAddress } from 'ethereumjs-util';
|
import { publicToAddress, toChecksumAddress } from 'ethereumjs-util';
|
||||||
import HDKey from 'hdkey';
|
import HDKey from 'hdkey';
|
||||||
import { INode } from 'libs/nodes/INode';
|
import { INode } from 'libs/nodes/INode';
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
TypeKeys as TK
|
TypeKeys as TK
|
||||||
} from 'actions/transaction';
|
} from 'actions/transaction';
|
||||||
import Tx from 'ethereumjs-tx';
|
import Tx from 'ethereumjs-tx';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config';
|
||||||
import { SagaIterator } from 'redux-saga';
|
import { SagaIterator } from 'redux-saga';
|
||||||
import { showNotification } from 'actions/notifications';
|
import { showNotification } from 'actions/notifications';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { apply, select, call } from 'redux-saga/effects';
|
import { apply, select, call } from 'redux-saga/effects';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { INode } from 'libs/nodes/INode';
|
import { INode } from 'libs/nodes/INode';
|
||||||
import { IWallet, WalletConfig } from 'libs/wallet';
|
import { IWallet, WalletConfig } from 'libs/wallet';
|
||||||
import { TokenBalance } from 'selectors/wallet';
|
import { TokenBalance } from 'selectors/wallet';
|
||||||
|
@ -36,7 +36,7 @@ import {
|
|||||||
Web3Wallet,
|
Web3Wallet,
|
||||||
WalletConfig
|
WalletConfig
|
||||||
} from 'libs/wallet';
|
} from 'libs/wallet';
|
||||||
import { NODES, initWeb3Node, Token } from 'config/data';
|
import { NODES, initWeb3Node, Token } from 'config';
|
||||||
import { SagaIterator, delay, Task } from 'redux-saga';
|
import { SagaIterator, delay, Task } from 'redux-saga';
|
||||||
import { apply, call, fork, put, select, takeEvery, take, cancel } from 'redux-saga/effects';
|
import { apply, call, fork, put, select, takeEvery, take, cancel } from 'redux-saga/effects';
|
||||||
import { getNodeLib, getAllTokens, getOffline } from 'selectors/config';
|
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'
|
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 network = NODES.web3.network;
|
||||||
const nodeLib: INode | Web3Node = yield select(getNodeLib);
|
const nodeLib: INode | Web3Node = yield select(getNodeLib);
|
||||||
|
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
// --- RC SLIDER ---
|
// --- RC SLIDER ---
|
||||||
@import "~rc-slider/assets/index.css";
|
@import "~rc-slider/assets/index.css";
|
||||||
|
|
||||||
|
// --- React Select ---
|
||||||
|
@import '~react-select/dist/react-select.css';
|
||||||
|
|
||||||
// --- CUSTOM ---
|
// --- CUSTOM ---
|
||||||
@import "./styles/badbrowser";
|
@import "./styles/badbrowser";
|
||||||
@import "./styles/noscript";
|
@import "./styles/noscript";
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
|
CustomNetworkConfig,
|
||||||
|
CustomNodeConfig,
|
||||||
NetworkConfig,
|
NetworkConfig,
|
||||||
NetworkContract,
|
NetworkContract,
|
||||||
NodeConfig,
|
NodeConfig,
|
||||||
CustomNodeConfig,
|
|
||||||
CustomNetworkConfig,
|
|
||||||
Token
|
Token
|
||||||
} from 'config/data';
|
} from 'config';
|
||||||
import { INode } from 'libs/nodes/INode';
|
import { INode } from 'libs/nodes/INode';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { getUnit } from 'selectors/transaction/meta';
|
import { getUnit } from 'selectors/transaction/meta';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TokenValue, Wei } from 'libs/units';
|
import { TokenValue, Wei } from 'libs/units';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { getNetworkConfig } from 'selectors/config';
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
import { IWallet, Web3Wallet, LedgerWallet, TrezorWallet, WalletConfig } from 'libs/wallet';
|
import { IWallet, Web3Wallet, LedgerWallet, TrezorWallet, WalletConfig } from 'libs/wallet';
|
||||||
|
@ -22,3 +22,6 @@ export function getParam(query: { [key: string]: string }, key: string) {
|
|||||||
export function isPositiveInteger(n: number) {
|
export function isPositiveInteger(n: number) {
|
||||||
return Number.isInteger(n) && n > 0;
|
return Number.isInteger(n) && n > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getValues = (...args) =>
|
||||||
|
args.reduce((acc, currArg) => [...acc, ...Object.values(currArg)], []);
|
||||||
|
@ -2,7 +2,7 @@ import { fromPrivateKey, IFullWallet, fromV3 } from 'ethereumjs-wallet';
|
|||||||
import { isValidPrivKey } from 'libs/validators';
|
import { isValidPrivKey } from 'libs/validators';
|
||||||
import { stripHexPrefix } from 'libs/values';
|
import { stripHexPrefix } from 'libs/values';
|
||||||
import { makeBlob } from 'utils/blob';
|
import { makeBlob } from 'utils/blob';
|
||||||
import { N_FACTOR } from 'config/data';
|
import { N_FACTOR } from 'config';
|
||||||
|
|
||||||
export interface KeystoreFile {
|
export interface KeystoreFile {
|
||||||
filename: string;
|
filename: string;
|
||||||
|
@ -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 {
|
export function makeCustomNetworkId(config: CustomNetworkConfig): string {
|
||||||
return config.chainId ? `${config.chainId}` : `${config.name}:${config.unit}`;
|
return config.chainId ? `${config.chainId}` : `${config.name}:${config.unit}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeNetworkConfigFromCustomConfig(config: CustomNetworkConfig): NetworkConfig {
|
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,
|
...config,
|
||||||
color: '#000',
|
color: '#000',
|
||||||
tokens: [],
|
tokens: [],
|
||||||
contracts: []
|
contracts: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return customConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNetworkConfigFromId(
|
export function getNetworkConfigFromId(
|
||||||
@ -26,3 +53,68 @@ export function getNetworkConfigFromId(
|
|||||||
return makeNetworkConfigFromCustomConfig(customConfig);
|
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);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CustomNode } from 'libs/nodes';
|
import { CustomNode } from 'libs/nodes';
|
||||||
import { NODES, NodeConfig, CustomNodeConfig } from 'config/data';
|
import { NODES, NodeConfig, CustomNodeConfig } from 'config';
|
||||||
|
|
||||||
export function makeCustomNodeId(config: CustomNodeConfig): string {
|
export function makeCustomNodeId(config: CustomNodeConfig): string {
|
||||||
return `${config.url}:${config.port}`;
|
return `${config.url}:${config.port}`;
|
||||||
@ -27,10 +27,16 @@ export function getNodeConfigFromId(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function makeNodeConfigFromCustomConfig(config: CustomNodeConfig): NodeConfig {
|
export function makeNodeConfigFromCustomConfig(config: CustomNodeConfig): NodeConfig {
|
||||||
return {
|
interface Override extends NodeConfig {
|
||||||
|
network: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customConfig: Override = {
|
||||||
network: config.network,
|
network: config.network,
|
||||||
lib: new CustomNode(config),
|
lib: new CustomNode(config),
|
||||||
service: 'your custom node',
|
service: 'your custom node',
|
||||||
estimateGas: true
|
estimateGas: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return customConfig;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
|
|
||||||
export function dedupeCustomTokens(networkTokens: Token[], customTokens: Token[]): Token[] {
|
export function dedupeCustomTokens(networkTokens: Token[], customTokens: Token[]): Token[] {
|
||||||
if (!customTokens.length) {
|
if (!customTokens.length) {
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
"react-redux": "5.0.6",
|
"react-redux": "5.0.6",
|
||||||
"react-router-dom": "4.2.2",
|
"react-router-dom": "4.2.2",
|
||||||
"react-router-redux": "4.0.8",
|
"react-router-redux": "4.0.8",
|
||||||
|
"react-select": "1.2.1",
|
||||||
"react-stepper-horizontal": "1.0.9",
|
"react-stepper-horizontal": "1.0.9",
|
||||||
"react-transition-group": "2.2.1",
|
"react-transition-group": "2.2.1",
|
||||||
"redux": "3.7.2",
|
"redux": "3.7.2",
|
||||||
@ -62,6 +63,7 @@
|
|||||||
"@types/react-redux": "5.0.14",
|
"@types/react-redux": "5.0.14",
|
||||||
"@types/react-router-dom": "4.2.3",
|
"@types/react-router-dom": "4.2.3",
|
||||||
"@types/react-router-redux": "5.0.11",
|
"@types/react-router-redux": "5.0.11",
|
||||||
|
"@types/react-select": "1.1.0",
|
||||||
"@types/redux-logger": "3.0.5",
|
"@types/redux-logger": "3.0.5",
|
||||||
"@types/redux-promise-middleware": "0.0.9",
|
"@types/redux-promise-middleware": "0.0.9",
|
||||||
"@types/uuid": "3.4.3",
|
"@types/uuid": "3.4.3",
|
||||||
|
19
spec/config/networks.spec.ts
Normal file
19
spec/config/networks.spec.ts
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,4 @@
|
|||||||
import { NODES, NodeConfig } from '../../common/config/data';
|
import { NODES, NodeConfig } from 'config';
|
||||||
import { RPCNode } from '../../common/libs/nodes';
|
import { RPCNode } from '../../common/libs/nodes';
|
||||||
import { Validator } from 'jsonschema';
|
import { Validator } from 'jsonschema';
|
||||||
import { schema } from '../../common/libs/validators';
|
import { schema } from '../../common/libs/validators';
|
||||||
|
@ -4,7 +4,7 @@ import Adapter from 'enzyme-adapter-react-16';
|
|||||||
import SendTransaction from 'containers/Tabs/SendTransaction';
|
import SendTransaction from 'containers/Tabs/SendTransaction';
|
||||||
import shallowWithStore from '../utils/shallowWithStore';
|
import shallowWithStore from '../utils/shallowWithStore';
|
||||||
import { createMockStore } from 'redux-test-utils';
|
import { createMockStore } from 'redux-test-utils';
|
||||||
import { NODES } from 'config/data';
|
import { NODES } from 'config';
|
||||||
import { RouteComponentProps } from 'react-router';
|
import { RouteComponentProps } from 'react-router';
|
||||||
import { createMockRouteComponentProps } from '../utils/mockRouteComponentProps';
|
import { createMockRouteComponentProps } from '../utils/mockRouteComponentProps';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { config, INITIAL_STATE } from 'reducers/config';
|
import { config, INITIAL_STATE } from 'reducers/config';
|
||||||
import * as configActions from 'actions/config';
|
import * as configActions from 'actions/config';
|
||||||
import { NODES, NETWORKS } from 'config/data';
|
import { NODES, NETWORKS } from 'config';
|
||||||
import { makeCustomNodeId, makeNodeConfigFromCustomConfig } from 'utils/node';
|
import { makeCustomNodeId, makeNodeConfigFromCustomConfig } from 'utils/node';
|
||||||
|
|
||||||
const custNode = {
|
const custNode = {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { customTokens } from 'reducers/customTokens';
|
import { customTokens } from 'reducers/customTokens';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import * as customTokensActions from 'actions/customTokens';
|
import * as customTokensActions from 'actions/customTokens';
|
||||||
|
|
||||||
describe('customTokens reducer', () => {
|
describe('customTokens reducer', () => {
|
||||||
|
@ -9,9 +9,10 @@ import {
|
|||||||
handleNodeChangeIntent,
|
handleNodeChangeIntent,
|
||||||
unsetWeb3Node,
|
unsetWeb3Node,
|
||||||
unsetWeb3NodeOnWalletEvent,
|
unsetWeb3NodeOnWalletEvent,
|
||||||
equivalentNodeOrDefault
|
equivalentNodeOrDefault,
|
||||||
|
reload
|
||||||
} from 'sagas/config';
|
} from 'sagas/config';
|
||||||
import { NODES, NodeConfig, NETWORKS } from 'config/data';
|
import { NODES, NodeConfig, NETWORKS } from 'config';
|
||||||
import {
|
import {
|
||||||
getNode,
|
getNode,
|
||||||
getNodeConfig,
|
getNodeConfig,
|
||||||
@ -20,13 +21,10 @@ import {
|
|||||||
getCustomNetworkConfigs
|
getCustomNetworkConfigs
|
||||||
} from 'selectors/config';
|
} from 'selectors/config';
|
||||||
import { INITIAL_STATE as configInitialState } from 'reducers/config';
|
import { INITIAL_STATE as configInitialState } from 'reducers/config';
|
||||||
import { getWalletInst } from 'selectors/wallet';
|
|
||||||
import { Web3Wallet } from 'libs/wallet';
|
import { Web3Wallet } from 'libs/wallet';
|
||||||
import { RPCNode } from 'libs/nodes';
|
import { RPCNode } from 'libs/nodes';
|
||||||
import { showNotification } from 'actions/notifications';
|
import { showNotification } from 'actions/notifications';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
import { resetWallet } from 'actions/wallet';
|
|
||||||
import { reset as resetTransaction } from 'actions/transaction';
|
|
||||||
// init module
|
// init module
|
||||||
configuredStore.getState();
|
configuredStore.getState();
|
||||||
|
|
||||||
@ -143,12 +141,12 @@ describe('handleNodeChangeIntent*', () => {
|
|||||||
const customNetworkConfigs = [];
|
const customNetworkConfigs = [];
|
||||||
const defaultNodeNetwork = NETWORKS[defaultNodeConfig.network];
|
const defaultNodeNetwork = NETWORKS[defaultNodeConfig.network];
|
||||||
const newNode = Object.keys(NODES).reduce(
|
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 newNodeConfig = NODES[newNode];
|
||||||
const newNodeNetwork = NETWORKS[newNodeConfig.network];
|
const newNodeNetwork = NETWORKS[newNodeConfig.network];
|
||||||
|
|
||||||
const changeNodeIntentAction = changeNodeIntent(newNode);
|
const changeNodeIntentAction = changeNodeIntent(newNode);
|
||||||
const truthyWallet = true;
|
|
||||||
const latestBlock = '0xa';
|
const latestBlock = '0xa';
|
||||||
const raceSuccess = {
|
const raceSuccess = {
|
||||||
lb: latestBlock
|
lb: latestBlock
|
||||||
@ -208,15 +206,9 @@ describe('handleNodeChangeIntent*', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select getWalletInst', () => {
|
it('should call reload if network is new', () => {
|
||||||
expect(data.gen.next().value).toEqual(select(getWalletInst));
|
expect(data.gen.next().value).toEqual(call(reload));
|
||||||
});
|
expect(data.gen.next().done).toEqual(true);
|
||||||
|
|
||||||
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 be done', () => {
|
it('should be done', () => {
|
||||||
@ -366,7 +358,7 @@ describe('equivalentNodeOrDefault', () => {
|
|||||||
const node = equivalentNodeOrDefault({
|
const node = equivalentNodeOrDefault({
|
||||||
...mockNodeConfig,
|
...mockNodeConfig,
|
||||||
network: 'noEqivalentExists'
|
network: 'noEqivalentExists'
|
||||||
});
|
} as any);
|
||||||
expect(node).toEqual(appDefaultNode);
|
expect(node).toEqual(appDefaultNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -374,12 +366,12 @@ describe('equivalentNodeOrDefault', () => {
|
|||||||
NODES.web3 = {
|
NODES.web3 = {
|
||||||
...mockNodeConfig,
|
...mockNodeConfig,
|
||||||
network: 'uniqueToWeb3'
|
network: 'uniqueToWeb3'
|
||||||
};
|
} as any;
|
||||||
|
|
||||||
const node = equivalentNodeOrDefault({
|
const node = equivalentNodeOrDefault({
|
||||||
...mockNodeConfig,
|
...mockNodeConfig,
|
||||||
network: 'uniqueToWeb3'
|
network: 'uniqueToWeb3'
|
||||||
});
|
} as any);
|
||||||
expect(node).toEqual(appDefaultNode);
|
expect(node).toEqual(appDefaultNode);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@ import { getDesiredToken, getWallets } from 'selectors/deterministicWallets';
|
|||||||
import { getTokens } from 'selectors/wallet';
|
import { getTokens } from 'selectors/wallet';
|
||||||
import { getNodeLib } from 'selectors/config';
|
import { getNodeLib } from 'selectors/config';
|
||||||
import * as dWalletActions from 'actions/deterministicWallets';
|
import * as dWalletActions from 'actions/deterministicWallets';
|
||||||
import { Token } from 'config/data';
|
import { Token } from 'config';
|
||||||
import {
|
import {
|
||||||
getDeterministicWallets,
|
getDeterministicWallets,
|
||||||
updateWalletValues,
|
updateWalletValues,
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
import { Wei } from 'libs/units';
|
import { Wei } from 'libs/units';
|
||||||
import { changeNodeIntent, web3UnsetNode } from 'actions/config';
|
import { changeNodeIntent, web3UnsetNode } from 'actions/config';
|
||||||
import { INode } from 'libs/nodes/INode';
|
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 { apply, call, fork, put, select, take } from 'redux-saga/effects';
|
||||||
import { getNodeLib, getOffline } from 'selectors/config';
|
import { getNodeLib, getOffline } from 'selectors/config';
|
||||||
import { getWalletInst, getWalletConfigTokens } from 'selectors/wallet';
|
import { getWalletInst, getWalletConfigTokens } from 'selectors/wallet';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user