From 9dad38c0f8b4173622497519cfde4b976482959b Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 3 Jul 2018 17:10:29 +0200 Subject: [PATCH 01/17] Debug message on error --- packages/fether-react/src/App/App.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/fether-react/src/App/App.js b/packages/fether-react/src/App/App.js index fc8a44f5..7ebcb74a 100644 --- a/packages/fether-react/src/App/App.js +++ b/packages/fether-react/src/App/App.js @@ -14,6 +14,7 @@ import { import { inject, observer } from 'mobx-react'; import Accounts from '../Accounts'; +import Debug from '../utils/debug'; import Onboarding from '../Onboarding'; import Overlay from '../Overlay'; import Send from '../Send'; @@ -21,6 +22,7 @@ import { STATUS } from '../stores/healthStore'; import Tokens from '../Tokens'; import Whitelist from '../Whitelist'; +const debug = Debug('App'); // Use MemoryRouter for production viewing in file:// protocol // https://github.com/facebook/create-react-app/issues/3591 const Router = @@ -29,6 +31,10 @@ const Router = @inject('healthStore', 'onboardingStore') @observer class App extends Component { + componentDidCatch (err) { + debug(err.message); + } + render () { return ( -- GitLab From 73c10544e96188da5cf729a287640eb70a53fae1 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 3 Jul 2018 17:48:23 +0200 Subject: [PATCH 02/17] Don't use default exports --- .../fether-ui/src/AccountCard/AccountCard.js | 6 ++--- .../src/AccountCard/Address/Address.js | 4 +-- .../src/AccountCard/Avatar/Avatar.js | 2 +- .../fether-ui/src/AccountCard/Name/Name.js | 2 +- packages/fether-ui/src/AccountCard/index.js | 4 +-- .../src/AccountHeader/AccountHeader.js | 13 +++++---- packages/fether-ui/src/AccountHeader/index.js | 4 +-- .../src/AddressShort/AddressShort.js | 4 +-- packages/fether-ui/src/AddressShort/index.js | 4 +-- packages/fether-ui/src/Card/Card.js | 4 +-- packages/fether-ui/src/Card/index.js | 4 +-- .../fether-ui/src/ClickToCopy/ClickToCopy.js | 4 +-- packages/fether-ui/src/ClickToCopy/index.js | 4 +-- packages/fether-ui/src/FormField/FormField.js | 4 +-- packages/fether-ui/src/FormField/index.js | 4 +-- packages/fether-ui/src/Header/Header.js | 6 ++--- packages/fether-ui/src/Header/index.js | 4 +-- .../fether-ui/src/Placeholder/Placeholder.js | 4 +-- packages/fether-ui/src/Placeholder/index.js | 4 +-- packages/fether-ui/src/TokenCard/TokenCard.js | 6 ++--- packages/fether-ui/src/TokenCard/index.js | 4 +-- packages/fether-ui/src/index.js | 27 ++++++------------- 22 files changed, 41 insertions(+), 81 deletions(-) diff --git a/packages/fether-ui/src/AccountCard/AccountCard.js b/packages/fether-ui/src/AccountCard/AccountCard.js index f569788d..d68ec279 100644 --- a/packages/fether-ui/src/AccountCard/AccountCard.js +++ b/packages/fether-ui/src/AccountCard/AccountCard.js @@ -8,11 +8,11 @@ import PropTypes from 'prop-types'; import Address from './Address'; import Avatar from './Avatar'; -import Card from '../Card'; +import { Card } from '../Card'; import Information from './Information'; import Name from './Name'; -const AccountCard = ({ address, name, shortAddress, ...otherProps }) => ( +export const AccountCard = ({ address, name, shortAddress, ...otherProps }) => (
@@ -34,5 +34,3 @@ AccountCard.propTypes = { name: PropTypes.string, shortAddress: PropTypes.bool }; - -export default AccountCard; diff --git a/packages/fether-ui/src/AccountCard/Address/Address.js b/packages/fether-ui/src/AccountCard/Address/Address.js index d1279565..9478364a 100644 --- a/packages/fether-ui/src/AccountCard/Address/Address.js +++ b/packages/fether-ui/src/AccountCard/Address/Address.js @@ -6,8 +6,8 @@ import React from 'react'; import PropTypes from 'prop-types'; -import AddressShort from '../../AddressShort'; -import Placeholder from '../../Placeholder'; +import { AddressShort } from '../../AddressShort'; +import { Placeholder } from '../../Placeholder'; const Address = ({ address, short, ...otherProps }) => (
diff --git a/packages/fether-ui/src/AccountCard/Avatar/Avatar.js b/packages/fether-ui/src/AccountCard/Avatar/Avatar.js index 6b089966..0bb991a6 100644 --- a/packages/fether-ui/src/AccountCard/Avatar/Avatar.js +++ b/packages/fether-ui/src/AccountCard/Avatar/Avatar.js @@ -7,7 +7,7 @@ import React from 'react'; import Blockies from 'react-blockies'; import PropTypes from 'prop-types'; -import Placeholder from '../../Placeholder'; +import { Placeholder } from '../../Placeholder'; const Avatar = ({ address, ...otherProps }) => (
diff --git a/packages/fether-ui/src/AccountCard/Name/Name.js b/packages/fether-ui/src/AccountCard/Name/Name.js index 6fec49b0..a829ed07 100644 --- a/packages/fether-ui/src/AccountCard/Name/Name.js +++ b/packages/fether-ui/src/AccountCard/Name/Name.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Placeholder from '../../Placeholder'; +import { Placeholder } from '../../Placeholder'; const Name = ({ name, ...otherProps }) => (
diff --git a/packages/fether-ui/src/AccountCard/index.js b/packages/fether-ui/src/AccountCard/index.js index 82cb08c3..509a607a 100644 --- a/packages/fether-ui/src/AccountCard/index.js +++ b/packages/fether-ui/src/AccountCard/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import AccountCard from './AccountCard'; - -export default AccountCard; +export * from './AccountCard'; diff --git a/packages/fether-ui/src/AccountHeader/AccountHeader.js b/packages/fether-ui/src/AccountHeader/AccountHeader.js index 9f60c356..d77ac6d8 100644 --- a/packages/fether-ui/src/AccountHeader/AccountHeader.js +++ b/packages/fether-ui/src/AccountHeader/AccountHeader.js @@ -7,8 +7,8 @@ import React from 'react'; import Blockies from 'react-blockies'; import PropTypes from 'prop-types'; -import ClickToCopy from '../ClickToCopy'; -import Header from '../Header'; +import { ClickToCopy } from '../ClickToCopy'; +import { Header } from '../Header'; const NormalContainer = ({ children }) => (

{children}

@@ -19,7 +19,12 @@ const CopyContainer = ({ address, children, ...otherProps }) => ( ); -const AccountHeader = ({ address, copyAddress, name, ...otherProps }) => { +export const AccountHeader = ({ + address, + copyAddress, + name, + ...otherProps +}) => { const Container = copyAddress ? CopyContainer : NormalContainer; return ( @@ -45,5 +50,3 @@ AccountHeader.propTypes = { address: PropTypes.string, name: PropTypes.string }; - -export default AccountHeader; diff --git a/packages/fether-ui/src/AccountHeader/index.js b/packages/fether-ui/src/AccountHeader/index.js index 60913613..b24ac61c 100644 --- a/packages/fether-ui/src/AccountHeader/index.js +++ b/packages/fether-ui/src/AccountHeader/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import AccountHeader from './AccountHeader'; - -export default AccountHeader; +export * from './AccountHeader'; diff --git a/packages/fether-ui/src/AddressShort/AddressShort.js b/packages/fether-ui/src/AddressShort/AddressShort.js index f3f4bb8b..8fc363bb 100644 --- a/packages/fether-ui/src/AddressShort/AddressShort.js +++ b/packages/fether-ui/src/AddressShort/AddressShort.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const AddressShort = ({ address, as: T = 'span', ...otherProps }) => ( +export const AddressShort = ({ address, as: T = 'span', ...otherProps }) => ( {address.slice(0, 6)}..{address.slice(-4)} @@ -15,5 +15,3 @@ const AddressShort = ({ address, as: T = 'span', ...otherProps }) => ( AddressShort.propTypes = { address: PropTypes.string.isRequired }; - -export default AddressShort; diff --git a/packages/fether-ui/src/AddressShort/index.js b/packages/fether-ui/src/AddressShort/index.js index bf2aed3a..c46d6bad 100644 --- a/packages/fether-ui/src/AddressShort/index.js +++ b/packages/fether-ui/src/AddressShort/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import AddressShort from './AddressShort'; - -export default AddressShort; +export * from './AddressShort'; diff --git a/packages/fether-ui/src/Card/Card.js b/packages/fether-ui/src/Card/Card.js index 963299ef..5119e6ea 100644 --- a/packages/fether-ui/src/Card/Card.js +++ b/packages/fether-ui/src/Card/Card.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const Card = ({ children, className, drawers, onClick }) => ( +export const Card = ({ children, className, drawers, onClick }) => (
( +export const Header = ({ left, right, title }) => (
{left}
@@ -23,5 +23,3 @@ Header.propTypes = { right: PropTypes.node, title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]) }; - -export default Header; diff --git a/packages/fether-ui/src/Header/index.js b/packages/fether-ui/src/Header/index.js index 95cd6385..704bf967 100644 --- a/packages/fether-ui/src/Header/index.js +++ b/packages/fether-ui/src/Header/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Header from './Header'; - -export default Header; +export * from './Header'; diff --git a/packages/fether-ui/src/Placeholder/Placeholder.js b/packages/fether-ui/src/Placeholder/Placeholder.js index 0543faf6..9fc8940d 100644 --- a/packages/fether-ui/src/Placeholder/Placeholder.js +++ b/packages/fether-ui/src/Placeholder/Placeholder.js @@ -7,7 +7,7 @@ import React from 'react'; import ContentLoader from 'react-content-loader'; import PropTypes from 'prop-types'; -const Placeholder = ({ height, width, ...otherProps }) => ( +export const Placeholder = ({ height, width, ...otherProps }) => ( Date: Tue, 3 Jul 2018 18:12:38 +0200 Subject: [PATCH 03/17] No export default --- packages/fether-ui/src/AccountCard/AccountCard.js | 10 +++++----- packages/fether-ui/src/AccountCard/Address/Address.js | 4 +--- packages/fether-ui/src/AccountCard/Address/index.js | 4 +--- packages/fether-ui/src/AccountCard/Avatar/Avatar.js | 4 +--- packages/fether-ui/src/AccountCard/Avatar/index.js | 4 +--- .../src/AccountCard/Information/Information.js | 4 +--- .../fether-ui/src/AccountCard/Information/index.js | 4 +--- packages/fether-ui/src/AccountCard/Name/Name.js | 4 +--- packages/fether-ui/src/AccountCard/Name/index.js | 4 +--- 9 files changed, 13 insertions(+), 29 deletions(-) diff --git a/packages/fether-ui/src/AccountCard/AccountCard.js b/packages/fether-ui/src/AccountCard/AccountCard.js index d68ec279..75f6b0bb 100644 --- a/packages/fether-ui/src/AccountCard/AccountCard.js +++ b/packages/fether-ui/src/AccountCard/AccountCard.js @@ -6,15 +6,15 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Address from './Address'; -import Avatar from './Avatar'; +import { Address } from './Address'; +import { Avatar } from './Avatar'; import { Card } from '../Card'; -import Information from './Information'; -import Name from './Name'; +import { Information } from './Information'; +import { Name } from './Name'; export const AccountCard = ({ address, name, shortAddress, ...otherProps }) => ( -
+
diff --git a/packages/fether-ui/src/AccountCard/Address/Address.js b/packages/fether-ui/src/AccountCard/Address/Address.js index 9478364a..9c1c5edb 100644 --- a/packages/fether-ui/src/AccountCard/Address/Address.js +++ b/packages/fether-ui/src/AccountCard/Address/Address.js @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import { AddressShort } from '../../AddressShort'; import { Placeholder } from '../../Placeholder'; -const Address = ({ address, short, ...otherProps }) => ( +export const Address = ({ address, short, ...otherProps }) => (
{address ? ( short ? ( @@ -31,5 +31,3 @@ Address.propTypes = { name: PropTypes.string, short: PropTypes.bool }; - -export default Address; diff --git a/packages/fether-ui/src/AccountCard/Address/index.js b/packages/fether-ui/src/AccountCard/Address/index.js index 32a08964..88179d97 100644 --- a/packages/fether-ui/src/AccountCard/Address/index.js +++ b/packages/fether-ui/src/AccountCard/Address/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Address from './Address'; - -export default Address; +export * from './Address'; diff --git a/packages/fether-ui/src/AccountCard/Avatar/Avatar.js b/packages/fether-ui/src/AccountCard/Avatar/Avatar.js index 0bb991a6..1912517f 100644 --- a/packages/fether-ui/src/AccountCard/Avatar/Avatar.js +++ b/packages/fether-ui/src/AccountCard/Avatar/Avatar.js @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import { Placeholder } from '../../Placeholder'; -const Avatar = ({ address, ...otherProps }) => ( +export const Avatar = ({ address, ...otherProps }) => (
{address ? ( @@ -22,5 +22,3 @@ const Avatar = ({ address, ...otherProps }) => ( Avatar.propTypes = { address: PropTypes.string }; - -export default Avatar; diff --git a/packages/fether-ui/src/AccountCard/Avatar/index.js b/packages/fether-ui/src/AccountCard/Avatar/index.js index 7118470a..82cb46a9 100644 --- a/packages/fether-ui/src/AccountCard/Avatar/index.js +++ b/packages/fether-ui/src/AccountCard/Avatar/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Avatar from './Avatar'; - -export default Avatar; +export * from './Avatar'; diff --git a/packages/fether-ui/src/AccountCard/Information/Information.js b/packages/fether-ui/src/AccountCard/Information/Information.js index ff9bdfef..e2d41c50 100644 --- a/packages/fether-ui/src/AccountCard/Information/Information.js +++ b/packages/fether-ui/src/AccountCard/Information/Information.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const Information = ({ children, ...otherProps }) => ( +export const Information = ({ children, ...otherProps }) => (
{children}
@@ -16,5 +16,3 @@ const Information = ({ children, ...otherProps }) => ( Information.propTypes = { children: PropTypes.node }; - -export default Information; diff --git a/packages/fether-ui/src/AccountCard/Information/index.js b/packages/fether-ui/src/AccountCard/Information/index.js index 916846dc..77e73b92 100644 --- a/packages/fether-ui/src/AccountCard/Information/index.js +++ b/packages/fether-ui/src/AccountCard/Information/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Information from './Information'; - -export default Information; +export * from './Information'; diff --git a/packages/fether-ui/src/AccountCard/Name/Name.js b/packages/fether-ui/src/AccountCard/Name/Name.js index a829ed07..40049c2a 100644 --- a/packages/fether-ui/src/AccountCard/Name/Name.js +++ b/packages/fether-ui/src/AccountCard/Name/Name.js @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import { Placeholder } from '../../Placeholder'; -const Name = ({ name, ...otherProps }) => ( +export const Name = ({ name, ...otherProps }) => (
{name || }
@@ -17,5 +17,3 @@ const Name = ({ name, ...otherProps }) => ( Name.propTypes = { name: PropTypes.string }; - -export default Name; diff --git a/packages/fether-ui/src/AccountCard/Name/index.js b/packages/fether-ui/src/AccountCard/Name/index.js index 091ba90e..ec7fc784 100644 --- a/packages/fether-ui/src/AccountCard/Name/index.js +++ b/packages/fether-ui/src/AccountCard/Name/index.js @@ -3,6 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Name from './Name'; - -export default Name; +export * from './Name'; -- GitLab From 8b2e7de66fa00e7de9c6c7732650d6dcc7c70203 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 3 Jul 2018 18:15:34 +0200 Subject: [PATCH 04/17] Reset tx when selecting a new token --- .../src/Tokens/TokensList/TokenBalance/TokenBalance.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js b/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js index 3517bda5..faa0a200 100644 --- a/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js +++ b/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js @@ -24,6 +24,7 @@ class TokenBalance extends Component { if (!token.address) { return; } + sendStore.clear(); sendStore.setTokenAddress(token.address); history.push('/send'); }; -- GitLab From 10839de8dd26e235f06ce8983960750661606fe7 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 3 Jul 2018 18:15:39 +0200 Subject: [PATCH 05/17] Fix lint --- packages/fether-ui/src/AccountCard/AccountCard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fether-ui/src/AccountCard/AccountCard.js b/packages/fether-ui/src/AccountCard/AccountCard.js index 75f6b0bb..65083b19 100644 --- a/packages/fether-ui/src/AccountCard/AccountCard.js +++ b/packages/fether-ui/src/AccountCard/AccountCard.js @@ -14,7 +14,7 @@ import { Name } from './Name'; export const AccountCard = ({ address, name, shortAddress, ...otherProps }) => ( -
+
-- GitLab From 3593935973571db6f67bff583d901e7ef397b414 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 3 Jul 2018 18:49:33 +0200 Subject: [PATCH 06/17] Fix logLastLine includes --- packages/parity-electron/src/runParity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parity-electron/src/runParity.js b/packages/parity-electron/src/runParity.js index cd2f277a..0e328cc1 100644 --- a/packages/parity-electron/src/runParity.js +++ b/packages/parity-electron/src/runParity.js @@ -47,7 +47,7 @@ export const runParity = async (additionalFlags, onParityError) => { await fsChmod(parityPath, '755'); } catch (e) {} - let logLastLine; // Always contains last line of the Parity logs + let logLastLine = ''; // Always contains last line of the Parity logs // Run an instance of parity with the correct args const args = [...parityArgv(), ...additionalFlags]; -- GitLab From 43008ef46769524b485d1d943746e66473977de1 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Wed, 4 Jul 2018 18:49:47 +0200 Subject: [PATCH 07/17] Use a custom Form Field component --- packages/fether-ui/package.json | 4 ++- packages/fether-ui/src/Form/Field/Field.js | 32 +++++++++++++++++++ .../src/{FormField => Form/Field}/index.js | 2 +- packages/fether-ui/src/Form/Form.js | 12 +++++++ packages/fether-ui/src/Form/Slider/Slider.js | 24 ++++++++++++++ packages/fether-ui/src/Form/Slider/index.js | 6 ++++ packages/fether-ui/src/Form/index.js | 6 ++++ packages/fether-ui/src/FormField/FormField.js | 25 --------------- packages/fether-ui/src/index.js | 2 +- 9 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 packages/fether-ui/src/Form/Field/Field.js rename packages/fether-ui/src/{FormField => Form/Field}/index.js (81%) create mode 100644 packages/fether-ui/src/Form/Form.js create mode 100644 packages/fether-ui/src/Form/Slider/Slider.js create mode 100644 packages/fether-ui/src/Form/Slider/index.js create mode 100644 packages/fether-ui/src/Form/index.js delete mode 100644 packages/fether-ui/src/FormField/FormField.js diff --git a/packages/fether-ui/package.json b/packages/fether-ui/package.json index 14ee7376..9d676770 100644 --- a/packages/fether-ui/package.json +++ b/packages/fether-ui/package.json @@ -33,7 +33,9 @@ "dependencies": { "react-blockies": "^1.3.0", "react-content-loader": "^3.1.2", - "react-tooltip": "^3.6.1" + "react-tooltip": "^3.6.1", + "semantic-ui-css": "^2.3.2", + "semantic-ui-react": "^0.81.3" }, "devDependencies": { "@babel/cli": "^7.0.0-beta.49", diff --git a/packages/fether-ui/src/Form/Field/Field.js b/packages/fether-ui/src/Form/Field/Field.js new file mode 100644 index 00000000..94557309 --- /dev/null +++ b/packages/fether-ui/src/Form/Field/Field.js @@ -0,0 +1,32 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import React from 'react'; +import { Popup } from 'semantic-ui-react'; +import 'semantic-ui-css/components/popup.min.css'; + +export const Field = ({ + as: T = 'input', + children, + input, + label, + meta, + ...otherProps +}) => ( +
+ + } + /> + + {children} +
+); diff --git a/packages/fether-ui/src/FormField/index.js b/packages/fether-ui/src/Form/Field/index.js similarity index 81% rename from packages/fether-ui/src/FormField/index.js rename to packages/fether-ui/src/Form/Field/index.js index eed88ea4..1a046ee2 100644 --- a/packages/fether-ui/src/FormField/index.js +++ b/packages/fether-ui/src/Form/Field/index.js @@ -3,4 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -export * from './FormField'; +export * from './Field'; diff --git a/packages/fether-ui/src/Form/Form.js b/packages/fether-ui/src/Form/Form.js new file mode 100644 index 00000000..23344af4 --- /dev/null +++ b/packages/fether-ui/src/Form/Form.js @@ -0,0 +1,12 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import { Field } from './Field'; +import { Slider } from './Slider'; + +export const Form = { + Field, + Slider +}; diff --git a/packages/fether-ui/src/Form/Slider/Slider.js b/packages/fether-ui/src/Form/Slider/Slider.js new file mode 100644 index 00000000..3e665b7d --- /dev/null +++ b/packages/fether-ui/src/Form/Slider/Slider.js @@ -0,0 +1,24 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import React from 'react'; + +import { Field } from '../Field'; + +export const Slider = ({ + centerText, + input, + leftText, + rightText, + ...otherProps +}) => ( + + + +); diff --git a/packages/fether-ui/src/Form/Slider/index.js b/packages/fether-ui/src/Form/Slider/index.js new file mode 100644 index 00000000..ac776dd2 --- /dev/null +++ b/packages/fether-ui/src/Form/Slider/index.js @@ -0,0 +1,6 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +export * from './Slider'; diff --git a/packages/fether-ui/src/Form/index.js b/packages/fether-ui/src/Form/index.js new file mode 100644 index 00000000..45c4b207 --- /dev/null +++ b/packages/fether-ui/src/Form/index.js @@ -0,0 +1,6 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +export * from './Form'; diff --git a/packages/fether-ui/src/FormField/FormField.js b/packages/fether-ui/src/FormField/FormField.js deleted file mode 100644 index 44a4d16a..00000000 --- a/packages/fether-ui/src/FormField/FormField.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -import React from 'react'; -import PropTypes from 'prop-types'; - -export const FormField = ({ - className, - input: inputComponent, - label, - ...otherProps -}) => ( -
- - {inputComponent || } -
-); - -FormField.propTypes = { - className: PropTypes.string, - input: PropTypes.node, - label: PropTypes.string -}; diff --git a/packages/fether-ui/src/index.js b/packages/fether-ui/src/index.js index 99c50982..2d1c5e9f 100644 --- a/packages/fether-ui/src/index.js +++ b/packages/fether-ui/src/index.js @@ -7,7 +7,7 @@ export * from './AccountCard'; export * from './AccountHeader'; export * from './AddressShort'; export * from './Card'; -export * from './FormField'; +export * from './Form'; export * from './Header'; export * from './Placeholder'; export * from './TokenCard'; -- GitLab From 18d908be7148a6d43c52663e7e82b5c5484279a0 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Wed, 4 Jul 2018 18:49:55 +0200 Subject: [PATCH 08/17] Allow no token in tokenCard --- packages/fether-ui/src/TokenCard/TokenCard.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/fether-ui/src/TokenCard/TokenCard.js b/packages/fether-ui/src/TokenCard/TokenCard.js index 8941fa12..c414279b 100644 --- a/packages/fether-ui/src/TokenCard/TokenCard.js +++ b/packages/fether-ui/src/TokenCard/TokenCard.js @@ -20,14 +20,18 @@ export const TokenCard = ({
- {token.logo ? ( + {token && token.logo ? ( {token.symbol} ) : ( )}
- {token.name ? token.name : } + {token && token.name ? ( + token.name + ) : ( + + )}
{balance ? ( @@ -35,7 +39,7 @@ export const TokenCard = ({ ) : showBalance ? ( ) : null} - {token.symbol} + {token && token.symbol}
{children}
@@ -53,7 +57,7 @@ TokenCard.propTypes = { logo: PropTypes.string, name: PropTypes.string, symbol: PropTypes.string - }).isRequired + }) }; export default TokenCard; -- GitLab From a85a8f25c9b3c5437e331b615ae64839d0971a8e Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Wed, 4 Jul 2018 18:50:08 +0200 Subject: [PATCH 09/17] In prod, redirect to / on error --- packages/fether-react/src/App/App.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/fether-react/src/App/App.js b/packages/fether-react/src/App/App.js index 7ebcb74a..8623d674 100644 --- a/packages/fether-react/src/App/App.js +++ b/packages/fether-react/src/App/App.js @@ -33,6 +33,10 @@ const Router = class App extends Component { componentDidCatch (err) { debug(err.message); + if (process.env.NODE_ENV !== 'development') { + // Redirect to '/' on errors + window.location.href = '/'; + } } render () { -- GitLab From 91bc364ee7d89b9053eba11436151ff4990d4ab9 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Wed, 4 Jul 2018 18:50:24 +0200 Subject: [PATCH 10/17] Take out estimateGas logic from sendStore --- packages/fether-react/package.json | 4 + packages/fether-react/src/Send/Send.js | 20 +- .../fether-react/src/Send/Signer/Signer.js | 15 +- .../fether-react/src/Send/TxForm/TxForm.js | 310 +++++++----------- .../TokensList/TokenBalance/TokenBalance.js | 7 +- packages/fether-react/src/stores/sendStore.js | 46 +-- .../fether-react/src/utils/estimateGas.js | 90 +++++ .../fether-react/src/utils/withBalance.js | 48 +-- yarn.lock | 62 +++- 9 files changed, 315 insertions(+), 287 deletions(-) create mode 100644 packages/fether-react/src/utils/estimateGas.js diff --git a/packages/fether-react/package.json b/packages/fether-react/package.json index 99370cc8..6b68b0cb 100644 --- a/packages/fether-react/package.json +++ b/packages/fether-react/package.json @@ -38,8 +38,10 @@ "@parity/light.js": "https://github.com/parity-js/light.js#9646ce15d9dd9c4cf11776ddd613d5bd86016f94", "@parity/shared": "^3.0.2", "bignumber.js": "^4.1.0", + "debounce-promise": "^3.1.0", "debug": "^3.1.0", "fether-ui": "^0.1.0", + "final-form": "^4.8.3", "is-electron": "^2.1.0", "light-hoc": "^0.1.0", "lodash": "^4.17.10", @@ -48,10 +50,12 @@ "react": "^16.3.2", "react-blockies": "^1.3.0", "react-dom": "^16.3.2", + "react-final-form": "^3.6.4", "react-markdown": "^3.3.4", "react-router-dom": "^4.2.2", "react-scripts": "1.1.4", "react-tooltip": "^3.6.1", + "recompose": "^0.27.1", "rxjs": "^6.2.0" }, "devDependencies": { diff --git a/packages/fether-react/src/Send/Send.js b/packages/fether-react/src/Send/Send.js index f460e272..231e073f 100644 --- a/packages/fether-react/src/Send/Send.js +++ b/packages/fether-react/src/Send/Send.js @@ -4,32 +4,20 @@ // SPDX-License-Identifier: BSD-3-Clause import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; import { Route, Redirect, Switch } from 'react-router-dom'; import Sent from './Sent'; import Signer from './Signer'; import TxForm from './TxForm'; -@inject('sendStore') -@observer class Send extends Component { render () { - const { - sendStore: { tokenAddress } - } = this.props; - - // We only show then Send components if we have already selected a token to - // send. - if (!tokenAddress) { - return ; - } - return ( - - - + + + + ); } diff --git a/packages/fether-react/src/Send/Signer/Signer.js b/packages/fether-react/src/Send/Signer/Signer.js index c91324f2..b491aee6 100644 --- a/packages/fether-react/src/Send/Signer/Signer.js +++ b/packages/fether-react/src/Send/Signer/Signer.js @@ -9,10 +9,14 @@ import { FormField, Header } from 'fether-ui'; import { inject, observer } from 'mobx-react'; import { Link } from 'react-router-dom'; import ReactTooltip from 'react-tooltip'; +import { withProps } from 'recompose'; import TokenBalance from '../../Tokens/TokensList/TokenBalance'; @inject('sendStore', 'tokensStore') +@withProps(({ match: { params: { tokenAddress } }, tokensStore }) => ({ + token: tokensStore.tokens[tokenAddress] +})) @observer class Signer extends Component { state = { @@ -58,11 +62,10 @@ class Signer extends Component { render () { const { - sendStore: { tokenAddress, tx }, - tokensStore + sendStore: { tx }, + token } = this.props; const { error, isSending, password } = this.state; - const token = tokensStore.tokens[tokenAddress]; return (
@@ -132,12 +135,6 @@ class Signer extends Component { />
-
); } diff --git a/packages/fether-react/src/Send/TxForm/TxForm.js b/packages/fether-react/src/Send/TxForm/TxForm.js index a93aca0b..a5c93a14 100644 --- a/packages/fether-react/src/Send/TxForm/TxForm.js +++ b/packages/fether-react/src/Send/TxForm/TxForm.js @@ -4,13 +4,15 @@ // SPDX-License-Identifier: BSD-3-Clause import React, { Component } from 'react'; -import debounce from 'lodash/debounce'; -import { FormField, Header } from 'fether-ui'; +import debounce from 'debounce-promise'; +import { estimateGas } from '../../utils/estimateGas'; +import { Field, Form } from 'react-final-form'; +import { Form as FetherForm, Header } from 'fether-ui'; import { fromWei, toWei } from '@parity/api/lib/util/wei'; import { inject, observer } from 'mobx-react'; import { isAddress } from '@parity/api/lib/util/address'; import { Link } from 'react-router-dom'; -import ReactTooltip from 'react-tooltip'; +import { withProps } from 'recompose'; import TokenBalance from '../../Tokens/TokensList/TokenBalance'; import withBalance from '../../utils/withBalance'; @@ -18,120 +20,25 @@ import withBalance from '../../utils/withBalance'; const MAX_GAS_PRICE = 40; // In Gwei const MIN_GAS_PRICE = 3; // Safelow gas price from GasStation, in Gwei -@inject('sendStore', 'tokensStore') -@withBalance( - ({ sendStore: { tokenAddress }, tokensStore }) => - tokensStore.tokens[tokenAddress] -) +@inject('parityStore', 'sendStore', 'tokensStore') +@withProps(({ match: { params: { tokenAddress } }, tokensStore }) => ({ + token: tokensStore.tokens[tokenAddress] +})) +@withBalance @observer class Send extends Component { - state = { - amount: '', // In Ether or in token - gasPrice: 4, // in Gwei - to: '', - estimating: false, // Currently estimating gasPrice - ...this.props.sendStore.tx - }; - - static getDerivedStateFromProps (nextProps, prevState) { - const { - balance, - sendStore: { estimated } - } = nextProps; - - // Calculate the maxAount - return { - maxAmount: - balance && estimated - ? +fromWei( - toWei(balance).minus( - estimated.mul(toWei(prevState.gasPrice, 'shannon')) - ) - ) - : 0.01 - }; - } - - componentDidMount () { - this.handleEstimateGasPrice(); - } - - estimateGas = debounce( - () => - this.props.sendStore - .estimateGas() - .then(() => this.setState({ estimating: false })) - .catch(() => this.setState({ estimating: false })), - 1000 - ); - - handleChangeAmount = ({ target: { value } }) => - this.setState({ amount: value }, this.handleEstimateGasPrice); - - handleChangeGasPrice = ({ target: { value } }) => - this.setState({ gasPrice: value }, this.handleEstimateGasPrice); - - handleChangeTo = ({ target: { value } }) => { - this.setState({ to: value }, this.handleEstimateGasPrice); - }; - - handleEstimateGasPrice = () => { - if (this.hasError()) { - return; - } - - const { amount, gasPrice, to } = this.state; - this.props.sendStore.setTx({ amount, gasPrice, to }); - this.setState({ estimating: true }, this.estimateGas); - }; - - handleMax = () => - this.setState( - { amount: this.state.maxAmount }, - this.handleEstimateGasPrice - ); - handleSubmit = event => { event.preventDefault(); - const { history } = this.props; - history.push('/send/signer'); - }; - - /** - * Get form errors. - * - * TODO Use a React form library to do this? - */ - hasError = () => { - const { amount, maxAmount, to } = this.state; - if (!amount || isNaN(amount)) { - return 'Please enter a valid amount'; - } - - if (amount < 0) { - return 'Please enter a positive amount '; - } - - if (amount > maxAmount) { - return "You don't have enough balance"; - } - - if (!isAddress(to)) { - return 'Please enter a valid Ethereum address'; - } - - return null; + const { history, sendStore, token } = this.props; + sendStore.setTx(event); + history.push(`/send/${token.address}/signer`); }; render () { const { - sendStore: { tokenAddress }, - tokensStore + sendStore: { tx }, + token } = this.props; - const { amount, estimating, gasPrice, maxAmount, to } = this.state; - - const token = tokensStore.tokens[tokenAddress]; - const error = this.hasError(); return (
@@ -141,7 +48,7 @@ class Send extends Component { Close } - title={

Send {token.name}

} + title={token &&

Send {token.name}

} />
@@ -149,104 +56,121 @@ class Send extends Component { -
- - - -
- } - label='Amount' - /> - - ( +
+
+ + + + + - } - label='To' - /> - - - - -
- } - label='Gas' - /> - - - + + + + )} + /> ]} - onClick={null} + onClick={null} // To disable cursor:pointer on card // TODO Can this be done better? token={token} />
-
); } + + /** + * Estimate gas amount, and validate that the user has enough balance to make + * the tx. + */ + validateBalance = debounce(async values => { + try { + const estimated = await estimateGas( + values, + this.props.token, + this.props.parityStore.api + ); + + const { balance } = this.props; + if (!balance || isNaN(estimated)) { + throw new Error('No "balance" or "estimated" value.'); + } + // Calculate the max amount the user can send + const maxAmount = +fromWei( + toWei(balance).minus(estimated.mul(toWei(values.gasPrice, 'shannon'))) + ); + + if (values.amount > maxAmount) { + return { amount: "You don't have enough balance" }; + } + } catch (err) { + return { + amount: 'Failed estimating balance, please try again' + }; + } + }, 1000); + + validateForm = values => { + if (!values.amount || isNaN(values.amount)) { + return { amount: 'Please enter a valid amount' }; + } + + if (values.amount < 0) { + return { amount: 'Please enter a positive amount ' }; + } + + if (this.props.balance && this.props.balance.lt(values.amount)) { + return { amount: "You don't have enough balance" }; + } + + if (!isAddress(values.to)) { + return { to: 'Please enter a valid Ethereum address' }; + } + + return this.validateBalance(values); + }; } export default Send; diff --git a/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js b/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js index faa0a200..08ea0a5f 100644 --- a/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js +++ b/packages/fether-react/src/Tokens/TokensList/TokenBalance/TokenBalance.js @@ -11,12 +11,12 @@ import { withRouter } from 'react-router-dom'; import withBalance from '../../../utils/withBalance'; -@withBalance() +@withBalance @inject('sendStore') @withRouter class TokenBalance extends Component { static propTypes = { - token: PropTypes.object.isRequired + token: PropTypes.object }; handleClick = () => { @@ -25,8 +25,7 @@ class TokenBalance extends Component { return; } sendStore.clear(); - sendStore.setTokenAddress(token.address); - history.push('/send'); + history.push(`/send/${token.address}`); }; render () { diff --git a/packages/fether-react/src/stores/sendStore.js b/packages/fether-react/src/stores/sendStore.js index 2bfc036e..8103f675 100644 --- a/packages/fether-react/src/stores/sendStore.js +++ b/packages/fether-react/src/stores/sendStore.js @@ -9,11 +9,11 @@ import { BigNumber } from 'bignumber.js'; import { blockNumber$, makeContract$, post$ } from '@parity/light.js'; import memoize from 'lodash/memoize'; import noop from 'lodash/noop'; -import { toWei } from '@parity/api/lib/util/wei'; import Debug from '../utils/debug'; import parityStore from './parityStore'; import tokensStore from './tokensStore'; +import { txForErc20, txForEth } from '../utils/estimateGas'; const debug = Debug('sendStore'); const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer. @@ -75,9 +75,11 @@ export class SendStore { } if (this.tokenAddress === 'ETH') { - return this.estimateGasForEth(this.txForEth); + return this.estimateGasForEth(txForEth(this.tx)); } else { - return this.estimateGasForErc20(this.txForErc20); + return this.estimateGasForErc20( + txForErc20(this.tx, tokensStore.tokens[this.tokenAddress]) + ); } }; @@ -113,10 +115,10 @@ export class SendStore { send = password => { const send$ = this.tokenAddress === 'ETH' - ? post$(this.txForEth) + ? post$(txForEth(this.tx)) : this.contract.transfer$( - ...this.txForErc20.args, - this.txForErc20.options + ...txForErc20(this.tx).args, + txForErc20(this.tx).options ); debug( @@ -138,38 +140,6 @@ export class SendStore { }); }; - /** - * This.tx is a user-friendly tx object. We convert it now as it can be - * passed to makeContract$(...). - */ - @computed - get txForErc20 () { - return { - args: [ - this.tx.to, - new BigNumber(this.tx.amount).mul( - new BigNumber(10).pow(tokensStore.tokens[this.tokenAddress].decimals) - ) - ], - options: { - gasPrice: toWei(this.tx.gasPrice, 'shannon') // shannon == gwei - } - }; - } - - /** - * This.tx is a user-friendly tx object. We convert it now as it can be - * passed to post$(tx). - */ - @computed - get txForEth () { - return { - gasPrice: toWei(this.tx.gasPrice, 'shannon'), // shannon == gwei - to: this.tx.to, - value: toWei(this.tx.amount.toString()) - }; - } - @action setBlockNumber = blockNumber => { this.blockNumber = blockNumber; diff --git a/packages/fether-react/src/utils/estimateGas.js b/packages/fether-react/src/utils/estimateGas.js new file mode 100644 index 00000000..375f119b --- /dev/null +++ b/packages/fether-react/src/utils/estimateGas.js @@ -0,0 +1,90 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import BigNumber from 'bignumber.js'; +import memoize from 'lodash/memoize'; +import { toWei } from '@parity/api/lib/util/wei'; + +import Debug from './debug'; + +const debug = Debug('estimateGas'); +const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer. + +/** + * Estimate the amount of gas for our transaction. + */ +export const estimateGas = (tx, token, api) => { + if (!tx || !Object.keys(tx).length) { + return Promise.reject(new Error('Tx not set in sendStore.')); + } + + if (token.address === 'ETH') { + return estimateGasForEth(txForEth(tx), api).then(addBuffer); + } else { + return estimateGasForErc20(txForErc20(tx, token), api).then(addBuffer); + } +}; + +/** + * Estimate gas to transfer in ERC20 contract. Expensive function, so we + * memoize it. + */ +const estimateGasForErc20 = memoize( + txForErc20 => + this.contract.contractObject.instance.transfer.estimateGas( + txForErc20.options, + txForErc20.args + ), + JSON.stringify +); + +/** + * Estimate gas to transfer to an ETH address. Expensive function, so we + * memoize it. + */ +const estimateGasForEth = memoize((txForEth, api) => { + debug(`Estimating gas for tx.`, txForEth); + return api.eth.estimateGas(txForEth); +}, JSON.stringify); + +/** + * Add some extra gas buffer just to be sure user has enough balance. + * + * @param {BigNumber} estimated - The estimated gas price returned by + * estimateGas. + */ +const addBuffer = estimated => { + const withBuffer = estimated.mul(GAS_MULT_FACTOR); + debug(`Estimated gas ${+estimated}, with buffer ${+withBuffer}.`); + return withBuffer; +}; + +/** + * This.tx is a user-friendly tx object. We convert it now as it can be + * passed to makeContract$.transfer(...). + */ +export const txForErc20 = (tx, token) => { + return { + args: [ + tx.to, + new BigNumber(tx.amount).mul(new BigNumber(10).pow(token.decimals)) + ], + options: { + gasPrice: toWei(tx.gasPrice, 'shannon') // shannon == gwei + } + }; +}; + +/** + * This.tx is a user-friendly tx object. We convert it now as it can be + * passed to post$(tx). + */ +export const txForEth = tx => { + return { + gasPrice: toWei(tx.gasPrice, 'shannon'), // shannon == gwei + to: tx.to, + value: toWei(tx.amount.toString()) + }; +}; diff --git a/packages/fether-react/src/utils/withBalance.js b/packages/fether-react/src/utils/withBalance.js index e852cadb..c4442e73 100644 --- a/packages/fether-react/src/utils/withBalance.js +++ b/packages/fether-react/src/utils/withBalance.js @@ -16,35 +16,35 @@ import { fromWei } from '@parity/api/lib/util/wei'; import light from 'light-hoc'; /** - * A HOC on light.js to get the current balance. + * A HOC on light.js to get the current balance. The inner component needs to + * have a `token` field in its props. * * @example - * @withBalance() + * @withBalance * class MyComponent extends React.Component{ * * } */ -export default (propsSelector = ({ token }) => token) => - light({ - balance: ownProps => { - // Find our token object in the props - const token = propsSelector(ownProps); +export default light({ + balance: ownProps => { + // Find our token object in the props + const { token } = ownProps; - if (!token.address) { - return empty(); - } - return token.address === 'ETH' - ? myBalance$().pipe( - map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null - map(value => value && fromWei(value)) - ) - : defaultAccount$().pipe( - filter(x => x), - switchMap(defaultAccount => - makeContract$(token.address, abi).balanceOf$(defaultAccount) - ), - map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null - map(value => value && value.div(10 ** token.decimals)) - ); + if (!token || !token.address) { + return empty(); } - }); + return token.address === 'ETH' + ? myBalance$().pipe( + map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null + map(value => value && fromWei(value)) + ) + : defaultAccount$().pipe( + filter(x => x), + switchMap(defaultAccount => + makeContract$(token.address, abi).balanceOf$(defaultAccount) + ), + map(value => (isNullOrLoading(value) ? null : value)), // Transform loading state to null + map(value => value && value.div(10 ** token.decimals)) + ); + } +}); diff --git a/yarn.lock b/yarn.lock index 90f7dd45..b8d3e188 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2919,6 +2919,10 @@ chalk@~0.4.0: has-color "~0.1.0" strip-ansi "~0.1.0" +change-emitter@^0.1.2: + version "0.1.6" + resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" + character-entities-legacy@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" @@ -3870,6 +3874,10 @@ dateformat@^3.0.0, dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" +debounce-promise@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debounce-promise/-/debounce-promise-3.1.0.tgz#25035f4b45017bd51a7bef8b3bd9f6401dc47423" + debug-log@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" @@ -5190,7 +5198,7 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.8.16: +fbjs@^0.8.1, fbjs@^0.8.16: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" dependencies: @@ -5276,6 +5284,10 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +final-form@^4.8.3: + version "4.8.3" + resolved "https://registry.yarnpkg.com/final-form/-/final-form-4.8.3.tgz#86a03da6cd6459ed8fe3737dbd9dc87ed40c11d7" + finalhandler@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" @@ -5993,7 +6005,7 @@ hoist-non-react-statics@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -hoist-non-react-statics@^2.5.0: +hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" @@ -7053,6 +7065,10 @@ jest@20.0.4: dependencies: jest-cli "^20.0.4" +jquery@x.*: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" + js-base64@^2.1.8, js-base64@^2.1.9: version "2.4.5" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.5.tgz#e293cd3c7c82f070d700fc7a1ca0a2e69f101f92" @@ -7284,6 +7300,10 @@ keccak@^1.0.2: nan "^2.2.1" safe-buffer "^5.1.0" +keyboard-key@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/keyboard-key/-/keyboard-key-1.0.1.tgz#a946294fe59ad5431c63a3ea269f023e51fac6aa" + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -9552,6 +9572,10 @@ react-error-overlay@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4" +react-final-form@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-3.6.4.tgz#1ca37935c2af0bc659a53b293dd84a75d2381548" + react-lifecycles-compat@^3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -9795,6 +9819,17 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recompose@^0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.27.1.tgz#1a49e931f183634516633bbb4f4edbfd3f38a7ba" + dependencies: + babel-runtime "^6.26.0" + change-emitter "^0.1.2" + fbjs "^0.8.1" + hoist-non-react-statics "^2.3.1" + react-lifecycles-compat "^3.0.2" + symbol-observable "^1.0.4" + recursive-readdir@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" @@ -10353,6 +10388,23 @@ selfsigned@^1.9.1: dependencies: node-forge "0.7.5" +semantic-ui-css@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semantic-ui-css/-/semantic-ui-css-2.3.2.tgz#f84ffaf7559c17401db43e2b44b7b4caaab54836" + dependencies: + jquery x.* + +semantic-ui-react@^0.81.3: + version "0.81.3" + resolved "https://registry.yarnpkg.com/semantic-ui-react/-/semantic-ui-react-0.81.3.tgz#ad98917c44cda4b316ee841d67dc071e16e93e9c" + dependencies: + "@babel/runtime" "^7.0.0-beta.49" + classnames "^2.2.5" + keyboard-key "^1.0.1" + lodash "^4.17.10" + prop-types "^15.6.1" + shallowequal "^1.0.2" + semistandard@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/semistandard/-/semistandard-12.0.1.tgz#82190c720b01cf68e3051e0985578f0b1d596683" @@ -10474,6 +10526,10 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +shallowequal@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -11082,7 +11138,7 @@ symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" -symbol-observable@^1.0.2, symbol-observable@^1.1.0: +symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" -- GitLab From eb1043f6260a68e7cb60841c551f1889d2efb6a3 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Wed, 4 Jul 2018 19:59:35 +0200 Subject: [PATCH 11/17] Make signer page work with new form --- packages/fether-react/src/App/App.js | 5 +- .../fether-react/src/Send/Signer/Signer.js | 106 ++++++++---------- .../fether-react/src/Send/TxForm/TxForm.js | 32 +++--- 3 files changed, 62 insertions(+), 81 deletions(-) diff --git a/packages/fether-react/src/App/App.js b/packages/fether-react/src/App/App.js index 8623d674..a624480f 100644 --- a/packages/fether-react/src/App/App.js +++ b/packages/fether-react/src/App/App.js @@ -14,7 +14,6 @@ import { import { inject, observer } from 'mobx-react'; import Accounts from '../Accounts'; -import Debug from '../utils/debug'; import Onboarding from '../Onboarding'; import Overlay from '../Overlay'; import Send from '../Send'; @@ -22,7 +21,6 @@ import { STATUS } from '../stores/healthStore'; import Tokens from '../Tokens'; import Whitelist from '../Whitelist'; -const debug = Debug('App'); // Use MemoryRouter for production viewing in file:// protocol // https://github.com/facebook/create-react-app/issues/3591 const Router = @@ -31,8 +29,7 @@ const Router = @inject('healthStore', 'onboardingStore') @observer class App extends Component { - componentDidCatch (err) { - debug(err.message); + componentDidCatch () { if (process.env.NODE_ENV !== 'development') { // Redirect to '/' on errors window.location.href = '/'; diff --git a/packages/fether-react/src/Send/Signer/Signer.js b/packages/fether-react/src/Send/Signer/Signer.js index b491aee6..0b968891 100644 --- a/packages/fether-react/src/Send/Signer/Signer.js +++ b/packages/fether-react/src/Send/Signer/Signer.js @@ -4,11 +4,10 @@ // SPDX-License-Identifier: BSD-3-Clause import React, { Component } from 'react'; -import { findDOMNode } from 'react-dom'; -import { FormField, Header } from 'fether-ui'; +import { Field, Form } from 'react-final-form'; +import { Form as FetherForm, Header } from 'fether-ui'; import { inject, observer } from 'mobx-react'; -import { Link } from 'react-router-dom'; -import ReactTooltip from 'react-tooltip'; +import { Link, Redirect } from 'react-router-dom'; import { withProps } from 'recompose'; import TokenBalance from '../../Tokens/TokensList/TokenBalance'; @@ -26,7 +25,7 @@ class Signer extends Component { }; handleAccept = event => { - const { history, sendStore } = this.props; + const { history, sendStore, token } = this.props; const { password } = this.state; event.preventDefault(); @@ -34,38 +33,27 @@ class Signer extends Component { this.setState({ isSending: true }, () => { sendStore .send(password) - .then(() => history.push('/send/sent')) + .then(() => history.push(`/send/${token.address}/sent`)) .catch(error => { - this.setState({ error, isSending: false }, () => - ReactTooltip.show(findDOMNode(this.tooltip)) - ); + this.setState({ error, isSending: false }); }); }); }; - handleCancel = () => { - const { history } = this.props; - history.goBack(); - }; - handleChangePassword = ({ target: { value } }) => { this.setState({ error: null, password: value }); }; - /** - * TODO All this tooltips refs etc should go inside a React validation - * library. - */ - handleTooltipRef = ref => { - this.tooltip = ref; - }; - render () { const { + history, sendStore: { tx }, token } = this.props; - const { error, isSending, password } = this.state; + + if (!tx || !token) { + return ; + } return (
@@ -75,7 +63,7 @@ class Signer extends Component { Close } - title={

Send {token.name}

} + title={token &&

Send {token.name}

} />
@@ -94,41 +82,41 @@ class Signer extends Component {
{tx.to}
, -
-
-

Enter your password to confirm this transaction.

-
- -
- -
- - -
+
( + +
+

Enter your password to confirm this transaction.

+
+ + + + + + )} + /> ]} onClick={null} token={token} diff --git a/packages/fether-react/src/Send/TxForm/TxForm.js b/packages/fether-react/src/Send/TxForm/TxForm.js index a5c93a14..52b0578d 100644 --- a/packages/fether-react/src/Send/TxForm/TxForm.js +++ b/packages/fether-react/src/Send/TxForm/TxForm.js @@ -27,10 +27,9 @@ const MIN_GAS_PRICE = 3; // Safelow gas price from GasStation, in Gwei @withBalance @observer class Send extends Component { - handleSubmit = event => { - event.preventDefault(); + handleSubmit = values => { const { history, sendStore, token } = this.props; - sendStore.setTx(event); + sendStore.setTx(values); history.push(`/send/${token.address}/signer`); }; @@ -125,8 +124,16 @@ class Send extends Component { * Estimate gas amount, and validate that the user has enough balance to make * the tx. */ - validateBalance = debounce(async values => { + validateAmount = debounce(async values => { try { + if (!values.amount || isNaN(values.amount)) { + return { amount: 'Please enter a valid amount' }; + } else if (values.amount < 0) { + return { amount: 'Please enter a positive amount ' }; + } else if (this.props.balance && this.props.balance.lt(values.amount)) { + return { amount: "You don't have enough balance" }; + } + const estimated = await estimateGas( values, this.props.token, @@ -153,23 +160,12 @@ class Send extends Component { }, 1000); validateForm = values => { - if (!values.amount || isNaN(values.amount)) { - return { amount: 'Please enter a valid amount' }; - } - - if (values.amount < 0) { - return { amount: 'Please enter a positive amount ' }; - } - - if (this.props.balance && this.props.balance.lt(values.amount)) { - return { amount: "You don't have enough balance" }; - } - + const errors = {}; if (!isAddress(values.to)) { - return { to: 'Please enter a valid Ethereum address' }; + errors.to = 'Please enter a valid Ethereum address'; } - return this.validateBalance(values); + return Object.keys(errors).length ? errors : this.validateAmount(values); }; } -- GitLab From 5e7402e3be7968fc86fbd044057ad2af8b98fa12 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 10:32:32 +0200 Subject: [PATCH 12/17] Use FetherForm in Signer page --- .../fether-react/src/Send/Signer/Signer.js | 56 ++++++++----------- .../src/assets/sass/shared/_form.scss | 4 ++ packages/fether-ui/src/Form/Field/Field.js | 4 +- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/packages/fether-react/src/Send/Signer/Signer.js b/packages/fether-react/src/Send/Signer/Signer.js index 0b968891..bb21f860 100644 --- a/packages/fether-react/src/Send/Signer/Signer.js +++ b/packages/fether-react/src/Send/Signer/Signer.js @@ -18,30 +18,15 @@ import TokenBalance from '../../Tokens/TokensList/TokenBalance'; })) @observer class Signer extends Component { - state = { - error: null, - isSending: false, - password: '' - }; - - handleAccept = event => { + handleAccept = values => { const { history, sendStore, token } = this.props; - const { password } = this.state; - - event.preventDefault(); - this.setState({ isSending: true }, () => { - sendStore - .send(password) - .then(() => history.push(`/send/${token.address}/sent`)) - .catch(error => { - this.setState({ error, isSending: false }); - }); - }); - }; - - handleChangePassword = ({ target: { value } }) => { - this.setState({ error: null, password: value }); + return sendStore + .send(values.password) + .then(() => history.push(`/send/${token.address}/sent`)) + .catch(error => ({ + password: error + })); }; render () { @@ -52,7 +37,7 @@ class Signer extends Component { } = this.props; if (!tx || !token) { - return ; + return ; } return ( @@ -71,16 +56,20 @@ class Signer extends Component { -
- -
- {tx.amount} {token.symbol} -
-
-
- -
{tx.to}
-
+ + +
,
} -- GitLab From 6e07a6830ab8f14b0e6a1358721967333a075384 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 10:59:28 +0200 Subject: [PATCH 13/17] Make signer page work --- .../fether-react/src/Send/Signer/Signer.js | 8 +-- .../fether-react/src/stores/parityStore.js | 3 +- packages/fether-react/src/stores/sendStore.js | 62 ++----------------- packages/fether-ui/src/Form/Field/Field.js | 4 +- 4 files changed, 13 insertions(+), 64 deletions(-) diff --git a/packages/fether-react/src/Send/Signer/Signer.js b/packages/fether-react/src/Send/Signer/Signer.js index bb21f860..5b938ee6 100644 --- a/packages/fether-react/src/Send/Signer/Signer.js +++ b/packages/fether-react/src/Send/Signer/Signer.js @@ -22,10 +22,10 @@ class Signer extends Component { const { history, sendStore, token } = this.props; return sendStore - .send(values.password) + .send(token, values.password) .then(() => history.push(`/send/${token.address}/sent`)) .catch(error => ({ - password: error + password: error.text })); }; @@ -74,7 +74,7 @@ class Signer extends Component { ( + render={({ errors, handleSubmit, pristine, submitting }) => (

Enter your password to confirm this transaction.

@@ -99,7 +99,7 @@ class Signer extends Component { diff --git a/packages/fether-react/src/stores/parityStore.js b/packages/fether-react/src/stores/parityStore.js index 365267ab..0c026a06 100644 --- a/packages/fether-react/src/stores/parityStore.js +++ b/packages/fether-react/src/stores/parityStore.js @@ -27,6 +27,7 @@ export class ParityStore { // Retrieve token from localStorage const token = store.get(LS_KEY); if (token) { + debug('Got token from localStorage.'); this.setToken(token); } @@ -99,6 +100,7 @@ export class ParityStore { } // If `parity signer new-token` has successfully given us a token back, // then we submit it + debug('Successfully received new token.'); this.setToken(token); }); }; @@ -137,7 +139,6 @@ export class ParityStore { return; } - debug(`Setting token in localStorage.`); this.token = token; // If we receive a new token, then we try to connect to the Api with this diff --git a/packages/fether-react/src/stores/sendStore.js b/packages/fether-react/src/stores/sendStore.js index 8103f675..f20c83ef 100644 --- a/packages/fether-react/src/stores/sendStore.js +++ b/packages/fether-react/src/stores/sendStore.js @@ -7,12 +7,9 @@ import abi from '@parity/shared/lib/contracts/abi/eip20'; import { action, computed, observable } from 'mobx'; import { BigNumber } from 'bignumber.js'; import { blockNumber$, makeContract$, post$ } from '@parity/light.js'; -import memoize from 'lodash/memoize'; -import noop from 'lodash/noop'; import Debug from '../utils/debug'; import parityStore from './parityStore'; -import tokensStore from './tokensStore'; import { txForErc20, txForEth } from '../utils/estimateGas'; const debug = Debug('sendStore'); @@ -22,7 +19,6 @@ const DEFAULT_GAS = new BigNumber(21000 * GAS_MULT_FACTOR); // Default gas amoun export class SendStore { @observable blockNumber; // Current block number, used to calculate tx confirmations. @observable estimated = DEFAULT_GAS; // Estimated gas amount for this transaction. - @observable tokenAddress; // 'ETH', or the token contract address tx = {}; // The actual tx we are sending. No need to be observable. @observable txStatus; // Status of the tx, see wiki for details. @@ -66,64 +62,21 @@ export class SendStore { return makeContract$(this.tokenAddress, abi); } - /** - * Estimate the amount of gas for our transaction. - */ - estimateGas = () => { - if (!this.tx || !Object.keys(this.tx).length) { - return Promise.reject(new Error('Tx not set in sendStore.')); - } - - if (this.tokenAddress === 'ETH') { - return this.estimateGasForEth(txForEth(this.tx)); - } else { - return this.estimateGasForErc20( - txForErc20(this.tx, tokensStore.tokens[this.tokenAddress]) - ); - } - }; - - /** - * Estimate gas to transfer in ERC20 contract. Expensive function, so we - * memoize it. - */ - estimateGasForErc20 = memoize( - txForErc20 => - this.contract.contractObject.instance.transfer - .estimateGas(txForErc20.options, txForErc20.args) - .then(this.setEstimated) - .catch(noop), - JSON.stringify - ); - - /** - * Estimate gas to transfer to an ETH address. Expensive function, so we - * memoize it. - */ - estimateGasForEth = memoize( - txForEth => - parityStore.api.eth - .estimateGas(txForEth) - .then(this.setEstimated) - .catch(noop), - JSON.stringify - ); - /** * Create a transaction. */ - send = password => { + send = (token, password) => { const send$ = - this.tokenAddress === 'ETH' + token.address === 'ETH' ? post$(txForEth(this.tx)) : this.contract.transfer$( - ...txForErc20(this.tx).args, - txForErc20(this.tx).options + ...txForErc20(this.tx, token).args, + txForErc20(this.tx, token).options ); debug( 'Sending tx.', - this.tokenAddress === 'ETH' ? this.txForEth : this.txForErc20 + token.address === 'ETH' ? this.txForEth : this.txForErc20 ); return new Promise((resolve, reject) => { @@ -151,11 +104,6 @@ export class SendStore { debug('Estimated gas,', +estimated, ', with buffer,', +this.estimated); }; - @action - setTokenAddress = tokenAddress => { - this.tokenAddress = tokenAddress; - }; - @action setTx = tx => { this.tx = tx; diff --git a/packages/fether-ui/src/Form/Field/Field.js b/packages/fether-ui/src/Form/Field/Field.js index 90780166..b1681714 100644 --- a/packages/fether-ui/src/Form/Field/Field.js +++ b/packages/fether-ui/src/Form/Field/Field.js @@ -18,10 +18,10 @@ export const Field = ({
} -- GitLab From 51018cd3e44084d3db389763e9efe3229112e160 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 11:00:17 +0200 Subject: [PATCH 14/17] Remove useless variable --- packages/fether-react/src/Send/Signer/Signer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fether-react/src/Send/Signer/Signer.js b/packages/fether-react/src/Send/Signer/Signer.js index 5b938ee6..338de447 100644 --- a/packages/fether-react/src/Send/Signer/Signer.js +++ b/packages/fether-react/src/Send/Signer/Signer.js @@ -74,7 +74,7 @@ class Signer extends Component { ( + render={({ handleSubmit, pristine, submitting }) => (

Enter your password to confirm this transaction.

-- GitLab From c3164b241cc1c7e61d7b12dd1e3d4ba73f8fbcab Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 11:32:51 +0200 Subject: [PATCH 15/17] Make tx work for tokens --- .../fether-react/src/Send/TxForm/TxForm.js | 24 ++++++----- packages/fether-react/src/stores/sendStore.js | 40 ++++--------------- .../fether-react/src/utils/estimateGas.js | 25 +++++++----- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/packages/fether-react/src/Send/TxForm/TxForm.js b/packages/fether-react/src/Send/TxForm/TxForm.js index 52b0578d..1ad25625 100644 --- a/packages/fether-react/src/Send/TxForm/TxForm.js +++ b/packages/fether-react/src/Send/TxForm/TxForm.js @@ -126,21 +126,25 @@ class Send extends Component { */ validateAmount = debounce(async values => { try { - if (!values.amount || isNaN(values.amount)) { + const { balance, parityStore, token } = this.props; + const amount = +values.amount; + + if (!amount || isNaN(amount)) { return { amount: 'Please enter a valid amount' }; - } else if (values.amount < 0) { + } else if (amount < 0) { return { amount: 'Please enter a positive amount ' }; - } else if (this.props.balance && this.props.balance.lt(values.amount)) { + } else if (balance && balance.lt(amount)) { return { amount: "You don't have enough balance" }; } - const estimated = await estimateGas( - values, - this.props.token, - this.props.parityStore.api - ); + if (token.address !== 'ETH') { + // No need to estimate gas for tokens. + // TODO Make sure that user has enough ETH balance + return; + } + + const estimated = await estimateGas(values, token, parityStore.api); - const { balance } = this.props; if (!balance || isNaN(estimated)) { throw new Error('No "balance" or "estimated" value.'); } @@ -149,7 +153,7 @@ class Send extends Component { toWei(balance).minus(estimated.mul(toWei(values.gasPrice, 'shannon'))) ); - if (values.amount > maxAmount) { + if (amount > maxAmount) { return { amount: "You don't have enough balance" }; } } catch (err) { diff --git a/packages/fether-react/src/stores/sendStore.js b/packages/fether-react/src/stores/sendStore.js index f20c83ef..950dbd8f 100644 --- a/packages/fether-react/src/stores/sendStore.js +++ b/packages/fether-react/src/stores/sendStore.js @@ -3,22 +3,17 @@ // // SPDX-License-Identifier: BSD-3-Clause -import abi from '@parity/shared/lib/contracts/abi/eip20'; import { action, computed, observable } from 'mobx'; -import { BigNumber } from 'bignumber.js'; -import { blockNumber$, makeContract$, post$ } from '@parity/light.js'; +import { blockNumber$, post$ } from '@parity/light.js'; +import { contractForToken, txForErc20, txForEth } from '../utils/estimateGas'; import Debug from '../utils/debug'; import parityStore from './parityStore'; -import { txForErc20, txForEth } from '../utils/estimateGas'; const debug = Debug('sendStore'); -const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer. -const DEFAULT_GAS = new BigNumber(21000 * GAS_MULT_FACTOR); // Default gas amount export class SendStore { @observable blockNumber; // Current block number, used to calculate tx confirmations. - @observable estimated = DEFAULT_GAS; // Estimated gas amount for this transaction. tx = {}; // The actual tx we are sending. No need to be observable. @observable txStatus; // Status of the tx, see wiki for details. @@ -51,33 +46,18 @@ export class SendStore { return this.blockNumber - +this.txStatus.confirmed.blockNumber; } - /** - * If it's a token, then return the makeContract$ object. - */ - @computed - get contract () { - if (this.tokenAddress === 'ETH') { - return null; - } - return makeContract$(this.tokenAddress, abi); - } - /** * Create a transaction. */ send = (token, password) => { + const tx = + token.address === 'ETH' ? txForEth(this.tx) : txForErc20(this.tx, token); const send$ = token.address === 'ETH' - ? post$(txForEth(this.tx)) - : this.contract.transfer$( - ...txForErc20(this.tx, token).args, - txForErc20(this.tx, token).options - ); + ? post$(tx) + : contractForToken(token.address).transfer$(...tx.args, tx.options); - debug( - 'Sending tx.', - token.address === 'ETH' ? this.txForEth : this.txForErc20 - ); + debug('Sending tx.', tx); return new Promise((resolve, reject) => { send$.subscribe(txStatus => { @@ -98,12 +78,6 @@ export class SendStore { this.blockNumber = blockNumber; }; - @action - setEstimated = estimated => { - this.estimated = estimated.mul(GAS_MULT_FACTOR); - debug('Estimated gas,', +estimated, ', with buffer,', +this.estimated); - }; - @action setTx = tx => { this.tx = tx; diff --git a/packages/fether-react/src/utils/estimateGas.js b/packages/fether-react/src/utils/estimateGas.js index 375f119b..72ad7ec5 100644 --- a/packages/fether-react/src/utils/estimateGas.js +++ b/packages/fether-react/src/utils/estimateGas.js @@ -3,7 +3,9 @@ // // SPDX-License-Identifier: BSD-3-Clause +import abi from '@parity/shared/lib/contracts/abi/eip20'; import BigNumber from 'bignumber.js'; +import { makeContract$ } from '@parity/light.js'; import memoize from 'lodash/memoize'; import { toWei } from '@parity/api/lib/util/wei'; @@ -12,6 +14,10 @@ import Debug from './debug'; const debug = Debug('estimateGas'); const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer. +export const contractForToken = memoize(tokenAddress => + makeContract$(tokenAddress, abi) +); + /** * Estimate the amount of gas for our transaction. */ @@ -23,7 +29,7 @@ export const estimateGas = (tx, token, api) => { if (token.address === 'ETH') { return estimateGasForEth(txForEth(tx), api).then(addBuffer); } else { - return estimateGasForErc20(txForErc20(tx, token), api).then(addBuffer); + return estimateGasForErc20(txForErc20(tx, token), token).then(addBuffer); } }; @@ -31,14 +37,15 @@ export const estimateGas = (tx, token, api) => { * Estimate gas to transfer in ERC20 contract. Expensive function, so we * memoize it. */ -const estimateGasForErc20 = memoize( - txForErc20 => - this.contract.contractObject.instance.transfer.estimateGas( - txForErc20.options, - txForErc20.args - ), - JSON.stringify -); +const estimateGasForErc20 = memoize((txForErc20, token) => { + debug(`Estimating gas for tx on token contract.`, token, txForErc20); + return contractForToken( + token.address + ).contractObject.instance.transfer.estimateGas( + txForErc20.options, + txForErc20.args + ); +}, JSON.stringify); /** * Estimate gas to transfer to an ETH address. Expensive function, so we -- GitLab From cb56de5f63e03334397f7012883d70c8d0f8914c Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 11:39:19 +0200 Subject: [PATCH 16/17] Fix tests --- .../fether-react/src/stores/sendStore.spec.js | 146 ++---------------- 1 file changed, 15 insertions(+), 131 deletions(-) diff --git a/packages/fether-react/src/stores/sendStore.spec.js b/packages/fether-react/src/stores/sendStore.spec.js index 7f3c63db..c0d8113c 100644 --- a/packages/fether-react/src/stores/sendStore.spec.js +++ b/packages/fether-react/src/stores/sendStore.spec.js @@ -5,8 +5,6 @@ /* eslint-env jest */ -import abi from '@parity/shared/lib/contracts/abi/eip20'; -import BigNumber from 'bignumber.js'; import lightJs from '@parity/light.js'; // Mocked import parityStore from './parityStore'; @@ -21,11 +19,6 @@ jest.mock('@parity/light.js', () => ({ })) })), makeContract$: jest.fn(() => ({ - contractObject: { - instance: { - transfer: { estimateGas: jest.fn(() => Promise.resolve(123)) } - } - }, transfer$: jest.fn(() => ({ subscribe: jest.fn() })) })), post$: jest.fn(() => ({ @@ -38,9 +31,6 @@ jest.mock('@parity/light.js', () => ({ jest.mock('./parityStore', () => ({ api: { - eth: { - estimateGas: jest.fn(() => Promise.resolve(123)) - }, signer: { confirmRequest: jest.fn(() => Promise.resolve(true)) } @@ -60,6 +50,15 @@ const mockTx = { to: '0x123' }; +const mockErc20Token = { + address: 'foo', + decimals: 18 +}; + +const mockEthToken = { + address: 'ETH' +}; + let sendStore; // Will hold the newly created instance of SendStore in each test beforeEach(() => { sendStore = new SendStore(); @@ -113,149 +112,34 @@ describe('@computed confirmations', () => { }); }); -describe('@computed contract', () => { - test('should create a contract with correct token address if the current token Erc20', () => { - sendStore.setTokenAddress('foo'); - sendStore.contract; // eslint-disable-line - expect(lightJs.makeContract$).toHaveBeenCalledWith('foo', abi); - }); - - test('should return null if the current token is ETH', () => { - sendStore.setTokenAddress('ETH'); - expect(sendStore.contract).toBe(null); - }); -}); - -describe('method estimateGas', () => { - test('should reject and not estimate if no tx is set', () => { - sendStore.estimateGasForErc20 = jest.fn(); - sendStore.estimateGasForEth = jest.fn(); - expect(sendStore.estimateGas()).rejects.toHaveProperty( - 'message', - 'Tx not set in sendStore.' - ); - expect(sendStore.estimateGasForErc20).not.toHaveBeenCalled(); - expect(sendStore.estimateGasForEth).not.toHaveBeenCalled(); - }); - - test('should call estimateGasForErc20 if the current token is Erc20', () => { - sendStore.estimateGasForErc20 = jest.fn(() => 'estimateGasForErc20'); - sendStore.setTokenAddress('foo'); - sendStore.setTx(mockTx); - expect(sendStore.estimateGas()).toBe('estimateGasForErc20'); - expect(sendStore.estimateGasForErc20).toHaveBeenCalled(); - }); - - test('should call estimateGasForEth if the current token is ETH', () => { - sendStore.estimateGasForEth = jest.fn(() => 'estimateGasForEth'); - sendStore.setTokenAddress('ETH'); - sendStore.setTx(mockTx); - expect(sendStore.estimateGas()).toBe('estimateGasForEth'); - expect(sendStore.estimateGasForEth).toHaveBeenCalled(); - }); -}); - -describe('method estimateGasForErc20', () => { - beforeEach(() => { - sendStore.setTokenAddress('foo'); - }); - - test.skip('should call the transfer method on the contract', () => { - sendStore.estimateGasForErc20(mockTx); - expect( - sendStore.contract.contractObject.instance.transfer.estimateGas - ).toHaveBeenCalledWith(mockTx); - }); - - test('should memoize result', () => { - const a = sendStore.estimateGasForErc20(mockTx); - const b = sendStore.estimateGasForErc20(mockTx); - expect(a).toBe(b); - }); -}); - -describe('method estimateGasForEth', () => { - beforeEach(() => { - sendStore.setTokenAddress('ETH'); - }); - - test('should call api.eth.estimateGas', () => { - sendStore.estimateGasForEth(mockTx); - expect(parityStore.api.eth.estimateGas).toHaveBeenCalledWith(mockTx); - }); - - test('should memoize result', () => { - const a = sendStore.estimateGasForEth(mockTx); - const b = sendStore.estimateGasForEth(mockTx); - expect(a).toBe(b); - }); -}); - describe('method send', () => { beforeEach(() => { sendStore.setTx(mockTx); }); test.skip('should call transfer$ if the token is Erc20 and subscribe to it', () => { - sendStore.setTokenAddress('foo'); - sendStore.send(); - expect(sendStore.contract.transfer$).toHaveBeenCalledWith( - sendStore.txForErc20 - ); + sendStore.send(mockErc20Token); + expect(sendStore.contract.transfer$).toHaveBeenCalled(); }); test('should call post$ if the token is ETH and subscribe to it', () => { - sendStore.setTokenAddress('ETH'); - sendStore.send(); - expect(lightJs.post$).toHaveBeenCalledWith(sendStore.txForEth); + sendStore.send(mockEthToken); + expect(lightJs.post$).toHaveBeenCalled(); }); test('should update txStatus', () => { sendStore.setTxStatus = jest.fn(); - sendStore.setTokenAddress('ETH'); - sendStore.send(); + sendStore.send(mockEthToken); expect(sendStore.setTxStatus).toHaveBeenCalledWith({ estimating: true }); }); test('should call acceptRequest when txStatus is requested', () => { sendStore.acceptRequest = jest.fn(() => Promise.resolve(true)); - sendStore.setTokenAddress('ETH'); - sendStore.send('foo'); + sendStore.send(mockEthToken, 'foo'); expect(sendStore.acceptRequest).toHaveBeenCalledWith(1, 'foo'); }); }); -describe('setter setEstimated', () => { - test('should add a 1.25 factor', () => { - sendStore.setEstimated(new BigNumber(2)); - expect(sendStore.estimated).toEqual(new BigNumber(2 * 1.25)); - }); -}); - -describe('@computed txForErc20', () => { - test('should return correct value', () => { - sendStore.setTokenAddress('foo'); - sendStore.setTx(mockTx); - expect(sendStore.txForErc20).toEqual({ - args: ['0x123', new BigNumber('10000000000000000')], - options: { gasPrice: new BigNumber('4000000000') } - }); - }); -}); - -describe('@computed txForEth', () => { - test('should return correct value', () => { - sendStore.setTokenAddress('foo'); - sendStore.setTx(mockTx); - expect(sendStore.txForEth).toEqual({ - gasPrice: new BigNumber('4000000000'), - to: '0x123', - value: new BigNumber('10000000000000000') - }); - }); -}); - storeTests.setterTest(SendStore, 'blockNumber'); -storeTests.setterTest(SendStore, 'tokenAddress'); storeTests.setterTest(SendStore, 'tx'); storeTests.setterTest(SendStore, 'txStatus'); -- GitLab From dba232c05c00f9e71e9a6d84306389dbf508b913 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 5 Jul 2018 11:46:25 +0200 Subject: [PATCH 17/17] Wording --- packages/fether-react/src/Send/TxForm/TxForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fether-react/src/Send/TxForm/TxForm.js b/packages/fether-react/src/Send/TxForm/TxForm.js index 1ad25625..6a65f394 100644 --- a/packages/fether-react/src/Send/TxForm/TxForm.js +++ b/packages/fether-react/src/Send/TxForm/TxForm.js @@ -134,7 +134,7 @@ class Send extends Component { } else if (amount < 0) { return { amount: 'Please enter a positive amount ' }; } else if (balance && balance.lt(amount)) { - return { amount: "You don't have enough balance" }; + return { amount: `You don't have enough ${token.symbol} balance` }; } if (token.address !== 'ETH') { @@ -154,7 +154,7 @@ class Send extends Component { ); if (amount > maxAmount) { - return { amount: "You don't have enough balance" }; + return { amount: "You don't have enough ETH balance" }; } } catch (err) { return { -- GitLab