Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

[instant][types][order-utils][asset-buyer] Move over and clean up features from zrx-buyer #1131

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
19f6190
feat: Move over features from zrx-buyer
fragosti Oct 11, 2018
63652df
feat: adjust amount input width
fragosti Oct 11, 2018
0edd9b3
feat: debounce the fetching of new quotes
fragosti Oct 11, 2018
1c92ae0
fix: export BuyQuoteInfo from asset-buyer
fragosti Oct 12, 2018
03b235b
feat: populate order details with information from worst buy quote
fragosti Oct 12, 2018
09c5ae4
feat: have coinbase API return BigNumber for eth-usd price endpoint
fragosti Oct 12, 2018
f3391e1
feat: make redux actions type-sage
fragosti Oct 12, 2018
025614a
feat: Add AssetData type as union of ERC721AssetData and ERC20AssetData
fragosti Oct 12, 2018
ccf021b
feat: use new AssetData type from types package
fragosti Oct 12, 2018
f395414
feat: model asset meta data and add dynamic assetData state
fragosti Oct 12, 2018
e7130af
Merge branch 'development' of https://github.com/0xProject/0x-monorep…
fragosti Oct 15, 2018
43f8f2a
feat: add changelog entries for changed packages
fragosti Oct 15, 2018
fcf3451
Add tnxHash to buy button callbacks
fragosti Oct 16, 2018
ac3bfdf
Put boundNoop in a util file
fragosti Oct 16, 2018
fa18db8
Rename OrderDetailsRow to EthAmountRow
fragosti Oct 16, 2018
18667d7
Hide USD price when ETH-USD price is not available
fragosti Oct 16, 2018
f2e5fd8
Add ts-optchain and use it instead of lodash get
fragosti Oct 16, 2018
875f621
Remove expiry buffer seconds option from AssetBuyer init
fragosti Oct 16, 2018
d268e19
Merge branch 'development' of https://github.com/0xProject/0x-monorep…
fragosti Oct 16, 2018
dbf5be6
Merge branch 'development' of https://github.com/0xProject/0x-monorep…
fragosti Oct 16, 2018
2610868
Add tests for format and use toFixed instead of round for usd
fragosti Oct 16, 2018
c328616
Run tests on circle CI
fragosti Oct 16, 2018
6a89935
Remove order-utils from dependencies
fragosti Oct 16, 2018
d2adbc3
chore: temporarily increase the bundle size for instant
fragosti Oct 16, 2018
009b5b5
feat: export AssetData from utils
fragosti Oct 17, 2018
8ba6534
feat: export AssetData from order-utils
fragosti Oct 17, 2018
eda0b3e
fix: dont use enum string as type as typedoc gets confused
fragosti Oct 17, 2018
32beeae
Bump max bundle size for instant
fragosti Oct 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 48 additions & 11 deletions packages/instant/src/components/buy_button.tsx
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might want to return the txHash as a param for the callback as well

onBuyFailure: (buyQuote: BuyQuote) => void;
text: string;
}

