From 19f61906d3075391efb32c17c99c2cba1f7a3858 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 10 Oct 2018 18:27:06 -0700 Subject: feat: Move over features from zrx-buyer --- packages/instant/src/components/buy_button.tsx | 59 ++++++++++++++++++---- .../instant/src/components/instant_heading.tsx | 39 ++++++++++++-- .../instant/src/components/zero_ex_instant.tsx | 2 + .../src/components/zero_ex_instant_container.tsx | 7 ++- packages/instant/src/constants.ts | 4 ++ .../src/containers/selected_asset_amount_input.ts | 57 +++++++++++++++++++++ .../src/containers/selected_asset_amount_input.tsx | 36 ------------- .../src/containers/selected_asset_buy_button.ts | 59 ++++++++++++++++++++++ .../containers/selected_asset_instant_heading.ts | 26 ++++++++++ packages/instant/src/redux/async_data.ts | 22 ++++++++ packages/instant/src/redux/reducer.ts | 19 ++++++- packages/instant/src/types.ts | 10 ++++ packages/instant/src/util/asset_buyer.ts | 11 ++++ packages/instant/src/util/coinbase_api.ts | 8 +++ packages/instant/src/util/provider.ts | 12 +++++ packages/instant/src/util/web3_wrapper.ts | 5 ++ 16 files changed, 321 insertions(+), 55 deletions(-) create mode 100644 packages/instant/src/constants.ts create mode 100644 packages/instant/src/containers/selected_asset_amount_input.ts delete mode 100644 packages/instant/src/containers/selected_asset_amount_input.tsx create mode 100644 packages/instant/src/containers/selected_asset_buy_button.ts create mode 100644 packages/instant/src/containers/selected_asset_instant_heading.ts create mode 100644 packages/instant/src/redux/async_data.ts create mode 100644 packages/instant/src/util/asset_buyer.ts create mode 100644 packages/instant/src/util/coinbase_api.ts create mode 100644 packages/instant/src/util/provider.ts create mode 100644 packages/instant/src/util/web3_wrapper.ts (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index 5a32b9575..097b8e547 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -1,19 +1,56 @@ +import { BuyQuote } from '@0xproject/asset-buyer'; +import * as _ from 'lodash'; import * as React from 'react'; import { ColorOption } from '../style/theme'; +import { assetBuyer } from '../util/asset_buyer'; +import { web3Wrapper } from '../util/web3_wrapper'; import { Button, Container, Text } from './ui'; -export interface BuyButtonProps {} +export interface BuyButtonProps { + buyQuote?: BuyQuote; + onClick: (buyQuote: BuyQuote) => void; + onBuySuccess: (buyQuote: BuyQuote) => void; + onBuyFailure: (buyQuote: BuyQuote) => void; + text: string; +} -export const BuyButton: React.StatelessComponent = props => ( - - - -); +const boundNoop = _.noop.bind(_); -BuyButton.displayName = 'BuyButton'; +export class BuyButton extends React.Component { + public static defaultProps = { + onClick: boundNoop, + onBuySuccess: boundNoop, + onBuyFailure: boundNoop, + }; + public render(): React.ReactNode { + const shouldDisableButton = _.isUndefined(this.props.buyQuote); + return ( + + + + ); + } + private readonly _handleClick = async () => { + // The button is disabled when there is no buy quote anyway. + if (_.isUndefined(this.props.buyQuote)) { + return; + } + this.props.onClick(this.props.buyQuote); + try { + const txnHash = await assetBuyer.executeBuyQuoteAsync(this.props.buyQuote, { + // HACK: There is a calculation issue in asset-buyer. ETH is refunded anyway so just over-estimate. + ethAmount: this.props.buyQuote.worstCaseQuoteInfo.totalEthAmount.mul(2), + }); + await web3Wrapper.awaitTransactionSuccessAsync(txnHash); + } catch { + this.props.onBuyFailure(this.props.buyQuote); + } + this.props.onBuySuccess(this.props.buyQuote); + }; +} diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index be0414b8d..cde3862c7 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -1,11 +1,42 @@ +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; import * as React from 'react'; +import { ethDecimals } from '../constants'; import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; import { ColorOption } from '../style/theme'; import { Container, Flex, Text } from './ui'; -export interface InstantHeadingProps {} +export interface InstantHeadingProps { + selectedAssetAmount?: BigNumber; + totalEthBaseAmount?: BigNumber; + ethUsdPrice?: BigNumber; +} + +const displaytotalEthBaseAmount = ({ selectedAssetAmount, totalEthBaseAmount }: InstantHeadingProps): string => { + if (_.isUndefined(selectedAssetAmount)) { + return '0 ETH'; + } + if (_.isUndefined(totalEthBaseAmount)) { + return '...loading'; + } + const ethUnitAmount = Web3Wrapper.toUnitAmount(totalEthBaseAmount, ethDecimals); + const roundedAmount = ethUnitAmount.round(4); + return `${roundedAmount} ETH`; +}; + +const displayUsdAmount = ({ totalEthBaseAmount, selectedAssetAmount, ethUsdPrice }: InstantHeadingProps): string => { + if (_.isUndefined(selectedAssetAmount)) { + return '$0.00'; + } + if (_.isUndefined(totalEthBaseAmount) || _.isUndefined(ethUsdPrice)) { + return '...loading'; + } + const ethUnitAmount = Web3Wrapper.toUnitAmount(totalEthBaseAmount, ethDecimals); + return `$${ethUnitAmount.mul(ethUsdPrice).round(2)}`; +}; export const InstantHeading: React.StatelessComponent = props => ( @@ -26,18 +57,18 @@ export const InstantHeading: React.StatelessComponent = pro - rep + zrx - 0 ETH + {displaytotalEthBaseAmount(props)} - $0.00 + {displayUsdAmount(props)} diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index 0e6230d1b..17ef737c9 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Provider } from 'react-redux'; +import { asyncData } from '../redux/async_data'; import { store } from '../redux/store'; import { fonts } from '../style/fonts'; import { theme, ThemeProvider } from '../style/theme'; @@ -8,6 +9,7 @@ import { theme, ThemeProvider } from '../style/theme'; import { ZeroExInstantContainer } from './zero_ex_instant_container'; fonts.include(); +asyncData.fetchAndDispatchToStore(); export interface ZeroExInstantProps {} diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 716227b51..4ff8b51bd 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; +import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button'; +import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading'; + import { ColorOption } from '../style/theme'; import { BuyButton } from './buy_button'; @@ -12,9 +15,9 @@ export interface ZeroExInstantContainerProps {} export const ZeroExInstantContainer: React.StatelessComponent = props => ( - + - + ); diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts new file mode 100644 index 000000000..397c9b07a --- /dev/null +++ b/packages/instant/src/constants.ts @@ -0,0 +1,4 @@ +export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/'; +export const zrxContractAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; +export const zrxDecimals = 18; +export const ethDecimals = 18; diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts new file mode 100644 index 000000000..b731b889a --- /dev/null +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -0,0 +1,57 @@ +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { zrxContractAddress, zrxDecimals } from '../constants'; +import { State } from '../redux/reducer'; +import { ColorOption } from '../style/theme'; +import { Action, ActionTypes, AsyncProcessState } from '../types'; +import { assetBuyer } from '../util/asset_buyer'; + +import { AmountInput } from '../components/amount_input'; + +export interface SelectedAssetAmountInputProps { + fontColor?: ColorOption; + fontSize?: string; +} + +interface ConnectedState { + value?: BigNumber; +} + +interface ConnectedDispatch { + onChange?: (value?: BigNumber) => void; +} + +const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({ + value: state.selectedAssetAmount, +}); + +const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ + onChange: async value => { + // Update the input + dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }); + // invalidate the last buy quote. + dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: undefined }); + // reset our buy state + dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.NONE }); + if (!_.isUndefined(value)) { + // get a new buy quote. + const baseUnitValue = Web3Wrapper.toBaseUnitAmount(value, zrxDecimals); + const newBuyQuote = await assetBuyer.getBuyQuoteForERC20TokenAddressAsync( + zrxContractAddress, + baseUnitValue, + ); + // invalidate the last buy quote. + dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: newBuyQuote }); + } + }, +}); + +export const SelectedAssetAmountInput: React.ComponentClass = connect( + mapStateToProps, + mapDispatchToProps, +)(AmountInput); diff --git a/packages/instant/src/containers/selected_asset_amount_input.tsx b/packages/instant/src/containers/selected_asset_amount_input.tsx deleted file mode 100644 index 800a4c568..000000000 --- a/packages/instant/src/containers/selected_asset_amount_input.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { BigNumber } from '@0xproject/utils'; -import * as React from 'react'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; - -import { State } from '../redux/reducer'; -import { ColorOption } from '../style/theme'; -import { Action, ActionTypes } from '../types'; - -import { AmountInput } from '../components/amount_input'; - -export interface SelectedAssetAmountInputProps { - fontColor?: ColorOption; - fontSize?: string; -} - -interface ConnectedState { - value?: BigNumber; -} - -interface ConnectedDispatch { - onChange?: (value?: BigNumber) => void; -} - -const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({ - value: state.selectedAssetAmount, -}); - -const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - onChange: value => dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }), -}); - -export const SelectedAssetAmountInput: React.ComponentClass = connect( - mapStateToProps, - mapDispatchToProps, -)(AmountInput); diff --git a/packages/instant/src/containers/selected_asset_buy_button.ts b/packages/instant/src/containers/selected_asset_buy_button.ts new file mode 100644 index 000000000..f537294e4 --- /dev/null +++ b/packages/instant/src/containers/selected_asset_buy_button.ts @@ -0,0 +1,59 @@ +import { BuyQuote } from '@0xproject/asset-buyer'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { State } from '../redux/reducer'; +import { Action, ActionTypes, AsyncProcessState } from '../types'; +import { assetBuyer } from '../util/asset_buyer'; +import { web3Wrapper } from '../util/web3_wrapper'; + +import { BuyButton } from '../components/buy_button'; + +export interface SelectedAssetBuyButtonProps {} + +interface ConnectedState { + text: string; + buyQuote?: BuyQuote; +} + +interface ConnectedDispatch { + onClick: (buyQuote: BuyQuote) => void; + onBuySuccess: (buyQuote: BuyQuote) => void; + onBuyFailure: (buyQuote: BuyQuote) => void; +} + +const textForState = (state: AsyncProcessState): string => { + switch (state) { + case AsyncProcessState.NONE: + return 'Buy'; + case AsyncProcessState.PENDING: + return '...Loading'; + case AsyncProcessState.SUCCESS: + return 'Success!'; + case AsyncProcessState.FAILURE: + return 'Failed'; + default: + return 'Buy'; + } +}; + +const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyButtonProps): ConnectedState => ({ + text: textForState(state.selectedAssetBuyState), + buyQuote: state.latestBuyQuote, +}); + +const mapDispatchToProps = (dispatch: Dispatch, ownProps: SelectedAssetBuyButtonProps): ConnectedDispatch => ({ + onClick: buyQuote => + dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.PENDING }), + onBuySuccess: buyQuote => + dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.SUCCESS }), + onBuyFailure: buyQuote => + dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.FAILURE }), +}); + +export const SelectedAssetBuyButton: React.ComponentClass = connect( + mapStateToProps, + mapDispatchToProps, +)(BuyButton); diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts new file mode 100644 index 000000000..a9e853fe1 --- /dev/null +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -0,0 +1,26 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; + +import { State } from '../redux/reducer'; + +import { InstantHeading } from '../components/instant_heading'; + +export interface InstantHeadingProps {} + +interface ConnectedState { + selectedAssetAmount?: BigNumber; + totalEthBaseAmount?: BigNumber; + ethUsdPrice?: BigNumber; +} + +const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ + selectedAssetAmount: state.selectedAssetAmount, + totalEthBaseAmount: _.get(state, 'latestBuyQuote.worstCaseQuoteInfo.totalEthAmount'), + ethUsdPrice: state.ethUsdPrice, +}); + +export const SelectedAssetInstantHeading: React.ComponentClass = connect(mapStateToProps)( + InstantHeading, +); diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts new file mode 100644 index 000000000..3fde2d2e5 --- /dev/null +++ b/packages/instant/src/redux/async_data.ts @@ -0,0 +1,22 @@ +import { BigNumber } from '@0xproject/utils'; + +import { ActionTypes } from '../types'; +import { coinbaseApi } from '../util/coinbase_api'; + +import { store } from './store'; + +export const asyncData = { + fetchAndDispatchToStore: async () => { + let ethUsdPriceStr = '0'; + try { + ethUsdPriceStr = await coinbaseApi.getEthUsdPrice(); + } catch (e) { + // ignore + } finally { + store.dispatch({ + type: ActionTypes.UPDATE_ETH_USD_PRICE, + data: new BigNumber(ethUsdPriceStr), + }); + } + }, +}; diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index 5026895ae..c0c4b06ad 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -1,16 +1,21 @@ +import { BuyQuote } from '@0xproject/asset-buyer'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import { Action, ActionTypes } from '../types'; +import { Action, ActionTypes, AsyncProcessState } from '../types'; export interface State { - ethUsdPrice?: string; selectedAssetAmount?: BigNumber; + selectedAssetBuyState: AsyncProcessState; + ethUsdPrice?: BigNumber; + latestBuyQuote?: BuyQuote; } export const INITIAL_STATE: State = { ethUsdPrice: undefined, + selectedAssetBuyState: AsyncProcessState.NONE, selectedAssetAmount: undefined, + latestBuyQuote: undefined, }; export const reducer = (state: State = INITIAL_STATE, action: Action): State => { @@ -25,6 +30,16 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => ...state, selectedAssetAmount: action.data, }; + case ActionTypes.UPDATE_LATEST_BUY_QUOTE: + return { + ...state, + latestBuyQuote: action.data, + }; + case ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE: + return { + ...state, + selectedAssetBuyState: action.data, + }; default: return state; } diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index d150bd8ab..bbf31cbf4 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -1,6 +1,16 @@ +// Reusable +export enum AsyncProcessState { + NONE, + PENDING, + SUCCESS, + FAILURE, +} + export enum ActionTypes { UPDATE_ETH_USD_PRICE, UPDATE_SELECTED_ASSET_AMOUNT, + UPDATE_SELECTED_ASSET_BUY_STATE, + UPDATE_LATEST_BUY_QUOTE, } export interface Action { diff --git a/packages/instant/src/util/asset_buyer.ts b/packages/instant/src/util/asset_buyer.ts new file mode 100644 index 000000000..b030501c4 --- /dev/null +++ b/packages/instant/src/util/asset_buyer.ts @@ -0,0 +1,11 @@ +import { AssetBuyer } from '@0xproject/asset-buyer'; + +import { sraApiUrl } from '../constants'; + +import { getProvider } from './provider'; + +const provider = getProvider(); + +export const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, sraApiUrl, { + expiryBufferSeconds: 300, +}); diff --git a/packages/instant/src/util/coinbase_api.ts b/packages/instant/src/util/coinbase_api.ts new file mode 100644 index 000000000..63c8077da --- /dev/null +++ b/packages/instant/src/util/coinbase_api.ts @@ -0,0 +1,8 @@ +const baseEndpoint = 'https://api.coinbase.com/v2'; +export const coinbaseApi = { + getEthUsdPrice: async (): Promise => { + const res = await fetch(`${baseEndpoint}/prices/ETH-USD/buy`); + const resJson = await res.json(); + return resJson.data.amount; + }, +}; diff --git a/packages/instant/src/util/provider.ts b/packages/instant/src/util/provider.ts new file mode 100644 index 000000000..49705fd11 --- /dev/null +++ b/packages/instant/src/util/provider.ts @@ -0,0 +1,12 @@ +import { Provider } from 'ethereum-types'; + +export const getProvider = (): Provider => { + const injectedWeb3 = (window as any).web3 || undefined; + try { + // Use MetaMask/Mist provider + return injectedWeb3.currentProvider; + } catch (err) { + // Throws when user doesn't have MetaMask/Mist running + throw new Error(`No injected web3 found: ${err}`); + } +}; diff --git a/packages/instant/src/util/web3_wrapper.ts b/packages/instant/src/util/web3_wrapper.ts new file mode 100644 index 000000000..d7e43521f --- /dev/null +++ b/packages/instant/src/util/web3_wrapper.ts @@ -0,0 +1,5 @@ +import { Web3Wrapper } from '@0xproject/web3-wrapper'; + +import { getProvider } from './provider'; + +export const web3Wrapper = new Web3Wrapper(getProvider()); -- cgit From 63652df3b90ca4ced73d27e09984b67bd32050f4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 11 Oct 2018 15:05:20 -0700 Subject: feat: adjust amount input width --- packages/instant/src/components/amount_input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx index 38810063d..a2113f143 100644 --- a/packages/instant/src/components/amount_input.tsx +++ b/packages/instant/src/components/amount_input.tsx @@ -24,7 +24,7 @@ export class AmountInput extends React.Component { onChange={this._handleChange} value={!_.isUndefined(value) ? value.toString() : ''} placeholder="0.00" - width="2em" + width="2.2em" /> ); -- cgit From 0edd9b32bab41d3eda9bad297f7e5db4a289cc88 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 11 Oct 2018 15:22:55 -0700 Subject: feat: debounce the fetching of new quotes --- .../src/containers/selected_asset_amount_input.ts | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts index b731b889a..fa675b385 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -30,6 +30,19 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps) value: state.selectedAssetAmount, }); +const updateBuyQuote = async (dispatch: Dispatch, assetAmount?: BigNumber): Promise => { + if (_.isUndefined(assetAmount)) { + return; + } + // get a new buy quote. + const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, zrxDecimals); + const newBuyQuote = await assetBuyer.getBuyQuoteForERC20TokenAddressAsync(zrxContractAddress, baseUnitValue); + // invalidate the last buy quote. + dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: newBuyQuote }); +}; + +const debouncedUpdateBuyQuote = _.debounce(updateBuyQuote, 200, { trailing: true }); + const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ onChange: async value => { // Update the input @@ -38,16 +51,7 @@ const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: undefined }); // reset our buy state dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.NONE }); - if (!_.isUndefined(value)) { - // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(value, zrxDecimals); - const newBuyQuote = await assetBuyer.getBuyQuoteForERC20TokenAddressAsync( - zrxContractAddress, - baseUnitValue, - ); - // invalidate the last buy quote. - dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: newBuyQuote }); - } + debouncedUpdateBuyQuote(dispatch, value); }, }); -- cgit From 03b235bb428e8a61934ff603f22f057d8394b56a Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 11 Oct 2018 17:35:22 -0700 Subject: feat: populate order details with information from worst buy quote --- .../instant/src/components/instant_heading.tsx | 14 +--- packages/instant/src/components/order_details.tsx | 92 +++++++++++++++------- .../src/components/zero_ex_instant_container.tsx | 4 +- packages/instant/src/constants.ts | 2 + .../containers/latest_buy_quote_order_details.ts | 26 ++++++ .../src/containers/selected_asset_buy_button.ts | 2 +- packages/instant/src/util/format.ts | 45 +++++++++++ 7 files changed, 143 insertions(+), 42 deletions(-) create mode 100644 packages/instant/src/containers/latest_buy_quote_order_details.ts create mode 100644 packages/instant/src/util/format.ts (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index cde3862c7..951858695 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { ethDecimals } from '../constants'; import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; import { ColorOption } from '../style/theme'; +import { format } from '../util/format'; import { Container, Flex, Text } from './ui'; @@ -19,23 +20,14 @@ const displaytotalEthBaseAmount = ({ selectedAssetAmount, totalEthBaseAmount }: if (_.isUndefined(selectedAssetAmount)) { return '0 ETH'; } - if (_.isUndefined(totalEthBaseAmount)) { - return '...loading'; - } - const ethUnitAmount = Web3Wrapper.toUnitAmount(totalEthBaseAmount, ethDecimals); - const roundedAmount = ethUnitAmount.round(4); - return `${roundedAmount} ETH`; + return format.ethBaseAmount(totalEthBaseAmount, 4, '...loading'); }; const displayUsdAmount = ({ totalEthBaseAmount, selectedAssetAmount, ethUsdPrice }: InstantHeadingProps): string => { if (_.isUndefined(selectedAssetAmount)) { return '$0.00'; } - if (_.isUndefined(totalEthBaseAmount) || _.isUndefined(ethUsdPrice)) { - return '...loading'; - } - const ethUnitAmount = Web3Wrapper.toUnitAmount(totalEthBaseAmount, ethDecimals); - return `$${ethUnitAmount.mul(ethUsdPrice).round(2)}`; + return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, '...loading'); }; export const InstantHeading: React.StatelessComponent = props => ( diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index dbf2c1f0b..78359898f 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -1,53 +1,88 @@ +import { BuyQuoteInfo } from '@0xproject/asset-buyer'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; import * as React from 'react'; +import { BIG_NUMBER_ZERO, ethDecimals } from '../constants'; + import { ColorOption } from '../style/theme'; +import { format } from '../util/format'; import { Container, Flex, Text } from './ui'; -export interface OrderDetailsProps {} - -export const OrderDetails: React.StatelessComponent = props => ( - - - - Order Details - - - - - - -); +export interface OrderDetailsProps { + buyQuoteInfo?: BuyQuoteInfo; + ethUsdPrice?: BigNumber; +} -OrderDetails.displayName = 'OrderDetails'; +export class OrderDetails extends React.Component { + public render(): React.ReactNode { + const { buyQuoteInfo, ethUsdPrice } = this.props; + const ethAssetPrice = _.get(buyQuoteInfo, 'ethPerAssetPrice'); + const ethTokenFee = _.get(buyQuoteInfo, 'feeEthAmount'); + const totalEthAmount = _.get(buyQuoteInfo, 'totalEthAmount'); + return ( + + + + Order Details + + + + + + + ); + } +} export interface OrderDetailsRowProps { name: string; - primaryValue: string; - secondaryValue: string; + ethAmount?: BigNumber; + shouldConvertEthToUnitAmount?: boolean; + ethUsdPrice?: BigNumber; shouldEmphasize?: boolean; } -export const OrderDetailsRow: React.StatelessComponent = props => { - const fontWeight = props.shouldEmphasize ? 700 : 400; +export const OrderDetailsRow: React.StatelessComponent = ({ + name, + ethAmount, + shouldConvertEthToUnitAmount, + ethUsdPrice, + shouldEmphasize, +}) => { + const fontWeight = shouldEmphasize ? 700 : 400; + const usdFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; + const ethFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmount : format.ethUnitAmount; return ( - {props.name} + {name} - ({props.secondaryValue}) + ({usdFormatter(ethAmount, ethUsdPrice)}) - {props.primaryValue} + {ethFormatter(ethAmount)} @@ -57,6 +92,7 @@ export const OrderDetailsRow: React.StatelessComponent = p OrderDetailsRow.defaultProps = { shouldEmphasize: false, + shouldConvertEthToUnitAmount: true, }; OrderDetailsRow.displayName = 'OrderDetailsRow'; diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 4ff8b51bd..44f702aa2 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; +import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details'; import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button'; import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading'; @@ -7,7 +8,6 @@ import { ColorOption } from '../style/theme'; import { BuyButton } from './buy_button'; import { InstantHeading } from './instant_heading'; -import { OrderDetails } from './order_details'; import { Container, Flex } from './ui'; export interface ZeroExInstantContainerProps {} @@ -16,7 +16,7 @@ export const ZeroExInstantContainer: React.StatelessComponent - + diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 397c9b07a..75afc474a 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,3 +1,5 @@ +import { BigNumber } from '@0xproject/utils'; +export const BIG_NUMBER_ZERO = new BigNumber(0); export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/'; export const zrxContractAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; export const zrxDecimals = 18; diff --git a/packages/instant/src/containers/latest_buy_quote_order_details.ts b/packages/instant/src/containers/latest_buy_quote_order_details.ts new file mode 100644 index 000000000..77d84289b --- /dev/null +++ b/packages/instant/src/containers/latest_buy_quote_order_details.ts @@ -0,0 +1,26 @@ +import { BuyQuoteInfo } from '@0xproject/asset-buyer'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; + +import { State } from '../redux/reducer'; + +import { OrderDetails } from '../components/order_details'; + +export interface LatestBuyQuoteOrderDetailsProps {} + +interface ConnectedState { + buyQuoteInfo?: BuyQuoteInfo; + ethUsdPrice?: BigNumber; +} + +const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({ + // use the worst case quote info + buyQuoteInfo: _.get(state, 'latestBuyQuote.worstCaseQuoteInfo'), + ethUsdPrice: state.ethUsdPrice, +}); + +export const LatestBuyQuoteOrderDetails: React.ComponentClass = connect( + mapStateToProps, +)(OrderDetails); diff --git a/packages/instant/src/containers/selected_asset_buy_button.ts b/packages/instant/src/containers/selected_asset_buy_button.ts index f537294e4..678c7e445 100644 --- a/packages/instant/src/containers/selected_asset_buy_button.ts +++ b/packages/instant/src/containers/selected_asset_buy_button.ts @@ -9,7 +9,7 @@ import { Action, ActionTypes, AsyncProcessState } from '../types'; import { assetBuyer } from '../util/asset_buyer'; import { web3Wrapper } from '../util/web3_wrapper'; -import { BuyButton } from '../components/buy_button'; +import { BuyButton, BuyButtonProps } from '../components/buy_button'; export interface SelectedAssetBuyButtonProps {} diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts new file mode 100644 index 000000000..de7eb62e6 --- /dev/null +++ b/packages/instant/src/util/format.ts @@ -0,0 +1,45 @@ +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; + +import { ethDecimals } from '../constants'; + +export const format = { + ethBaseAmount: (ethBaseAmount?: BigNumber, decimalPlaces: number = 4, defaultText: string = '0 ETH'): string => { + if (_.isUndefined(ethBaseAmount)) { + return defaultText; + } + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals); + return format.ethUnitAmount(ethUnitAmount, decimalPlaces); + }, + ethUnitAmount: (ethUnitAmount?: BigNumber, decimalPlaces: number = 4, defaultText: string = '0 ETH'): string => { + if (_.isUndefined(ethUnitAmount)) { + return defaultText; + } + const roundedAmount = ethUnitAmount.round(decimalPlaces); + return `${roundedAmount} ETH`; + }, + ethBaseAmountInUsd: ( + ethBaseAmount?: BigNumber, + ethUsdPrice?: BigNumber, + decimalPlaces: number = 2, + defaultText: string = '$0.00', + ): string => { + if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) { + return defaultText; + } + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals); + return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces); + }, + ethUnitAmountInUsd: ( + ethUnitAmount?: BigNumber, + ethUsdPrice?: BigNumber, + decimalPlaces: number = 2, + defaultText: string = '$0.00', + ): string => { + if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) { + return defaultText; + } + return `$${ethUnitAmount.mul(ethUsdPrice).round(decimalPlaces)}`; + }, +}; -- cgit From 09c5ae4e65f8fddd4504be041f27f9107d12df7d Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 12 Oct 2018 09:55:33 -0700 Subject: feat: have coinbase API return BigNumber for eth-usd price endpoint --- packages/instant/src/redux/async_data.ts | 7 ++++--- packages/instant/src/util/coinbase_api.ts | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 3fde2d2e5..b368491f0 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,5 +1,6 @@ import { BigNumber } from '@0xproject/utils'; +import { BIG_NUMBER_ZERO } from '../constants'; import { ActionTypes } from '../types'; import { coinbaseApi } from '../util/coinbase_api'; @@ -7,15 +8,15 @@ import { store } from './store'; export const asyncData = { fetchAndDispatchToStore: async () => { - let ethUsdPriceStr = '0'; + let ethUsdPrice = BIG_NUMBER_ZERO; try { - ethUsdPriceStr = await coinbaseApi.getEthUsdPrice(); + ethUsdPrice = await coinbaseApi.getEthUsdPrice(); } catch (e) { // ignore } finally { store.dispatch({ type: ActionTypes.UPDATE_ETH_USD_PRICE, - data: new BigNumber(ethUsdPriceStr), + data: ethUsdPrice, }); } }, diff --git a/packages/instant/src/util/coinbase_api.ts b/packages/instant/src/util/coinbase_api.ts index 63c8077da..94a5d3c80 100644 --- a/packages/instant/src/util/coinbase_api.ts +++ b/packages/instant/src/util/coinbase_api.ts @@ -1,8 +1,10 @@ +import { BigNumber } from '@0xproject/utils'; + const baseEndpoint = 'https://api.coinbase.com/v2'; export const coinbaseApi = { - getEthUsdPrice: async (): Promise => { + getEthUsdPrice: async (): Promise => { const res = await fetch(`${baseEndpoint}/prices/ETH-USD/buy`); const resJson = await res.json(); - return resJson.data.amount; + return new BigNumber(resJson.data.amount); }, }; -- cgit From f3391e1250767eb9ef7a74f78eded38fa3c94586 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 12 Oct 2018 11:00:36 -0700 Subject: feat: make redux actions type-sage --- .../instant/src/components/instant_heading.tsx | 2 -- packages/instant/src/components/order_details.tsx | 3 -- .../instant/src/components/zero_ex_instant.tsx | 1 + .../src/components/zero_ex_instant_container.tsx | 2 -- .../src/containers/selected_asset_amount_input.ts | 12 +++++--- .../src/containers/selected_asset_buy_button.ts | 16 ++++------ packages/instant/src/redux/actions.ts | 36 ++++++++++++++++++++++ packages/instant/src/redux/async_data.ts | 5 ++- packages/instant/src/redux/reducer.ts | 4 ++- packages/instant/src/types.ts | 16 +++------- 10 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 packages/instant/src/redux/actions.ts (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index 951858695..377bb20bf 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -1,9 +1,7 @@ import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; -import { ethDecimals } from '../constants'; import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; import { ColorOption } from '../style/theme'; import { format } from '../util/format'; diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 78359898f..66e7e2406 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -1,11 +1,8 @@ import { BuyQuoteInfo } from '@0xproject/asset-buyer'; import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; -import { BIG_NUMBER_ZERO, ethDecimals } from '../constants'; - import { ColorOption } from '../style/theme'; import { format } from '../util/format'; diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index 17ef737c9..f6472e811 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -9,6 +9,7 @@ import { theme, ThemeProvider } from '../style/theme'; import { ZeroExInstantContainer } from './zero_ex_instant_container'; fonts.include(); +// tslint:disable-next-line:no-floating-promises asyncData.fetchAndDispatchToStore(); export interface ZeroExInstantProps {} diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 44f702aa2..b624d712f 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -6,8 +6,6 @@ import { SelectedAssetInstantHeading } from '../containers/selected_asset_instan import { ColorOption } from '../style/theme'; -import { BuyButton } from './buy_button'; -import { InstantHeading } from './instant_heading'; import { Container, Flex } from './ui'; export interface ZeroExInstantContainerProps {} diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts index fa675b385..9e3063ecc 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -6,9 +6,10 @@ import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { zrxContractAddress, zrxDecimals } from '../constants'; +import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; -import { Action, ActionTypes, AsyncProcessState } from '../types'; +import { AsyncProcessState } from '../types'; import { assetBuyer } from '../util/asset_buyer'; import { AmountInput } from '../components/amount_input'; @@ -38,7 +39,7 @@ const updateBuyQuote = async (dispatch: Dispatch, assetAmount?: BigNumbe const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, zrxDecimals); const newBuyQuote = await assetBuyer.getBuyQuoteForERC20TokenAddressAsync(zrxContractAddress, baseUnitValue); // invalidate the last buy quote. - dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: newBuyQuote }); + dispatch(actions.updateLatestBuyQuote(newBuyQuote)); }; const debouncedUpdateBuyQuote = _.debounce(updateBuyQuote, 200, { trailing: true }); @@ -46,11 +47,12 @@ const debouncedUpdateBuyQuote = _.debounce(updateBuyQuote, 200, { trailing: true const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ onChange: async value => { // Update the input - dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }); + dispatch(actions.updateSelectedAssetAmount(value)); // invalidate the last buy quote. - dispatch({ type: ActionTypes.UPDATE_LATEST_BUY_QUOTE, data: undefined }); + dispatch(actions.updateLatestBuyQuote(undefined)); // reset our buy state - dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.NONE }); + dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.NONE)); + // tslint:disable-next-line:no-floating-promises debouncedUpdateBuyQuote(dispatch, value); }, }); diff --git a/packages/instant/src/containers/selected_asset_buy_button.ts b/packages/instant/src/containers/selected_asset_buy_button.ts index 678c7e445..4cbaf5537 100644 --- a/packages/instant/src/containers/selected_asset_buy_button.ts +++ b/packages/instant/src/containers/selected_asset_buy_button.ts @@ -4,12 +4,11 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; -import { Action, ActionTypes, AsyncProcessState } from '../types'; -import { assetBuyer } from '../util/asset_buyer'; -import { web3Wrapper } from '../util/web3_wrapper'; +import { AsyncProcessState } from '../types'; -import { BuyButton, BuyButtonProps } from '../components/buy_button'; +import { BuyButton } from '../components/buy_button'; export interface SelectedAssetBuyButtonProps {} @@ -45,12 +44,9 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyButtonProps): }); const mapDispatchToProps = (dispatch: Dispatch, ownProps: SelectedAssetBuyButtonProps): ConnectedDispatch => ({ - onClick: buyQuote => - dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.PENDING }), - onBuySuccess: buyQuote => - dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.SUCCESS }), - onBuyFailure: buyQuote => - dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, data: AsyncProcessState.FAILURE }), + onClick: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.PENDING)), + onBuySuccess: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.SUCCESS)), + onBuyFailure: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.FAILURE)), }); export const SelectedAssetBuyButton: React.ComponentClass = connect( diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts new file mode 100644 index 000000000..7d07b4950 --- /dev/null +++ b/packages/instant/src/redux/actions.ts @@ -0,0 +1,36 @@ +import { BuyQuote } from '@0xproject/asset-buyer'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { ActionsUnion, AsyncProcessState } from '../types'; + +export interface PlainAction { + type: T; +} + +export interface ActionWithPayload extends PlainAction { + data: P; +} + +export type Action = ActionsUnion; + +function createAction(type: T): PlainAction; +function createAction(type: T, data: P): ActionWithPayload; +function createAction(type: T, data?: P): PlainAction | ActionWithPayload { + return _.isUndefined(data) ? { type } : { type, data }; +} + +export enum ActionTypes { + UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE', + UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT', + UPDATE_SELECTED_ASSET_BUY_STATE = 'UPDATE_SELECTED_ASSET_BUY_STATE', + UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE', +} + +export const actions = { + updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price), + updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), + updateSelectedAssetBuyState: (buyState: AsyncProcessState) => + createAction(ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, buyState), + updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote), +}; diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index b368491f0..348838307 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,9 +1,8 @@ -import { BigNumber } from '@0xproject/utils'; - import { BIG_NUMBER_ZERO } from '../constants'; -import { ActionTypes } from '../types'; import { coinbaseApi } from '../util/coinbase_api'; +import { ActionTypes } from './actions'; + import { store } from './store'; export const asyncData = { diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index c0c4b06ad..56a4ee236 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -2,7 +2,9 @@ import { BuyQuote } from '@0xproject/asset-buyer'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import { Action, ActionTypes, AsyncProcessState } from '../types'; +import { AsyncProcessState } from '../types'; + +import { Action, ActionTypes } from './actions'; export interface State { selectedAssetAmount?: BigNumber; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index bbf31cbf4..ffa5d679e 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -1,3 +1,5 @@ +import { ObjectMap } from '@0xproject/types'; + // Reusable export enum AsyncProcessState { NONE, @@ -6,14 +8,6 @@ export enum AsyncProcessState { FAILURE, } -export enum ActionTypes { - UPDATE_ETH_USD_PRICE, - UPDATE_SELECTED_ASSET_AMOUNT, - UPDATE_SELECTED_ASSET_BUY_STATE, - UPDATE_LATEST_BUY_QUOTE, -} - -export interface Action { - type: ActionTypes; - data?: any; -} +export type FunctionType = (...args: any[]) => any; +export type ActionCreatorsMapObject = ObjectMap; +export type ActionsUnion = ReturnType; -- cgit From f39541436a5088e928660b61bde7cef5153bc7a1 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 12 Oct 2018 16:11:30 -0700 Subject: feat: model asset meta data and add dynamic assetData state --- packages/instant/src/components/amount_input.tsx | 9 ++-- .../instant/src/components/asset_amount_input.tsx | 51 ++++++++++++++++++++++ .../instant/src/components/instant_heading.tsx | 9 +--- packages/instant/src/constants.ts | 2 +- .../src/containers/selected_asset_amount_input.ts | 31 ++++++++----- packages/instant/src/data/asset_meta_data.ts | 15 +++++++ packages/instant/src/redux/reducer.ts | 8 +++- packages/instant/src/types.ts | 22 +++++++++- 8 files changed, 120 insertions(+), 27 deletions(-) create mode 100644 packages/instant/src/components/asset_amount_input.tsx create mode 100644 packages/instant/src/data/asset_meta_data.ts (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx index a2113f143..5b81a3d68 100644 --- a/packages/instant/src/components/amount_input.tsx +++ b/packages/instant/src/components/amount_input.tsx @@ -10,10 +10,13 @@ export interface AmountInputProps { fontColor?: ColorOption; fontSize?: string; value?: BigNumber; - onChange?: (value?: BigNumber) => void; + onChange: (value?: BigNumber) => void; } export class AmountInput extends React.Component { + public static defaultProps = { + onChange: _.noop.bind(_), + }; public render(): React.ReactNode { const { fontColor, fontSize, value } = this.props; return ( @@ -40,8 +43,6 @@ export class AmountInput extends React.Component { return; } } - if (!_.isUndefined(this.props.onChange)) { - this.props.onChange(bigNumberValue); - } + this.props.onChange(bigNumberValue); }; } diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx new file mode 100644 index 000000000..915c66e1e --- /dev/null +++ b/packages/instant/src/components/asset_amount_input.tsx @@ -0,0 +1,51 @@ +import { AssetProxyId } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { assetMetaData } from '../data/asset_meta_data'; +import { ColorOption } from '../style/theme'; + +import { AmountInput, AmountInputProps } from './amount_input'; +import { Container, Text } from './ui'; + +export interface AssetAmountInputProps extends AmountInputProps { + assetData?: string; + onChange: (value?: BigNumber, assetData?: string) => void; +} + +export class AssetAmountInput extends React.Component { + public static defaultProps = { + onChange: _.noop.bind(_), + }; + public render(): React.ReactNode { + const { assetData, onChange, ...rest } = this.props; + return ( + + + + + {this._getLabel()} + + + + ); + } + private readonly _getLabel = (): string => { + const unknownLabel = '???'; + if (_.isUndefined(this.props.assetData)) { + return unknownLabel; + } + const metaData = assetMetaData[this.props.assetData]; + if (_.isUndefined(metaData)) { + return unknownLabel; + } + if (metaData.assetProxyId === AssetProxyId.ERC20) { + return metaData.symbol; + } + return unknownLabel; + }; + private readonly _handleChange = (value?: BigNumber): void => { + this.props.onChange(value, this.props.assetData); + }; +} diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index 377bb20bf..492c1b2c0 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -43,14 +43,7 @@ export const InstantHeading: React.StatelessComponent = pro - - - - - zrx - - - + diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 75afc474a..1fd321c5a 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0xproject/utils'; export const BIG_NUMBER_ZERO = new BigNumber(0); export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/'; -export const zrxContractAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; +export const zrxAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; export const zrxDecimals = 18; export const ethDecimals = 18; diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts index 9e3063ecc..f2ca96ae4 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -5,14 +5,14 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { zrxContractAddress, zrxDecimals } from '../constants'; +import { zrxDecimals } from '../constants'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; import { AsyncProcessState } from '../types'; import { assetBuyer } from '../util/asset_buyer'; -import { AmountInput } from '../components/amount_input'; +import { AssetAmountInput } from '../components/asset_amount_input'; export interface SelectedAssetAmountInputProps { fontColor?: ColorOption; @@ -21,31 +21,40 @@ export interface SelectedAssetAmountInputProps { interface ConnectedState { value?: BigNumber; + assetData?: string; } interface ConnectedDispatch { - onChange?: (value?: BigNumber) => void; + onChange: (value?: BigNumber, assetData?: string) => void; } const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({ value: state.selectedAssetAmount, + assetData: state.selectedAssetData, }); -const updateBuyQuote = async (dispatch: Dispatch, assetAmount?: BigNumber): Promise => { - if (_.isUndefined(assetAmount)) { +const updateBuyQuoteAsync = async ( + dispatch: Dispatch, + assetData?: string, + assetAmount?: BigNumber, +): Promise => { + if (_.isUndefined(assetAmount) || _.isUndefined(assetData)) { return; } // get a new buy quote. const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, zrxDecimals); - const newBuyQuote = await assetBuyer.getBuyQuoteForERC20TokenAddressAsync(zrxContractAddress, baseUnitValue); + const newBuyQuote = await assetBuyer.getBuyQuoteAsync(assetData, baseUnitValue); // invalidate the last buy quote. dispatch(actions.updateLatestBuyQuote(newBuyQuote)); }; -const debouncedUpdateBuyQuote = _.debounce(updateBuyQuote, 200, { trailing: true }); +const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trailing: true }); -const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - onChange: async value => { +const mapDispatchToProps = ( + dispatch: Dispatch, + _ownProps: SelectedAssetAmountInputProps, +): ConnectedDispatch => ({ + onChange: (value, assetData) => { // Update the input dispatch(actions.updateSelectedAssetAmount(value)); // invalidate the last buy quote. @@ -53,11 +62,11 @@ const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ // reset our buy state dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.NONE)); // tslint:disable-next-line:no-floating-promises - debouncedUpdateBuyQuote(dispatch, value); + debouncedUpdateBuyQuoteAsync(dispatch, assetData, value); }, }); export const SelectedAssetAmountInput: React.ComponentClass = connect( mapStateToProps, mapDispatchToProps, -)(AmountInput); +)(AssetAmountInput); diff --git a/packages/instant/src/data/asset_meta_data.ts b/packages/instant/src/data/asset_meta_data.ts new file mode 100644 index 000000000..e4d3e8f73 --- /dev/null +++ b/packages/instant/src/data/asset_meta_data.ts @@ -0,0 +1,15 @@ +import { AssetProxyId, ObjectMap } from '@0xproject/types'; + +import { zrxAssetData } from '../constants'; +import { AssetMetaData } from '../types'; + +// Map from assetData string to AssetMetaData object +// TODO: import this from somewhere else. +export const assetMetaData: ObjectMap = { + [zrxAssetData]: { + assetProxyId: AssetProxyId.ERC20, + decimals: 18, + primaryColor: 'rgb(54, 50, 60)', + symbol: 'zrx', + }, +}; diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index 56a4ee236..adecf2ab7 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -2,11 +2,13 @@ import { BuyQuote } from '@0xproject/asset-buyer'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; +import { zrxAssetData } from '../constants'; import { AsyncProcessState } from '../types'; import { Action, ActionTypes } from './actions'; export interface State { + selectedAssetData?: string; selectedAssetAmount?: BigNumber; selectedAssetBuyState: AsyncProcessState; ethUsdPrice?: BigNumber; @@ -14,9 +16,11 @@ export interface State { } export const INITIAL_STATE: State = { - ethUsdPrice: undefined, - selectedAssetBuyState: AsyncProcessState.NONE, + // TODO: Remove hardcoded zrxAssetData + selectedAssetData: zrxAssetData, selectedAssetAmount: undefined, + selectedAssetBuyState: AsyncProcessState.NONE, + ethUsdPrice: undefined, latestBuyQuote: undefined, }; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index ffa5d679e..bf3ee392f 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -1,4 +1,4 @@ -import { ObjectMap } from '@0xproject/types'; +import { AssetProxyId, ObjectMap } from '@0xproject/types'; // Reusable export enum AsyncProcessState { @@ -11,3 +11,23 @@ export enum AsyncProcessState { export type FunctionType = (...args: any[]) => any; export type ActionCreatorsMapObject = ObjectMap; export type ActionsUnion = ReturnType; + +export interface ERC20AssetMetaData { + assetProxyId: AssetProxyId.ERC20; + decimals: number; + primaryColor?: string; + symbol: string; +} + +export interface ERC721AssetMetaData { + assetProxyId: AssetProxyId.ERC721; + name: string; + primaryColor?: string; +} + +export type AssetMetaData = ERC20AssetMetaData | ERC721AssetMetaData; + +export enum Network { + Kovan = 42, + Mainnet = 1, +} -- cgit From fcf345144835cf142da2cbca544151100791700f Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 17:06:06 -0700 Subject: Add tnxHash to buy button callbacks --- packages/instant/src/components/asset_amount_input.tsx | 4 ++-- packages/instant/src/components/buy_button.tsx | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx index 915c66e1e..72bcfb8fb 100644 --- a/packages/instant/src/components/asset_amount_input.tsx +++ b/packages/instant/src/components/asset_amount_input.tsx @@ -25,13 +25,13 @@ export class AssetAmountInput extends React.Component { - {this._getLabel()} + {this._getAssetSymbolLabel()} ); } - private readonly _getLabel = (): string => { + private readonly _getAssetSymbolLabel = (): string => { const unknownLabel = '???'; if (_.isUndefined(this.props.assetData)) { return unknownLabel; diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index 097b8e547..e9466619e 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -11,8 +11,8 @@ import { Button, Container, Text } from './ui'; export interface BuyButtonProps { buyQuote?: BuyQuote; onClick: (buyQuote: BuyQuote) => void; - onBuySuccess: (buyQuote: BuyQuote) => void; - onBuyFailure: (buyQuote: BuyQuote) => void; + onBuySuccess: (buyQuote: BuyQuote, txnHash: string) => void; + onBuyFailure: (buyQuote: BuyQuote, tnxHash?: string) => void; text: string; } @@ -42,15 +42,13 @@ export class BuyButton extends React.Component { return; } this.props.onClick(this.props.buyQuote); + let txnHash; try { - const txnHash = await assetBuyer.executeBuyQuoteAsync(this.props.buyQuote, { - // HACK: There is a calculation issue in asset-buyer. ETH is refunded anyway so just over-estimate. - ethAmount: this.props.buyQuote.worstCaseQuoteInfo.totalEthAmount.mul(2), - }); + txnHash = await assetBuyer.executeBuyQuoteAsync(this.props.buyQuote); await web3Wrapper.awaitTransactionSuccessAsync(txnHash); + this.props.onBuySuccess(this.props.buyQuote, txnHash); } catch { - this.props.onBuyFailure(this.props.buyQuote); + this.props.onBuyFailure(this.props.buyQuote, txnHash); } - this.props.onBuySuccess(this.props.buyQuote); }; } -- cgit From ac3bfdfe5ffc4fc49b88fbad062e1d562987e728 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 17:06:28 -0700 Subject: Put boundNoop in a util file --- packages/instant/src/components/amount_input.tsx | 3 ++- packages/instant/src/components/asset_amount_input.tsx | 3 ++- packages/instant/src/components/buy_button.tsx | 9 ++++----- packages/instant/src/util/util.ts | 5 +++++ 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 packages/instant/src/util/util.ts (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx index 5b81a3d68..7644f5f67 100644 --- a/packages/instant/src/components/amount_input.tsx +++ b/packages/instant/src/components/amount_input.tsx @@ -3,6 +3,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { ColorOption } from '../style/theme'; +import { util } from '../util/util'; import { Container, Input } from './ui'; @@ -15,7 +16,7 @@ export interface AmountInputProps { export class AmountInput extends React.Component { public static defaultProps = { - onChange: _.noop.bind(_), + onChange: util.boundNoop, }; public render(): React.ReactNode { const { fontColor, fontSize, value } = this.props; diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx index 72bcfb8fb..7c6b03ee9 100644 --- a/packages/instant/src/components/asset_amount_input.tsx +++ b/packages/instant/src/components/asset_amount_input.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { assetMetaData } from '../data/asset_meta_data'; import { ColorOption } from '../style/theme'; +import { util } from '../util/util'; import { AmountInput, AmountInputProps } from './amount_input'; import { Container, Text } from './ui'; @@ -16,7 +17,7 @@ export interface AssetAmountInputProps extends AmountInputProps { export class AssetAmountInput extends React.Component { public static defaultProps = { - onChange: _.noop.bind(_), + onChange: util.boundNoop, }; public render(): React.ReactNode { const { assetData, onChange, ...rest } = this.props; diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index e9466619e..0706817c9 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { ColorOption } from '../style/theme'; import { assetBuyer } from '../util/asset_buyer'; +import { util } from '../util/util'; import { web3Wrapper } from '../util/web3_wrapper'; import { Button, Container, Text } from './ui'; @@ -16,13 +17,11 @@ export interface BuyButtonProps { text: string; } -const boundNoop = _.noop.bind(_); - export class BuyButton extends React.Component { public static defaultProps = { - onClick: boundNoop, - onBuySuccess: boundNoop, - onBuyFailure: boundNoop, + onClick: util.boundNoop, + onBuySuccess: util.boundNoop, + onBuyFailure: util.boundNoop, }; public render(): React.ReactNode { const shouldDisableButton = _.isUndefined(this.props.buyQuote); diff --git a/packages/instant/src/util/util.ts b/packages/instant/src/util/util.ts new file mode 100644 index 000000000..232a86850 --- /dev/null +++ b/packages/instant/src/util/util.ts @@ -0,0 +1,5 @@ +import * as _ from 'lodash'; + +export const util = { + boundNoop: _.noop.bind(_), +}; -- cgit From fa18db84d976644270adf0815c73a8d6f40cf9a8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 17:33:27 -0700 Subject: Rename OrderDetailsRow to EthAmountRow --- packages/instant/src/components/order_details.tsx | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 66e7e2406..9256d1398 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -32,15 +32,15 @@ export class OrderDetails extends React.Component { Order Details - - - + { } } -export interface OrderDetailsRowProps { - name: string; +export interface EthAmountRowProps { + rowLabel: string; ethAmount?: BigNumber; - shouldConvertEthToUnitAmount?: boolean; + isEthAmountInBaseUnits?: boolean; ethUsdPrice?: BigNumber; shouldEmphasize?: boolean; } -export const OrderDetailsRow: React.StatelessComponent = ({ - name, +export const EthAmountRow: React.StatelessComponent = ({ + rowLabel, ethAmount, - shouldConvertEthToUnitAmount, + isEthAmountInBaseUnits, ethUsdPrice, shouldEmphasize, }) => { const fontWeight = shouldEmphasize ? 700 : 400; - const usdFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; - const ethFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmount : format.ethUnitAmount; + const usdFormatter = isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; + const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount; return ( - {name} + {rowLabel} @@ -87,9 +87,9 @@ export const OrderDetailsRow: React.StatelessComponent = ( ); }; -OrderDetailsRow.defaultProps = { +EthAmountRow.defaultProps = { shouldEmphasize: false, - shouldConvertEthToUnitAmount: true, + isEthAmountInBaseUnits: true, }; -OrderDetailsRow.displayName = 'OrderDetailsRow'; +EthAmountRow.displayName = 'EthAmountRow'; -- cgit From 18667d739c112955478ab07cb245dc435b31a974 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 17:39:52 -0700 Subject: Hide USD price when ETH-USD price is not available --- packages/instant/src/components/order_details.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 9256d1398..bd31bfba8 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -68,6 +68,11 @@ export const EthAmountRow: React.StatelessComponent = ({ const fontWeight = shouldEmphasize ? 700 : 400; const usdFormatter = isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount; + const usdPriceSection = _.isUndefined(ethUsdPrice) ? null : ( + + ({usdFormatter(ethAmount, ethUsdPrice)}) + + ); return ( @@ -75,9 +80,7 @@ export const EthAmountRow: React.StatelessComponent = ({ {rowLabel} - - ({usdFormatter(ethAmount, ethUsdPrice)}) - + {usdPriceSection} {ethFormatter(ethAmount)} -- cgit From f2e5fd88469c9e3396197b42298d0b2de8a4d47c Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 18:07:19 -0700 Subject: Add ts-optchain and use it instead of lodash get --- packages/instant/src/components/order_details.tsx | 8 +++++--- packages/instant/src/containers/latest_buy_quote_order_details.ts | 3 ++- packages/instant/src/containers/selected_asset_instant_heading.ts | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index bd31bfba8..a15ff411b 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -2,6 +2,7 @@ import { BuyQuoteInfo } from '@0xproject/asset-buyer'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; +import { oc } from 'ts-optchain'; import { ColorOption } from '../style/theme'; import { format } from '../util/format'; @@ -16,9 +17,10 @@ export interface OrderDetailsProps { export class OrderDetails extends React.Component { public render(): React.ReactNode { const { buyQuoteInfo, ethUsdPrice } = this.props; - const ethAssetPrice = _.get(buyQuoteInfo, 'ethPerAssetPrice'); - const ethTokenFee = _.get(buyQuoteInfo, 'feeEthAmount'); - const totalEthAmount = _.get(buyQuoteInfo, 'totalEthAmount'); + const buyQuoteAccessor = oc(buyQuoteInfo); + const ethAssetPrice = buyQuoteAccessor.ethPerAssetPrice(); + const ethTokenFee = buyQuoteAccessor.feeEthAmount(); + const totalEthAmount = buyQuoteAccessor.totalEthAmount(); return ( diff --git a/packages/instant/src/containers/latest_buy_quote_order_details.ts b/packages/instant/src/containers/latest_buy_quote_order_details.ts index 77d84289b..b354c78fa 100644 --- a/packages/instant/src/containers/latest_buy_quote_order_details.ts +++ b/packages/instant/src/containers/latest_buy_quote_order_details.ts @@ -3,6 +3,7 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; +import { oc } from 'ts-optchain'; import { State } from '../redux/reducer'; @@ -17,7 +18,7 @@ interface ConnectedState { const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({ // use the worst case quote info - buyQuoteInfo: _.get(state, 'latestBuyQuote.worstCaseQuoteInfo'), + buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(), ethUsdPrice: state.ethUsdPrice, }); diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts index a9e853fe1..c97cfe11a 100644 --- a/packages/instant/src/containers/selected_asset_instant_heading.ts +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; +import { oc } from 'ts-optchain'; import { State } from '../redux/reducer'; @@ -17,7 +18,7 @@ interface ConnectedState { const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ selectedAssetAmount: state.selectedAssetAmount, - totalEthBaseAmount: _.get(state, 'latestBuyQuote.worstCaseQuoteInfo.totalEthAmount'), + totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), ethUsdPrice: state.ethUsdPrice, }); -- cgit From 875f621f20431389131730536afe578a1060152a Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 15 Oct 2018 18:13:09 -0700 Subject: Remove expiry buffer seconds option from AssetBuyer init --- packages/instant/src/util/asset_buyer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/util/asset_buyer.ts b/packages/instant/src/util/asset_buyer.ts index b030501c4..27d66d600 100644 --- a/packages/instant/src/util/asset_buyer.ts +++ b/packages/instant/src/util/asset_buyer.ts @@ -6,6 +6,4 @@ import { getProvider } from './provider'; const provider = getProvider(); -export const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, sraApiUrl, { - expiryBufferSeconds: 300, -}); +export const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, sraApiUrl); -- cgit From 2610868589a9de0a9067047e69dcd252b2b5f985 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 16 Oct 2018 16:13:23 -0700 Subject: Add tests for format and use toFixed instead of round for usd --- packages/instant/src/util/format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/instant/src') diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts index de7eb62e6..b62c968fb 100644 --- a/packages/instant/src/util/format.ts +++ b/packages/instant/src/util/format.ts @@ -40,6 +40,6 @@ export const format = { if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) { return defaultText; } - return `$${ethUnitAmount.mul(ethUsdPrice).round(decimalPlaces)}`; + return `$${ethUnitAmount.mul(ethUsdPrice).toFixed(decimalPlaces)}`; }, }; -- cgit