diff --git a/.flowconfig b/.flowconfig
deleted file mode 100644
index 1a77fd5f..00000000
--- a/.flowconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-[ignore]
-
-[include]
-
-[libs]
-
-[options]
-module.file_ext=.js
-module.file_ext=.json
-module.file_ext=.jsx
-module.file_ext=.scss
-module.file_ext=.less
-module.system.node.resolve_dirname=node_modules
-module.system.node.resolve_dirname=common
-module.name_mapper='.*\.(css|less)$' -> 'empty/object'
diff --git a/.gitignore b/.gitignore
index 48b0195e..a540c898 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,4 @@ webpack_config/server.csr
v8-compile-cache-0/
+package-lock.json
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..43c97e71
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index cff9f393..00000000
--- a/Dockerfile
+++ /dev/null
@@ -1,8 +0,0 @@
-FROM node:8.1.4
-
-WORKDIR /usr/app
-
-COPY package.json .
-RUN npm install --quiet
-
-COPY . .
diff --git a/README.md b/README.md
index f454b5a7..d0fe51a7 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# MyEtherWallet V4+ (ALPHA - VISIT [V3](https://github.com/kvhnuke/etherwallet) for the production site)
+[](https://greenkeeper.io/)
+
#### Run:
```bash
@@ -36,7 +38,7 @@ npm run dev:https
2. [dternyak/eth-priv-to-addr](https://hub.docker.com/r/dternyak/eth-priv-to-addr/) pulled from DockerHub
##### Docker setup instructions:
-1. Install docker (on macOS, I suggest [Docker for Mac](https://docs.docker.com/docker-for-mac/))
+1. Install docker (on macOS, [Docker for Mac](https://docs.docker.com/docker-for-mac/) is suggested)
2. `docker pull dternyak/eth-priv-to-addr`
##### Run Derivation Checker
@@ -48,40 +50,32 @@ npm run derivation-checker
```
│
-├── common - Your App
+├── common
│ ├── actions - application actions
-│ ├── api - Services and XHR utils(also custom form validation, see InputComponent from components/common)
+│ ├── api - Services and XHR utils
│ ├── components - components according to "Redux philosophy"
│ ├── config - frontend config depending on REACT_WEBPACK_ENV
│ ├── containers - containers according to "Redux philosophy"
│ ├── reducers - application reducers
│ ├── routing - application routing
-│ ├── index.jsx - entry
+│ ├── index.tsx - entry
│ ├── index.html
├── static
├── webpack_config - Webpack configuration
├── jest_config - Jest configuration
```
-## Docker setup
-You should already have docker and docker-compose setup for your platform as a pre-req.
-
-```bash
-docker-compose up
-```
-
## Style Guides and Philosophies
The following are guides for developers to follow for writing compliant code.
-
### Redux and Actions
-Each reducer has one file in `reducers/[namespace].js` that contains the reducer
-and initial state, one file in `actions/[namespace].js` that contains the action
+Each reducer has one file in `reducers/[namespace].ts` that contains the reducer
+and initial state, one file in `actions/[namespace].ts` that contains the action
creators and their return types, and optionally one file in
-`sagas/[namespace].js` that handles action side effects using
+`sagas/[namespace].ts` that handles action side effects using
[`redux-saga`](https://github.com/redux-saga/redux-saga).
The files should be laid out as follows:
@@ -89,75 +83,192 @@ The files should be laid out as follows:
#### Reducer
* State should be explicitly defined and exported
-* Initial state should match state flow typing, define every key
-* Reducer function should handle all cases for actions. If state does not change
-as a result of an action (Because it merely kicks off side-effects in saga) then
-define the case above default, and have it fall through.
+* Initial state should match state typing, define every key
-```js
-// @flow
-import type { NamespaceAction } from "actions/namespace";
+```ts
+import { NamespaceAction } from "actions/[namespace]";
+import { TypeKeys } from 'actions/[namespace]/constants';
-export type State = { /* Flowtype definition for state object */ };
+export interface State { /* definition for state object */ };
export const INITIAL_STATE: State = { /* Initial state shape */ };
-export function namespace(
+export function [namespace](
state: State = INITIAL_STATE,
action: NamespaceAction
): State {
switch (action.type) {
- case 'NAMESPACE_NAME_OF_ACTION':
+ case TypeKeys.NAMESPACE_NAME_OF_ACTION:
return {
...state,
// Alterations to state
- };
-
- case 'NAMESPACE_NAME_OF_SAGA_ACTION':
+ };
default:
- // Ensures every action was handled in reducer
- // Unhandled actions should just fall into default
- (action: empty);
return state;
}
}
```
#### Actions
+* Define each action creator in `actionCreator.ts`
+* Define each action object type in `actionTypes.ts`
+ * Export a union of all of the action types for use by the reducer
+* Define each action type as a string enum in `constants.ts`
+* Export `actionCreators` and `actionTypes` from module file `index.ts`
-* Define each action object type beside the action creator
-* Export a union of all of the action types for use by the reducer
-
-```js
+```
+├── common
+ ├── actions - application actions
+ ├── [namespace] - action namespace
+ ├── actionCreators.ts - action creators
+ ├── actionTypes.ts - action interfaces / types
+ ├── constants.ts - string enum
+ ├── index.ts - exports all action creators and action object types
+```
+##### constants.ts
+```ts
+export enum TypeKeys {
+ NAMESPACE_NAME_OF_ACTION = 'NAMESPACE_NAME_OF_ACTION'
+}
+```
+##### actionTypes.ts
+```ts
/*** Name of action ***/
-export type NameOfActionAction = {
- type: 'NAMESPACE_NAME_OF_ACTION',
+export interface NameOfActionAction {
+ type: TypeKeys.NAMESPACE_NAME_OF_ACTION,
/* Rest of the action object shape */
};
-export function nameOfAction(): NameOfActionAction {
- return {
- type: 'NAMESPACE_NAME_OF_ACTION',
- /* Rest of the action object */
- };
-};
-
/*** Action Union ***/
export type NamespaceAction =
| ActionOneAction
| ActionTwoAction
| ActionThreeAction;
```
+##### actionCreators.ts
+```ts
+import * as interfaces from './actionTypes';
+import { TypeKeys } from './constants';
-#### Action Constants
+export interface TNameOfAction = typeof nameOfAction;
+export function nameOfAction(): interfaces.NameOfActionAction {
+ return {
+ type: TypeKeys.NAMESPACE_NAME_OF_ACTION,
+ payload: {}
+ };
+};
+```
+##### index.ts
+```ts
+export * from './actionCreators';
+export * from './actionTypes';
+```
-Action constants are not used thanks to flow type checking. To avoid typos, we
-use `(action: empty)` in the default case which assures every case is accounted
-for. If you need to use another reducer's action, import that action type into
-your reducer, and create a new action union of your actions, and the other
-action types used.
+### Typing Redux-Connected Components
+Components that receive props directly from redux as a result of the `connect`
+function should use AppState for typing, rather than manually defining types.
+This makes refactoring reducers easier by catching mismatches or changes of
+types in components, and reduces the chance for inconsistency. It's also less
+code overall.
+```
+// Do this
+import { AppState } from 'reducers';
+interface Props {
+ wallet: AppState['wallet']['inst'];
+ rates: AppState['rates']['rates'];
+ // ...
+}
+
+// Not this
+import { IWallet } from 'libs/wallet';
+import { Rates } from 'libs/rates';
+
+interface Props {
+ wallet: IWallet;
+ rates: Rates;
+ // ...
+}
+```
+
+However, if you have a sub-component that takes in props from a connected
+component, it's OK to manually specify the type. Especially if you go from
+being type-or-null to guaranteeing the prop will be passed (because of a
+conditional render.)
+
+### Higher Order Components
+
+#### Typing Injected Props
+Props made available through higher order components can be tricky to type. Normally, if a component requires a prop, you add it to the component's interface and it just works. However, working with injected props from [higher order components](https://medium.com/@DanHomola/react-higher-order-components-in-typescript-made-simple-6f9b55691af1), you will be forced to supply all required props whenever you compose the component.
+
+```ts
+interface MyComponentProps {
+ name: string;
+ countryCode?: string;
+ routerLocation: { pathname: string };
+}
+
+...
+
+class OtherComponent extends React.Component<{}, {}> {
+ render() {
+ return (
+
- {translate('sidebar_TransHistory')}
-
-
- {!!blockExplorer && (
-
-
+ {translate('sidebar_TransHistory')}
+
+
+ {!!blockExplorer && (
+
+ {ratesError}
;
+ } else {
+ valuesEl = (
+ {translate('sidebar_Equiv')}
-
-
- {rates
- ? symbols.map(key => {
- if (!rates[key]) {
- return null;
+
+ })}
+
+
+
+
+ {translate('sidebar_Equiv')} for{' '}
+
{valuesEl}
);
}
diff --git a/common/components/BalanceSidebar/TokenBalances/index.tsx b/common/components/BalanceSidebar/TokenBalances/index.tsx
index 93bd4b8a..a8920dbd 100644
--- a/common/components/BalanceSidebar/TokenBalances/index.tsx
+++ b/common/components/BalanceSidebar/TokenBalances/index.tsx
@@ -25,25 +25,24 @@ export default class TokenBalances extends React.Component {
title={`${balance.toString()} (Double-Click)`}
onDoubleClick={this.toggleShowLongBalance}
>
- {!!custom &&
+ {!!custom && (
- }
+ />
+ )}
- {showLongBalance ? balance.toString() : formatNumber(balance)}
+
- {symbol}
-
+ {symbol}
- {translate('sidebar_TokenBal')}
-
+ {translate('sidebar_TokenBal')}
- {shownTokens.map(token =>
+ {shownTokens.map(token => (
@@ -58,16 +57,15 @@ export default class TokenBalances extends React.Component
Latest Block#: ?????
+Latest Block#: {this.props.latestBlock}
diff --git a/common/components/Header/components/CustomNodeModal.tsx b/common/components/Header/components/CustomNodeModal.tsx new file mode 100644 index 00000000..d4b63800 --- /dev/null +++ b/common/components/Header/components/CustomNodeModal.tsx @@ -0,0 +1,230 @@ +import React from 'react'; +import classnames from 'classnames'; +import Modal, { IButton } from 'components/ui/Modal'; +import translate from 'translations'; +import { NETWORKS, CustomNodeConfig } from 'config/data'; + +const NETWORK_KEYS = Object.keys(NETWORKS); + +interface Input { + name: string; + placeholder?: string; + type?: string; +} + +interface Props { + handleAddCustomNode(node: CustomNodeConfig): void; + handleClose(): void; +} + +interface State { + name: string; + url: string; + port: string; + network: string; + hasAuth: boolean; + username: string; + password: string; +} + +export default class CustomNodeModal extends React.ComponentYOUR ADDRESS
Your Address:
- {this.state.address}
+ {address}
Your Private Key:
@@ -151,7 +137,7 @@ export default class PaperWallet extends React.Component
Always look for this icon when sending to this wallet
diff --git a/common/components/PrintableWallet/index.tsx b/common/components/PrintableWallet/index.tsx
index 205d56b1..bbabe064 100644
--- a/common/components/PrintableWallet/index.tsx
+++ b/common/components/PrintableWallet/index.tsx
@@ -1,49 +1,53 @@
import { PaperWallet } from 'components';
-import PrivKeyWallet from 'libs/wallet/privkey';
-import React, { Component } from 'react';
+import { IFullWallet } from 'ethereumjs-wallet';
+import React from 'react';
import translate from 'translations';
import printElement from 'utils/printElement';
-interface Props {
- wallet: PrivKeyWallet;
-}
+const print = (address: string, privateKey: string) => () =>
+ address &&
+ privateKey &&
+ printElement(
- {translate('ADD_Label_3')}
- {translate('ADD_Label_3')}
+ Paste a signed transaction and press the "SEND TRANSACTION"
+ button.
+
+ You are about to {action} on the{' '}
+ {networkName} chain.
+
+ The {network} node you are sending through is
+ provided by {service}.
+
+ {isMobile
+ ? `
+ MyEtherWallet requires certain features for secure wallet generation
+ that your browser doesn't offer. You can still securely use the site
+ otherwise. To generate a wallet, please use your device's default
+ browser, or switch to a laptop or desktop computer.
+ `
+ : `
+ MyEtherWallet requires certain features for secure wallet generation
+ that your browser doesn't offer. You can still securely use the site
+ otherwise. To generate a wallet, upgrade to one of the following
+ browsers:
+ `}
+
+ Do not lose it! It cannot be recovered if you lose it.
+
+ Do not share it! Your funds will be stolen if you use
+ this file on a malicious/phishing site.
+
+ Make a backup! Secure it like the millions of dollars
+ it may one day be worth.
+
- Do not lose it! It cannot be recovered if you lose
- it.
-
- Do not share it! Your funds will be stolen if you
- use this file on a malicious/phishing site.
-
- Make a backup! Secure it like the millions of
- dollars it may one day be worth.
- {translate('WARN_Send_Link')} {translate('WARN_Send_Link')}
+ To broadcast this transaction, paste the above
+ into{' '}
+
+ {' '}
+ myetherwallet.com/pushTx
+ {' '}
+ or{' '}
+
+ {' '}
+ etherscan.io/pushTx
+
+
- To broadcast this transaction, paste the above
- into{' '}
-
- {' '}
- myetherwallet.com/pushTx
- {' '}
- or{' '}
-
- {' '}
- etherscan.io/pushTx
-
-
+ MetaMask / Mist wallets are not available in offline mode.
+
{translate('SEND_amount')}
{translate('SWAP_your_rate')}
diff --git a/common/containers/Tabs/index.ts b/common/containers/Tabs/index.ts
index 163a8878..8a6aaf49 100644
--- a/common/containers/Tabs/index.ts
+++ b/common/containers/Tabs/index.ts
@@ -4,6 +4,7 @@ import { default as Help } from './Help';
import { default as SendTransaction } from './SendTransaction';
import { default as Swap } from './Swap';
import { default as ViewWallet } from './ViewWallet';
+import { default as SignAndVerifyMessage } from './SignAndVerifyMessage';
export default {
ENS,
@@ -11,5 +12,6 @@ export default {
Help,
SendTransaction,
Swap,
- ViewWallet
+ ViewWallet,
+ SignAndVerifyMessage
};
diff --git a/common/derivation-checker.ts b/common/derivation-checker.ts
index a235e4a0..a68b2aae 100644
--- a/common/derivation-checker.ts
+++ b/common/derivation-checker.ts
@@ -1,5 +1,5 @@
import assert from 'assert';
-import PrivKeyWallet from './libs/wallet/privkey';
+import { generate, IFullWallet } from 'ethereumjs-wallet';
const { exec } = require('child_process');
const ProgressBar = require('progress');
@@ -17,8 +17,10 @@ function promiseFromChildProcess(command): Promise
+
#
Address
-
- {network.unit}
-
+ {network.unit}
More
@@ -265,24 +266,19 @@ class DeterministicWalletsModal extends React.Component
-
- {wallet.index + 1}
-
+ {wallet.index + 1}
{
{wallet.address}
- {value} {network.unit}
+
- {tokenValue} {desiredToken}
+ {token ? (
+
{
}
}
-function mapStateToProps(state) {
+function mapStateToProps(state: AppState) {
return {
wallets: state.deterministicWallets.wallets,
desiredToken: state.deterministicWallets.desiredToken,
diff --git a/common/components/WalletDecrypt/Keystore.tsx b/common/components/WalletDecrypt/Keystore.tsx
index aaa07112..efc5210c 100644
--- a/common/components/WalletDecrypt/Keystore.tsx
+++ b/common/components/WalletDecrypt/Keystore.tsx
@@ -1,4 +1,4 @@
-import { isKeystorePassRequired } from 'libs/keystore';
+import { isKeystorePassRequired } from 'libs/wallet';
import React, { Component } from 'react';
import translate, { translateRaw } from 'translations';
@@ -32,9 +32,7 @@ export default class KeystoreDecrypt extends Component {
return (
- {translate('ADD_Radio_2_alt')}
-
+ {translate('ADD_Radio_2_alt')}
- {translate('ADD_Radio_5')}
-
+ {translate('ADD_Radio_5')}
+
+);
+
+export default Code;
diff --git a/common/components/ui/ColorDropdown.scss b/common/components/ui/ColorDropdown.scss
new file mode 100644
index 00000000..4f0a8211
--- /dev/null
+++ b/common/components/ui/ColorDropdown.scss
@@ -0,0 +1,23 @@
+.ColorDropdown {
+ &-item {
+ position: relative;
+ padding-right: 10px;
+ border-left: 2px solid;
+
+ &-remove {
+ position: absolute;
+ top: 50%;
+ right: 5px;
+ width: 15px;
+ height: 15px;
+ opacity: 0.5;
+ cursor: pointer;
+ // Z fixes clipping issue
+ transform: translateY(-50%) translateZ(0);
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+ }
+}
diff --git a/common/components/ui/ColorDropdown.tsx b/common/components/ui/ColorDropdown.tsx
index 161d5e43..9a90b116 100644
--- a/common/components/ui/ColorDropdown.tsx
+++ b/common/components/ui/ColorDropdown.tsx
@@ -1,11 +1,15 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import DropdownShell from './DropdownShell';
+import removeIcon from 'assets/images/icon-remove.svg';
+import './ColorDropdown.scss';
interface Option{children}
+
+ )}
Broadcast Signed Transaction
+
+ {translate('CONTRACT_Interact_Title')}
+ {address}
+
+
+
+
+ {selectedFunction && (
+
+ {showExplorer &&
+ currentContract && (
+ {translate('SEND_raw')}
+ {JSON.stringify(rawTx, null, 2)}
+ {translate('SEND_signed')}
+ {props.signedTx}
+
+ {translate('SENDModal_Title')}
+
+
+ {translate('SENDModal_Content_3')}
+
+ {' '}
+ or{' '}
+
+
+
+ Your Browser Cannot Generate a Wallet
+
+
+
{translate('GEN_Help_4')}
{translate('GEN_Label_5')}
+
-export default class PaperWallet extends Component{translate('x_Print')}
+ {translate('GEN_Label_5')}
-
+ {/* Warning */}
+ {translate('x_Print')}
- {translate('GEN_Help_4')}
+
+
- {/* Continue button */}
-
- {translate('NAV_ViewWallet')} →
-
- {translate('GEN_Help_17')}
+
+
- const help = (
- {translate('GEN_Help_4')}
-
-
+ {translate('x_PrintDesc')}
+ {translate('GEN_Help_17')}
-
-
+const PaperWallet: React.SFC<{
+ wallet: IFullWallet;
+}> = ({ wallet }) => ;
- {translate('x_PrintDesc')}
- Uh oh. Not sure how you got here.
;
+ }
+ break;
+
+ default:
content = Uh oh. Not sure how you got here.
;
- }
- break;
-
- default:
- content = Uh oh. Not sure how you got here.
;
+ }
+ } else {
+ content =
{fromAddress}
+ You are sending from {from}
{toAddress}
@@ -150,9 +150,20 @@ class ConfirmationModal extends React.ComponentSorry...
+ {translate('MSG_message')}
+ {translate('MSG_signature')}
+ {translate('MSG_signature')}
+
+ {' '}
+ or{' '}
+
+
+ {translate('SWAP_init_1')}
-
- {translate('SWAP_init_2')}
-
-
-
- {translate('SWAP_init_2')}
+
- {` ${originAmount} ${originKind}`}
-
+ {` ${originAmount} ${
+ originKind
+ }`}
{`${computedOriginDestinationRatio &&
- toFixedIfLarger(
- computedOriginDestinationRatio
- )} ${originKind}/${destinationKind}`}
+ toFixedIfLarger(computedOriginDestinationRatio)} ${
+ destinationKind
+ }/${originKind}`}
+ View Wallet Info
+
+