export const BuyButton: React.StatelessComponent<BuyButtonProps> = props => (
<Container padding="20px" width="100%">
<Button width="100%">
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
Buy
</Text>
</Button>
</Container>
);
const boundNoop = _.noop.bind(_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we extract this out into a util or something since we are using it in multiple components? Also do we necessarily need to bind here? I suppose the linter may complain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linter complains yes.


BuyButton.displayName = 'BuyButton';
export class BuyButton extends React.Component<BuyButtonProps> {
public static defaultProps = {
onClick: boundNoop,
onBuySuccess: boundNoop,
onBuyFailure: boundNoop,
};
public render(): React.ReactNode {
const shouldDisableButton = _.isUndefined(this.props.buyQuote);
return (
<Container padding="20px" width="100%">
<Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
{this.props.text}
</Text>
</Button>
</Container>
);
}
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not commit this, the fix should be up from the protocol side early this week

ethAmount: this.props.buyQuote.worstCaseQuoteInfo.totalEthAmount.mul(2),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this ethAmount should come from the buyQuoteInfo stored in redux so there is less chance we end up executing a buy thats different from what we are presenting in the order details

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not technically possible in this implementation, but yes the API allows that... I feel like that's ok?

});
await web3Wrapper.awaitTransactionSuccessAsync(txnHash);
} catch {
this.props.onBuyFailure(this.props.buyQuote);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be if the tx fails? I'm not 100% sure if awaitTransactionSuccessAsync will throw. We might want to use web3wrapper.awaitTransactionMinedAsync instead. In other news I'll be adding some hooks to assetBuyer.executeBuyQuoteAsync so we can know when the user has been asked to sign a transaction and when they have failed to do so

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It throws.

Sweet!

}
this.props.onBuySuccess(this.props.buyQuote);
};
}
39 changes: 35 additions & 4 deletions packages/instant/src/components/instant_heading.tsx
Original file line number Diff line number Diff line change
@@ -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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this logic just live in format?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see format as just formatting ethereum amounts into strings (including ETH dollar amounts). This case is handling the input not having any value, which seems to specific.

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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

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<InstantHeadingProps> = props => (
<Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%" borderRadius="3px 3px 0px 0px">
Expand All @@ -26,18 +57,18 @@ export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = pro
<SelectedAssetAmountInput fontSize="45px" />
<Container display="inline-block" marginLeft="10px">
<Text fontSize="45px" fontColor={ColorOption.white} textTransform="uppercase">
rep
zrx
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove hardcoded zrx values

</Text>
</Container>
</Container>
<Flex direction="column" justify="space-between">
<Container marginBottom="5px">
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
0 ETH
{displaytotalEthBaseAmount(props)}
</Text>
</Container>
<Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}>
$0.00
{displayUsdAmount(props)}
</Text>
</Flex>
</Flex>
Expand Down
2 changes: 2 additions & 0 deletions packages/instant/src/components/zero_ex_instant.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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';

import { ZeroExInstantContainer } from './zero_ex_instant_container';

fonts.include();
asyncData.fetchAndDispatchToStore();

export interface ZeroExInstantProps {}

Expand Down
7 changes: 5 additions & 2 deletions packages/instant/src/components/zero_ex_instant_container.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -12,9 +15,9 @@ export interface ZeroExInstantContainerProps {}
export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
<Container hasBoxShadow={true} width="350px" backgroundColor={ColorOption.white} borderRadius="3px">
<Flex direction="column" justify="flex-start">
<InstantHeading />
<SelectedAssetInstantHeading />
<OrderDetails />
<BuyButton />
<SelectedAssetBuyButton />
</Flex>
</Container>
);
4 changes: 4 additions & 0 deletions packages/instant/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will we make this more configurable in a separate PR?

export const zrxContractAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498';
export const zrxDecimals = 18;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove hard-coded zrx values

export const ethDecimals = 18;
57 changes: 57 additions & 0 deletions packages/instant/src/containers/selected_asset_amount_input.ts
Original file line number Diff line number Diff line change
@@ -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<Action>): 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 });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create action creators? would allow us to have typed actions...

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<SelectedAssetAmountInputProps> = connect(
mapStateToProps,
mapDispatchToProps,
)(AmountInput);
36 changes: 0 additions & 36 deletions packages/instant/src/containers/selected_asset_amount_input.tsx

This file was deleted.

59 changes: 59 additions & 0 deletions packages/instant/src/containers/selected_asset_buy_button.ts
Original file line number Diff line number Diff line change
@@ -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;
BMillman19 marked this conversation as resolved.
Show resolved Hide resolved
}

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<Action>, 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<SelectedAssetBuyButtonProps> = connect(
mapStateToProps,
mapDispatchToProps,
)(BuyButton);
26 changes: 26 additions & 0 deletions packages/instant/src/containers/selected_asset_instant_heading.ts
Original file line number Diff line number Diff line change
@@ -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<InstantHeadingProps> = connect(mapStateToProps)(
InstantHeading,
);
22 changes: 22 additions & 0 deletions packages/instant/src/redux/async_data.ts
Original file line number Diff line number Diff line change
@@ -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();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coinbase API call should return BigNumber

} catch (e) {
// ignore
} finally {
store.dispatch({
type: ActionTypes.UPDATE_ETH_USD_PRICE,
data: new BigNumber(ethUsdPriceStr),
});
}
},
};
Loading