Use Network Symbol in Confirmation Modal (#1039)

* Set default unit to 'ETH' instead of 'ether'

* Use 'isEtherUnit()' everywhere

* Set default unit to empty string

* Update isEthUnit to isNetworkUnit

* Fix unit conversion for non-ethereum networks

* Set default network unit properly

* Fix tests

* fix typos

* Update isNetworkUnit selector

* Update isNetworkUnit

* Fix validationhelpers tests

* Add mock state to tests & Move isNetworkUnit to selectors

* Fix validation helper spec

* fix unit swap spec
This commit is contained in:
James Prado 2018-03-01 20:24:14 -05:00 committed by Daniel Ternyak
parent 08335fbca0
commit aabcd3f7a3
38 changed files with 198 additions and 126 deletions

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Provider, connect } from 'react-redux';
import { withRouter, Switch, Redirect, HashRouter, Route, BrowserRouter } from 'react-router-dom';
// Components
import Contracts from 'containers/Tabs/Contracts';
@ -15,27 +15,41 @@ import PageNotFound from 'components/PageNotFound';
import LogOutPrompt from 'components/LogOutPrompt';
import { TitleBar } from 'components/ui';
import { Store } from 'redux';
import { pollOfflineStatus } from 'actions/config';
import { pollOfflineStatus, TPollOfflineStatus } from 'actions/config';
import { AppState } from 'reducers';
import { RouteNotFound } from 'components/RouteNotFound';
import { RedirectWithQuery } from 'components/RedirectWithQuery';
import 'what-input';
import { setUnitMeta, TSetUnitMeta } from 'actions/transaction';
import { getNetworkUnit } from 'selectors/config';
interface Props {
interface OwnProps {
store: Store<AppState>;
}
interface StateProps {
networkUnit: string;
}
interface DispatchProps {
pollOfflineStatus: TPollOfflineStatus;
setUnitMeta: TSetUnitMeta;
}
type Props = OwnProps & StateProps & DispatchProps;
interface State {
error: Error | null;
}
export default class Root extends Component<Props, State> {
class RootClass extends Component<Props, State> {
public state = {
error: null
};
public componentDidMount() {
this.props.store.dispatch(pollOfflineStatus());
this.props.pollOfflineStatus();
this.props.setUnitMeta(this.props.networkUnit);
}
public componentDidCatch(error: Error) {
@ -134,3 +148,14 @@ const LegacyRoutes = withRouter(props => {
</Switch>
);
});
const mapStateToProps = (state: AppState) => {
return {
networkUnit: getNetworkUnit(state)
};
};
export default connect(mapStateToProps, {
pollOfflineStatus,
setUnitMeta
})(RootClass);

View File

@ -5,23 +5,22 @@ import {
SetTokenToMetaAction
} from 'actions/transaction';
type TSetTokenBalance = typeof setTokenValue;
type TSetUnitMeta = typeof setUnitMeta;
type TSetTokenTo = typeof setTokenTo;
const setTokenTo = (payload: SetTokenToMetaAction['payload']): SetTokenToMetaAction => ({
export type TSetTokenTo = typeof setTokenTo;
export const setTokenTo = (payload: SetTokenToMetaAction['payload']): SetTokenToMetaAction => ({
type: TypeKeys.TOKEN_TO_META_SET,
payload
});
const setTokenValue = (payload: SetTokenValueMetaAction['payload']): SetTokenValueMetaAction => ({
export type TSetTokenValue = typeof setTokenValue;
export const setTokenValue = (
payload: SetTokenValueMetaAction['payload']
): SetTokenValueMetaAction => ({
type: TypeKeys.TOKEN_VALUE_META_SET,
payload
});
const setUnitMeta = (payload: SetUnitMetaAction['payload']): SetUnitMetaAction => ({
export type TSetUnitMeta = typeof setUnitMeta;
export const setUnitMeta = (payload: SetUnitMetaAction['payload']): SetUnitMetaAction => ({
type: TypeKeys.UNIT_META_SET,
payload
});
export { TSetUnitMeta, TSetTokenBalance, TSetTokenTo, setUnitMeta, setTokenValue, setTokenTo };

View File

@ -6,8 +6,7 @@ import { Query } from 'components/renderCbs';
import { connect } from 'react-redux';
import { AppState } from 'reducers';
import { getUnit } from 'selectors/transaction';
import { getNetworkConfig } from 'selectors/config';
import { NetworkConfig } from 'types/network';
import { getNetworkUnit } from 'selectors/config';
interface DispatchProps {
setUnitMeta: TSetUnitMeta;
@ -18,21 +17,21 @@ interface StateProps {
tokens: TokenBalance[];
allTokens: MergedToken[];
showAllTokens?: boolean;
network: NetworkConfig;
networkUnit: string;
}
class UnitDropdownClass extends Component<DispatchProps & StateProps> {
public render() {
const { tokens, allTokens, showAllTokens, unit, network } = this.props;
const { tokens, allTokens, showAllTokens, unit, networkUnit } = this.props;
const focusedTokens = showAllTokens ? allTokens : tokens;
const options = [network.unit, ...getTokenSymbols(focusedTokens)];
const options = [networkUnit, ...getTokenSymbols(focusedTokens)];
return (
<Query
params={['readOnly']}
withQuery={({ readOnly }) => (
<Dropdown
options={options}
value={unit === 'ether' ? network.unit : unit}
value={unit === 'ether' ? networkUnit : unit}
onChange={this.handleOnChange}
clearable={false}
searchable={options.length > 10}
@ -53,7 +52,7 @@ function mapStateToProps(state: AppState) {
tokens: getShownTokenBalances(state, true),
allTokens: getTokens(state),
unit: getUnit(state),
network: getNetworkConfig(state)
networkUnit: getNetworkUnit(state)
};
}

View File

@ -17,7 +17,7 @@ import { getGasLimit } from 'selectors/transaction';
import { AddressField, AmountField, TXMetaDataPanel } from 'components';
import { SetGasLimitFieldAction } from 'actions/transaction/actionTypes/fields';
import { buildEIP681EtherRequest, buildEIP681TokenRequest } from 'libs/values';
import { getNetworkConfig, getSelectedTokenContractAddress } from 'selectors/config';
import { getNetworkConfig, getSelectedTokenContractAddress, isNetworkUnit } from 'selectors/config';
import './RequestPayment.scss';
import { reset, TReset, setCurrentTo, TSetCurrentTo } from 'actions/transaction';
import { NetworkConfig } from 'types/network';
@ -34,6 +34,7 @@ interface StateProps {
networkConfig: NetworkConfig;
decimal: number;
tokenContractAddress: string;
isNetworkUnit: boolean;
}
interface ActionProps {
@ -161,7 +162,7 @@ class RequestPayment extends React.Component<Props, {}> {
return '';
}
if (unit === 'ether') {
if (this.props.isNetworkUnit) {
return buildEIP681EtherRequest(currentTo, chainId, currentValue);
} else {
return buildEIP681TokenRequest(
@ -184,7 +185,8 @@ function mapStateToProps(state: AppState): StateProps {
gasLimit: getGasLimit(state),
networkConfig: getNetworkConfig(state),
decimal: getDecimal(state),
tokenContractAddress: getSelectedTokenContractAddress(state)
tokenContractAddress: getSelectedTokenContractAddress(state),
isNetworkUnit: isNetworkUnit(state, getUnit(state))
};
}

View File

@ -103,8 +103,6 @@ const fromTokenBase = (value: TokenValue, decimal: number) =>
const toTokenBase = (value: string, decimal: number) =>
TokenValue(convertedToBaseUnit(value, decimal));
const isEtherUnit = (unit: string) => unit === 'ether' || unit === 'ETH';
const convertTokenBase = (value: TokenValue, oldDecimal: number, newDecimal: number) => {
if (oldDecimal === newDecimal) {
return value;
@ -115,7 +113,6 @@ const convertTokenBase = (value: TokenValue, oldDecimal: number, newDecimal: num
const gasPricetoBase = (price: number) => toWei(price.toString(), getDecimalFromEtherUnit('gwei'));
export {
isEtherUnit,
Data,
Address,
TokenValue,

View File

@ -14,8 +14,8 @@ import {
import { Reducer } from 'redux';
const INITIAL_STATE: State = {
unit: 'ether',
previousUnit: 'ether',
unit: '',
previousUnit: '',
decimal: getDecimalFromEtherUnit('ether'),
tokenValue: { raw: '', value: null },
tokenTo: { raw: '', value: null },

View File

@ -11,8 +11,7 @@ import {
import { TypeKeys as WalletTK, setTokenBalancePending } from 'actions/wallet';
import { AppState } from 'reducers';
import { showNotification } from 'actions/notifications';
import { isSupportedUnit } from 'selectors/config';
import { isEtherUnit } from 'libs/units';
import { isSupportedUnit, isNetworkUnit } from 'selectors/config';
import { showLiteSend, configureLiteSend } from 'actions/swap';
import { TypeKeys as SwapTK } from 'actions/swap/constants';
import { isUnlocked, isEtherBalancePending } from 'selectors/wallet';
@ -40,9 +39,9 @@ export function* configureLiteSendSaga(): SagaIterator {
if (!unlocked) {
yield take(WalletTK.WALLET_SET);
}
const isNetwrkUnit = yield select(isNetworkUnit, label);
//if it's a token, manually scan for that tokens balance and wait for it to resolve
if (!isEtherUnit(label)) {
if (!isNetwrkUnit) {
yield put(setTokenBalancePending({ tokenSymbol: label }));
yield take([
WalletTK.WALLET_SET_TOKEN_BALANCE_FULFILLED,

View File

@ -7,7 +7,7 @@ import { meta } from './meta';
import { network } from './network';
import { signing } from './signing';
import { sendEverything } from './sendEverything';
import { reset } from './reset';
import { reset, setDefaultUnit } from './reset';
export function* transaction(): SagaIterator {
yield all([
@ -18,6 +18,7 @@ export function* transaction(): SagaIterator {
...network,
...signing,
...sendEverything,
...reset
...reset,
setDefaultUnit
]);
}

View File

@ -9,8 +9,9 @@ import {
getValue,
getDecimalFromUnit
} from 'selectors/transaction';
import { isNetworkUnit } from 'selectors/config';
import { getToken, MergedToken } from 'selectors/wallet';
import { isEtherUnit, TokenValue, Address } from 'libs/units';
import { TokenValue, Address } from 'libs/units';
import {
swapTokenToEther,
swapEtherToToken,
@ -23,10 +24,12 @@ import { validateInput, rebaseUserInput, IInput } from 'sagas/transaction/valida
export function* handleSetUnitMeta({ payload: currentUnit }: SetUnitMetaAction): SagaIterator {
const previousUnit: string = yield select(getPreviousUnit);
const etherToEther = isEtherUnit(currentUnit) && isEtherUnit(previousUnit);
const etherToToken = !isEtherUnit(currentUnit) && isEtherUnit(previousUnit);
const tokenToEther = isEtherUnit(currentUnit) && !isEtherUnit(previousUnit);
const tokenToToken = !isEtherUnit(currentUnit) && !isEtherUnit(previousUnit);
const prevUnit = yield select(isNetworkUnit, previousUnit);
const currUnit = yield select(isNetworkUnit, currentUnit);
const etherToEther = currUnit && prevUnit;
const etherToToken = !currUnit && prevUnit;
const tokenToEther = currUnit && !prevUnit;
const tokenToToken = !currUnit && !prevUnit;
const decimal: number = yield select(getDecimalFromUnit, currentUnit);
if (etherToEther) {

View File

@ -1,10 +1,22 @@
import { SagaIterator } from 'redux-saga';
import { TypeKeys } from 'actions/wallet';
import { takeEvery, put } from 'redux-saga/effects';
import { reset as resetActionCreator } from 'actions/transaction';
import { takeEvery, put, select } from 'redux-saga/effects';
import {
reset as resetActionCreator,
setUnitMeta,
TypeKeys as Constants
} from 'actions/transaction';
import { getNetworkUnit } from 'selectors/config';
export function* resetTransactionState(): SagaIterator {
yield put(resetActionCreator());
}
export function* setNetworkUnit(): SagaIterator {
const networkUnit = yield select(getNetworkUnit);
yield put(setUnitMeta(networkUnit));
}
export const setDefaultUnit = takeEvery(Constants.RESET, setNetworkUnit);
export const reset = [takeEvery([TypeKeys.WALLET_RESET], resetTransactionState)];

View File

@ -1,7 +1,7 @@
import { TokenValue, Wei, isEtherUnit, toTokenBase } from 'libs/units';
import { TokenValue, Wei, toTokenBase } from 'libs/units';
import { SagaIterator } from 'redux-saga';
import { getEtherBalance, getTokenBalance } from 'selectors/wallet';
import { getOffline } from 'selectors/config';
import { getOffline, isNetworkUnit } from 'selectors/config';
import { select, call } from 'redux-saga/effects';
import { AppState } from 'reducers';
import { getGasLimit, getGasPrice, getUnit, getDecimalFromUnit } from 'selectors/transaction';
@ -48,7 +48,7 @@ export function* validateInput(input: TokenValue | Wei | null, unit: string): Sa
const etherBalance: Wei | null = yield select(getEtherBalance);
const isOffline: boolean = yield select(getOffline);
const etherTransaction: boolean = yield call(isEtherUnit, unit);
const networkUnitTransaction: boolean = yield select(isNetworkUnit, unit);
if (isOffline || !etherBalance) {
return true;
@ -59,14 +59,14 @@ export function* validateInput(input: TokenValue | Wei | null, unit: string): Sa
// TODO: do gas estimation here if we're switching to a token too, it should cover the last edge case
//make a new transaction for validating ether balances
const validationTx = etherTransaction
const validationTx = networkUnitTransaction
? yield call(makeCostCalculationTx, input)
: yield call(makeCostCalculationTx, null);
// check that they have enough ether, this checks gas cost too
valid = valid && enoughBalanceViaTx(validationTx, etherBalance);
if (!etherTransaction) {
if (!networkUnitTransaction) {
const tokenBalance: TokenValue | null = yield select(getTokenBalance, unit);
valid = valid && enoughTokensViaInput(input, tokenBalance);
}

View File

@ -66,6 +66,10 @@ export const getNetworkConfig = (state: AppState): StaticNetworkConfig | CustomN
return config;
};
export const getNetworkUnit = (state: AppState): string => {
return getNetworkConfig(state).unit;
};
export const getNetworkContracts = (state: AppState): NetworkContract[] | null => {
const network = getStaticNetworkConfig(state);
return network ? network.contracts : [];
@ -74,3 +78,7 @@ export const getNetworkContracts = (state: AppState): NetworkContract[] | null =
export const getCustomNetworkConfigs = (state: AppState) => getNetworks(state).customNetworks;
export const getStaticNetworkConfigs = (state: AppState) => getNetworks(state).staticNetworks;
export const isNetworkUnit = (state: AppState, unit: string) => {
return unit === getNetworkUnit(state);
};

View File

@ -1,8 +1,7 @@
import { AppState } from 'reducers';
import { getUnit } from 'selectors/transaction/meta';
import { isEtherUnit } from 'libs/units';
import { SHAPESHIFT_TOKEN_WHITELIST } from 'api/shapeshift';
import { getStaticNetworkConfig } from 'selectors/config';
import { getStaticNetworkConfig, isNetworkUnit } from 'selectors/config';
import { Token } from 'types/network';
export function getNetworkTokens(state: AppState): Token[] {
@ -19,7 +18,7 @@ export function getSelectedTokenContractAddress(state: AppState): string {
const allTokens = getAllTokens(state);
const currentUnit = getUnit(state);
if (isEtherUnit(currentUnit)) {
if (isNetworkUnit(state, currentUnit)) {
return '';
}
@ -44,7 +43,7 @@ export function tokenExists(state: AppState, token: string): boolean {
export function isSupportedUnit(state: AppState, unit: string) {
const isToken: boolean = tokenExists(state, unit);
const isEther: boolean = isEtherUnit(unit);
const isEther: boolean = isNetworkUnit(state, unit);
if (!isToken && !isEther) {
return false;
}

View File

@ -1,6 +1,6 @@
import { AppState } from 'reducers';
import { getNetworkConfig } from 'selectors/config';
import { getUnit, isEtherTransaction, getParamsFromSerializedTx } from 'selectors/transaction';
import { getUnit, getParamsFromSerializedTx } from 'selectors/transaction';
import BN from 'bn.js';
import { Wei, TokenValue } from 'libs/units';
@ -9,13 +9,11 @@ export const getRates = (state: AppState) => state.rates;
const getUSDConversionRate = (state: AppState, unit: string) => {
const { isTestnet } = getNetworkConfig(state);
const { rates } = getRates(state);
const isEther = isEtherTransaction(state);
const conversionUnit = isEther ? 'ETH' : unit;
if (isTestnet) {
return null;
}
const conversionRate = rates[conversionUnit];
const conversionRate = rates[unit];
if (!conversionRate) {
return null;
@ -29,7 +27,6 @@ export const getValueInUSD = (state: AppState, value: TokenValue | Wei) => {
if (!conversionRate) {
return null;
}
const sendValueUSD = value.muln(conversionRate);
return sendValueUSD;
};

View File

@ -1,9 +1,10 @@
import { getTo, getValue } from './fields';
import { getUnit, getTokenTo, getTokenValue } from './meta';
import { AppState } from 'reducers';
import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units';
import { TokenValue, Wei, Address } from 'libs/units';
import { gasPriceValidator, gasLimitValidator } from 'libs/validators';
import { getDataExists, getGasPrice, getGasLimit } from 'selectors/transaction';
import { isNetworkUnit } from 'selectors/config';
import { getAddressMessage, AddressMessage } from 'config';
interface ICurrentValue {
@ -18,7 +19,7 @@ interface ICurrentTo {
const isEtherTransaction = (state: AppState) => {
const unit = getUnit(state);
const etherUnit = isEtherUnit(unit);
const etherUnit = isNetworkUnit(state, unit);
return etherUnit;
};

View File

@ -1,7 +1,6 @@
import { AppState } from 'reducers';
import { ICurrentTo, ICurrentValue } from 'selectors/transaction';
import { isEtherUnit } from 'libs/units';
import { isNetworkUnit } from 'selectors/config';
export const reduceToValues = (transactionFields: AppState['transaction']['fields']) =>
Object.keys(transactionFields).reduce(
(obj, currFieldName) => {
@ -12,6 +11,7 @@ export const reduceToValues = (transactionFields: AppState['transaction']['field
);
export const isFullTx = (
state: AppState,
transactionFields: AppState['transaction']['fields'],
currentTo: ICurrentTo,
currentValue: ICurrentValue,
@ -26,7 +26,7 @@ export const isFullTx = (
isValid && !!v.value,
true
);
if (isEtherUnit(unit)) {
if (isNetworkUnit(state, unit)) {
// if theres data we can have no current value, and we dont have to check for a to address
if (dataExists && validGasCost && !currentValue.value && currentValue.raw === '') {
return validPartialParams;

View File

@ -1,7 +1,8 @@
import { AppState } from 'reducers';
import { getTransactionState } from './transaction';
import { getDecimalFromEtherUnit, isEtherUnit } from 'libs/units';
import { getToken } from 'selectors/wallet';
import { isNetworkUnit } from 'selectors/config/wallet';
import { getDecimalFromEtherUnit } from 'libs/units';
const getMetaState = (state: AppState) => getTransactionState(state).meta;
const getFrom = (state: AppState) => getMetaState(state).from;
@ -11,7 +12,7 @@ const getTokenValue = (state: AppState) => getMetaState(state).tokenValue;
const getUnit = (state: AppState) => getMetaState(state).unit;
const getPreviousUnit = (state: AppState) => getMetaState(state).previousUnit;
const getDecimalFromUnit = (state: AppState, unit: string) => {
if (isEtherUnit(unit)) {
if (isNetworkUnit(state, unit)) {
return getDecimalFromEtherUnit('ether');
} else {
const token = getToken(state, unit);

View File

@ -34,6 +34,7 @@ const getTransaction = (state: AppState): IGetTransaction => {
const dataExists = getDataExists(state);
const validGasCost = getValidGasCost(state);
const isFullTransaction = isFullTx(
state,
transactionFields,
currentTo,
currentValue,

View File

@ -73,13 +73,13 @@ export function formatNumber(num: string, digits?: number): string {
}
// TODO: Comment up this function to make it clear what's happening here.
export function formatGasLimit(limit: Wei, transactionUnit: string = 'ether') {
export function formatGasLimit(limit: Wei, transactionUnit: string = 'ETH') {
let limitStr = limit.toString();
// I'm guessing this is some known off-by-one-error from the node?
// 21k is only the limit for ethereum though, so make sure they're
// sending ether if we're going to fix it for them.
if (limitStr === '21001' && transactionUnit === 'ether') {
if (limitStr === '21001' && transactionUnit === 'ETH') {
limitStr = '21000';
}

View File

@ -1,5 +1,7 @@
import { configuredStore } from 'store';
import CONTRACTS from 'config/contracts';
import { isValidETHAddress } from 'libs/validators';
configuredStore.getState();
describe('Contracts JSON', () => {
Object.keys(CONTRACTS).forEach(network => {

View File

@ -1,5 +1,7 @@
import { configuredStore } from 'store';
import TOKENS from 'config/tokens';
import { isValidETHAddress } from 'libs/validators';
configuredStore.getState();
describe('Tokens JSON', () => {
Object.keys(TOKENS).forEach(network => {

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import {
decryptPrivKey,
decodeCryptojsSalt,
@ -5,6 +6,7 @@ import {
decipherBuffer,
decryptMnemonicToPrivKey
} from '../../common/libs/decrypt';
configuredStore.getState();
// Elements of a V1 encrypted priv key
const v1 = {
@ -42,11 +44,10 @@ describe('decodeCryptojsSalt', () => {
describe('evp_kdf', () => {
it('should derive correct key and iv', () => {
const result = evp_kdf(
new Buffer(v1.pass, 'utf8'),
new Buffer(v1.salt, 'base64'),
{ keysize: 32, ivsize: 16 }
);
const result = evp_kdf(new Buffer(v1.pass, 'utf8'), new Buffer(v1.salt, 'base64'), {
keysize: 32,
ivsize: 16
});
expect(result.key).toBeInstanceOf(Buffer);
expect(result.iv).toBeInstanceOf(Buffer);
@ -78,13 +79,11 @@ describe('decipherBuffer', () => {
describe('decryptMnemonicToPrivKey', () => {
const mocks = [
{
phrase:
'first catalog away faculty jelly now life kingdom pigeon raise gain accident',
phrase: 'first catalog away faculty jelly now life kingdom pigeon raise gain accident',
pass: '',
path: "m/44'/60'/0'/0/8",
address: '0xe2EdC95134bbD88443bc6D55b809F7d0C2f0C854',
privKey:
'31e97f395cabc6faa37d8a9d6bb185187c35704e7b976c7a110e2f0eab37c344'
privKey: '31e97f395cabc6faa37d8a9d6bb185187c35704e7b976c7a110e2f0eab37c344'
},
{
phrase:
@ -92,8 +91,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: '',
path: "m/44'/60'/0'/0/18",
address: '0xB20f8aCA62e18f4586aAEf4720daCac23cC29954',
privKey:
'594ee624ebad54b9469915c3f5eb22127727a5e380a17d24780dbe272996b401'
privKey: '594ee624ebad54b9469915c3f5eb22127727a5e380a17d24780dbe272996b401'
},
{
phrase:
@ -101,8 +99,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: '',
path: "m/44'/60'/0'/0/24",
address: '0xE6D0932fFDDcB45bf0e18dE4716137dEdD2E4c2c',
privKey:
'6aba8bb6018a85af7cb552325b52e397f83cfb56f68cf8937aa14c3875bbb0aa'
privKey: '6aba8bb6018a85af7cb552325b52e397f83cfb56f68cf8937aa14c3875bbb0aa'
},
{
phrase:
@ -110,8 +107,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: '',
path: "m/44'/60'/0'/0/0",
address: '0xd163f4d95782608b251c4d985846A1754c53D32C',
privKey:
'88046b4bdbb1c88945662cb0984258ca1b09df0bb0b38fdc55bcb8998f28aad4'
privKey: '88046b4bdbb1c88945662cb0984258ca1b09df0bb0b38fdc55bcb8998f28aad4'
},
{
phrase:
@ -119,17 +115,14 @@ describe('decryptMnemonicToPrivKey', () => {
pass: '',
path: "m/44'/60'/0'/0/4",
address: '0x04E2df6Fe2a28dd24dbCC49485ff30Fc3ea04822',
privKey:
'fc9ad0931a3aee167179c1fd31825b7a7b558b4bb2eb3fb0c04028c98d495907'
privKey: 'fc9ad0931a3aee167179c1fd31825b7a7b558b4bb2eb3fb0c04028c98d495907'
},
{
phrase:
'stadium river pigeon midnight grit truck fiscal eight hello rescue destroy eyebrow',
phrase: 'stadium river pigeon midnight grit truck fiscal eight hello rescue destroy eyebrow',
pass: 'password',
path: "m/44'/60'/0'/0/5",
address: '0xe74908668F594f327fd2215A2564Cf79298a136e',
privKey:
'b65abfb2660f71b4b46aed98975f0cc1ebe1fcb3835a7a10b236e4012c93f306'
privKey: 'b65abfb2660f71b4b46aed98975f0cc1ebe1fcb3835a7a10b236e4012c93f306'
},
{
phrase:
@ -137,8 +130,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: 'password',
path: "m/44'/60'/0'/0/10",
address: '0x0d20865AfAE9B8a1F867eCd60684FBCDA3Bd1FA5',
privKey:
'29eb9ec0f5586d1935bc4c6bd89e6fb3de76b4fad345fa844efc5432885cfe73'
privKey: '29eb9ec0f5586d1935bc4c6bd89e6fb3de76b4fad345fa844efc5432885cfe73'
},
{
phrase:
@ -146,8 +138,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: 'password',
path: "m/44'/60'/0'/0/7",
address: '0xdd5d6e5dEfD09c3F2BD6d994EE43B59df88c7187',
privKey:
'd13404b9b05f6b5bf8e5cf810aa903e4b60ac654b0acf09a8ea0efe174746ae5'
privKey: 'd13404b9b05f6b5bf8e5cf810aa903e4b60ac654b0acf09a8ea0efe174746ae5'
},
{
phrase:
@ -155,8 +146,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: 'password',
path: "m/44'/60'/0'/0/11",
address: '0x6d95e7cC28113F9491b2Ec6b621575a5565Fd208',
privKey:
'a52329aa3d6f2426f8783a1e5f419997e2628ec9a89cc2b7b182d2eaf7f95a24'
privKey: 'a52329aa3d6f2426f8783a1e5f419997e2628ec9a89cc2b7b182d2eaf7f95a24'
},
{
phrase:
@ -164,8 +154,7 @@ describe('decryptMnemonicToPrivKey', () => {
pass: 'password',
path: "m/44'/60'/0'/0/4",
address: '0x3e583eF3d3cE5Dd483c86A1E00A479cE11Ca21Cf',
privKey:
'450538d4181c4d8ce076ecb34785198316adebe959d6f9462cfb68a58b1819bc'
privKey: '450538d4181c4d8ce076ecb34785198316adebe959d6f9462cfb68a58b1819bc'
},
{
phrase:
@ -173,20 +162,14 @@ describe('decryptMnemonicToPrivKey', () => {
pass: 'password123',
path: "m/44'/60'/0'/1",
address: '0x7545D615643F933c34C3E083E68CC831167F31af',
privKey:
'0a43098da5ae737843e385b76b44266a9f8f856cb1b943055b5a96188d306d97'
privKey: '0a43098da5ae737843e385b76b44266a9f8f856cb1b943055b5a96188d306d97'
}
];
it('should derive correct private key from variable phrase lengths/passwords/paths', () => {
mocks.forEach(mock => {
const { phrase, pass, path, privKey, address } = mock;
const derivedPrivKey = decryptMnemonicToPrivKey(
phrase,
pass,
path,
address
);
const derivedPrivKey = decryptMnemonicToPrivKey(phrase, pass, path, address);
expect(derivedPrivKey.toString('hex')).toEqual(privKey);
});
});

View File

@ -1,5 +1,7 @@
import { configuredStore } from 'store';
import { toWei } from 'libs/units';
import ERC20 from 'libs/erc20';
configuredStore.getState();
const MYCRYPTO_ADDRESS = '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8';
describe('ERC20', () => {

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import {
fromWei,
Wei,
@ -8,6 +9,7 @@ import {
convertTokenBase,
TokenValue
} from 'libs/units';
configuredStore.getState();
const Units = {
wei: '1',

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import {
isValidBTCAddress,
isValidETHAddress,
@ -6,6 +7,7 @@ import {
} from '../../common/libs/validators';
import { DPaths } from 'config/dpaths';
import { valid, invalid } from '../utils/testStrings';
configuredStore.getState();
const VALID_BTC_ADDRESS = '1MEWT2SGbqtz6mPCgFcnea8XmWV5Z4Wc6';
const VALID_ETH_ADDRESS = '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8';

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import React from 'react';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
@ -6,6 +7,7 @@ import shallowWithStore from '../utils/shallowWithStore';
import { createMockStore } from 'redux-test-utils';
import { createMockRouteComponentProps } from '../utils/mockRouteComponentProps';
import { RouteComponentProps } from 'react-router';
configuredStore.getState();
Enzyme.configure({ adapter: new Adapter() });

View File

@ -1,8 +1,10 @@
import { configuredStore } from 'store';
import { web3SetNode, web3UnsetNode } from 'actions/config';
import { staticNodes, INITIAL_STATE } from 'reducers/config/nodes/staticNodes';
import { EtherscanNode, InfuraNode, RPCNode } from 'libs/nodes';
import { Web3NodeConfig } from 'types/node';
import { Web3Service } from 'libs/nodes/web3';
configuredStore.getState();
const expectedInitialState = {
eth_mycrypto: {

View File

@ -1,6 +1,8 @@
import { configuredStore } from 'store';
import { deterministicWallets, INITIAL_STATE } from 'reducers/deterministicWallets';
import * as dWalletActions from 'actions/deterministicWallets';
import { TokenValue } from 'libs/units';
configuredStore.getState();
describe('deterministicWallets reducer', () => {
const tokenValues: dWalletActions.ITokenValues = {

View File

@ -1,6 +1,8 @@
import { configuredStore } from 'store';
import { wallet, INITIAL_STATE } from 'reducers/wallet';
import { Wei } from 'libs/units';
import * as walletActions from 'actions/wallet';
configuredStore.getState();
describe('wallet reducer', () => {
it('should handle WALLET_SET', () => {

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import { Address } from 'libs/units';
import { call, select, put } from 'redux-saga/effects';
import { isValidETHAddress, isValidENSAddress } from 'libs/validators';
@ -5,6 +6,7 @@ import { setCurrentTo, setField } from 'sagas/transaction/current/currentTo';
import { isEtherTransaction } from 'selectors/transaction';
import { cloneableGenerator } from 'redux-saga/utils';
import { setToField, setTokenTo } from 'actions/transaction';
configuredStore.getState();
const raw = '0xa';
const payload = {

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import BN from 'bn.js';
import { call, put } from 'redux-saga/effects';
import { setDataField, setGasLimitField, setNonceField } from 'actions/transaction/actionCreators';
@ -11,6 +12,7 @@ import {
} from 'sagas/transaction/fields/fields';
import { cloneableGenerator } from 'redux-saga/utils';
import { setGasPriceField } from 'actions/transaction';
configuredStore.getState();
const itShouldBeDone = gen => {
it('should be done', () => {

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import { select, call, put } from 'redux-saga/effects';
import { setDataField } from 'actions/transaction';
import { encodeTransfer } from 'libs/transaction/utils/token';
@ -6,6 +7,7 @@ import { bufferToHex, toBuffer } from 'ethereumjs-util';
import { getTokenTo, getData } from 'selectors/transaction';
import { handleTokenTo, handleTokenValue } from 'sagas/transaction/meta/token';
import { cloneableGenerator } from 'redux-saga/utils';
configuredStore.getState();
const itShouldBeDone = gen => {
it('should be done', () => {

View File

@ -19,6 +19,7 @@ import { bufferToHex } from 'ethereumjs-util';
import { rebaseUserInput, validateInput } from 'sagas/transaction/validationHelpers';
import { cloneableGenerator } from 'redux-saga/utils';
import { handleSetUnitMeta } from 'sagas/transaction/meta/unitSwap';
import { isNetworkUnit } from 'selectors/config';
const itShouldBeDone = gen => {
it('should be done', () => {
@ -27,25 +28,41 @@ const itShouldBeDone = gen => {
};
describe('handleSetUnitMeta*', () => {
const expectedStart = (gen, previousUnit, currentUnit) => {
const expectedStart = (
gen,
previousUnit,
currentUnit,
prevUnitIsNetworkUnit,
currUnitIsNetworkUnit
) => {
it('should select getPreviousUnit', () => {
expect(gen.next().value).toEqual(select(getPreviousUnit));
});
it('should check if prevUnit is a network unit', () => {
expect(gen.next(previousUnit).value).toEqual(select(isNetworkUnit, previousUnit));
});
it('should check if currUnit is a network unit', () => {
expect(gen.next(prevUnitIsNetworkUnit).value).toEqual(select(isNetworkUnit, currentUnit));
});
it('should select getDeciimalFromUnit with currentUnit', () => {
expect(gen.next(previousUnit).value).toEqual(select(getDecimalFromUnit, currentUnit));
expect(gen.next(currUnitIsNetworkUnit).value).toEqual(
select(getDecimalFromUnit, currentUnit)
);
});
};
describe('etherToEther', () => {
const currentUnit = 'ether';
const previousUnit = 'ether';
const currentUnit = 'ETH';
const previousUnit = 'ETH';
const action: any = {
payload: currentUnit
};
const gen = handleSetUnitMeta(action);
expectedStart(gen, previousUnit, currentUnit);
expectedStart(gen, previousUnit, currentUnit, true, true);
it('should return correctly', () => {
expect(gen.next().value).toEqual(undefined);
@ -56,7 +73,7 @@ describe('handleSetUnitMeta*', () => {
describe('tokenToEther', () => {
const previousUnit = 'token';
const currentUnit = 'ether';
const currentUnit = 'ETH';
const action: any = {
payload: currentUnit
};
@ -67,7 +84,7 @@ describe('handleSetUnitMeta*', () => {
const value: any = 'value';
const gen = handleSetUnitMeta(action);
expectedStart(gen, previousUnit, currentUnit);
expectedStart(gen, previousUnit, currentUnit, false, true);
it('should select getTokenTo', () => {
expect(gen.next(decimal).value).toEqual(select(getTokenTo));
@ -146,7 +163,7 @@ describe('handleSetUnitMeta*', () => {
};
describe('etherToToken', () => {
const previousUnit = 'ether';
const previousUnit = 'ETH';
const currentUnit = 'token';
const action: any = {
payload: currentUnit
@ -164,7 +181,7 @@ describe('handleSetUnitMeta*', () => {
const gens: any = {};
gens.gen = cloneableGenerator(handleSetUnitMeta)(action);
expectedStart(gens.gen, previousUnit, currentUnit);
expectedStart(gens.gen, previousUnit, currentUnit, true, false);
sharedLogicA(gens.gen, decimal, currentUnit);
@ -211,7 +228,7 @@ describe('handleSetUnitMeta*', () => {
const gens: any = {};
gens.gen = cloneableGenerator(handleSetUnitMeta)(action);
expectedStart(gens.gen, previousUnit, currentUnit);
expectedStart(gens.gen, previousUnit, currentUnit, false, false);
sharedLogicA(gens.gen, decimal, currentUnit);

View File

@ -1,9 +1,11 @@
import { configuredStore } from 'store';
import { apply, put, select } from 'redux-saga/effects';
import { getWalletInst } from 'selectors/wallet';
import { getFromSucceeded, getFromFailed } from 'actions/transaction';
import { showNotification } from 'actions/notifications';
import { cloneableGenerator } from 'redux-saga/utils';
import { handleFromRequest } from 'sagas/transaction/network/from';
configuredStore.getState();
describe('handleFromRequest*', () => {
const walletInst: any = {

View File

@ -1,8 +1,10 @@
import { configuredStore } from 'store';
import { getWalletInst } from 'selectors/wallet';
import { getNetworkConfig } from 'selectors/config';
import { select, call, put, take } from 'redux-saga/effects';
import { signTransactionFailed, getFromRequested, TypeKeys as TK } from 'actions/transaction';
import { showNotification } from 'actions/notifications';
configuredStore.getState();
/* tslint:disable */
import 'actions/transaction';

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import { put, apply, call } from 'redux-saga/effects';
import { signLocalTransactionSucceeded, signWeb3TransactionSucceeded } from 'actions/transaction';
import { computeIndexingHash } from 'libs/transaction';
@ -5,6 +6,7 @@ import {
signLocalTransactionHandler,
signWeb3TransactionHandler
} from 'sagas/transaction/signing/signing';
configuredStore.getState();
describe('signLocalTransactionHandler*', () => {
const tx = 'tx';

View File

@ -1,7 +1,7 @@
import { select, call } from 'redux-saga/effects';
import { getUnit, getDecimalFromUnit, getGasLimit, getGasPrice } from 'selectors/transaction';
import { getEtherBalance, getTokenBalance } from 'selectors/wallet';
import { isEtherUnit, toTokenBase, Wei } from 'libs/units';
import { toTokenBase, Wei } from 'libs/units';
import { makeTransaction } from 'libs/transaction';
import {
rebaseUserInput,
@ -9,7 +9,7 @@ import {
makeCostCalculationTx
} from 'sagas/transaction/validationHelpers';
import { cloneableGenerator } from 'redux-saga/utils';
import { getOffline } from 'selectors/config';
import { getOffline, isNetworkUnit } from 'selectors/config';
const itShouldBeDone = gen => {
it('should be done', () => {
@ -80,7 +80,6 @@ describe('validateInput*', () => {
const input: any = 'input';
const unit = 'unit';
const etherBalance = Wei('1000');
const isOffline = false;
const etherTransaction = true;
const validationTx = {
gasLimit: Wei('30'),
@ -101,27 +100,24 @@ describe('validateInput*', () => {
});
it('should select getOffline', () => {
gens.clone1 = gens.gen.clone();
gens.clone2 = gens.gen.clone();
expect(gens.gen.next(etherBalance).value).toEqual(select(getOffline));
gens.clone1 = gens.gen.clone();
});
it('should call isEtherUnit', () => {
expect(gens.gen.next(isOffline).value).toEqual(call(isEtherUnit, unit));
it('should call isNetworkUnit', () => {
expect(gens.gen.next(false).value).toEqual(select(isNetworkUnit, unit));
gens.clone3 = gens.gen.clone();
});
it('should return true when offline', () => {
gens.clone1.next();
gens.clone1.next(true);
expect(gens.clone1.next(true).value).toEqual(true);
expect(gens.clone1.next(true).value).toEqual(select(isNetworkUnit, unit));
expect(gens.clone1.next().done).toEqual(true);
});
it('should return when !etherBalance', () => {
gens.clone2.next(null);
gens.clone2.next(false);
expect(gens.clone2.next().value).toEqual(true);
expect(gens.clone2.next(null).value).toEqual(select(getOffline));
expect(gens.clone2.next(true).value).toEqual(select(isNetworkUnit, unit));
expect(gens.clone2.next().done).toEqual(true);
});

View File

@ -1,3 +1,4 @@
import { configuredStore } from 'store';
import { Wei } from 'libs/units';
import {
toFixedIfLarger,
@ -5,6 +6,7 @@ import {
formatGasLimit,
formatMnemonic
} from '../../common/utils/formatters';
configuredStore.getState();
describe('toFixedIfLarger', () => {
it('should return same value if decimal isnt longer than default', () => {
@ -83,11 +85,11 @@ describe('formatNumber', () => {
describe('formatGasLimit', () => {
it('should fix transaction gas limit off-by-one errors', () => {
expect(formatGasLimit(Wei('21001'), 'ether')).toEqual('21000');
expect(formatGasLimit(Wei('21001'), 'ETH')).toEqual('21000');
});
it('should mark the gas limit `-1` if you exceed the limit per block', () => {
expect(formatGasLimit(Wei('999999999999999'), 'ether')).toEqual('-1');
expect(formatGasLimit(Wei('999999999999999'), 'ETH')).toEqual('-1');
});
it('should not alter a valid gas limit', () => {