-
Notifications
You must be signed in to change notification settings - Fork 465
[instant][types][order-utils][asset-buyer] Move over and clean up features from zrx-buyer #1131
Changes from 1 commit
19f6190
63652df
0edd9b3
1c92ae0
03b235b
09c5ae4
f3391e1
025614a
ccf021b
f395414
e7130af
43f8f2a
fcf3451
ac3bfdf
fa18db8
18667d7
f2e5fd8
875f621
d268e19
dbf5be6
2610868
c328616
6a89935
d2adbc3
009b5b5
8ba6534
eda0b3e
32beeae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
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(_); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It throws. Sweet! |
||
} | ||
this.props.onBuySuccess(this.props.buyQuote); | ||
}; | ||
} |
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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this logic just live in format? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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"> | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove hard-coded zrx values |
||
export const ethDecimals = 18; |
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 }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); |
This file was deleted.
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); |
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, | ||
); |
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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. coinbase API call should return |
||
} catch (e) { | ||
// ignore | ||
} finally { | ||
store.dispatch({ | ||
type: ActionTypes.UPDATE_ETH_USD_PRICE, | ||
data: new BigNumber(ethUsdPriceStr), | ||
}); | ||
} | ||
}, | ||
}; |
There was a problem hiding this comment.
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