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

Conversation

fragosti
Copy link
Contributor

@fragosti fragosti commented Oct 11, 2018

Description

Straight port of features:

  • Abstraction for fetching async data, and fetching ETH-USD price from coinbase.
  • Connecting InstantHeading and BuyButton to the redux store.
  • Instantiate provider, asset buyer, web3wrapper etc...
  • Display the buy quote and allow the buy button to execute the buy quote.
  • Add notion of dynamic assetData and assetMetaData.
  • Add some utils for formatting eth price and eth-usd price.
  • Populate the order details section.
  • Add debouncing to fetches that result from input changes.
  • Make redux actions type-safe.

I also changed some types but then ended up not needing the change... but still seems like a good change? Basically we should control all unions of different asset data types, such that the build fails if we add a type to that union, assuming we are using a switch statement in the implementation of the function that is meant to handle all asset data types.

screen shot 2018-10-12 at 4 11 58 pm

Testing instructions

Types of changes

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Prefix PR title with bracketed package name(s) corresponding to the changed package(s). For example: [sol-cov] Fixed bug.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@@ -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

@@ -0,0 +1,4 @@
export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/';
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

// 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...

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

import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';

import { Action, ActionTypes } from '../types';
import { Action, ActionTypes, AsyncProcessState } from '../types';

export interface State {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

add state for Provider, and other things required to remove hard-coded values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not going to do this for now. Should be done when we implement different wallets.

@fragosti fragosti changed the title [WIP][instant] Move over and clean up features from zrx-buyer [instant] Move over and clean up features from zrx-buyer Oct 12, 2018
@fragosti fragosti changed the title [instant] Move over and clean up features from zrx-buyer [instant][types][order-utils] Move over and clean up features from zrx-buyer Oct 12, 2018
@fragosti fragosti changed the title [instant][types][order-utils] Move over and clean up features from zrx-buyer [instant][types][order-utils][asset-buyer] Move over and clean up features from zrx-buyer Oct 12, 2018
@@ -10,10 +10,13 @@ export interface AmountInputProps {
fontColor?: ColorOption;
fontSize?: string;
value?: BigNumber;
onChange?: (value?: BigNumber) => void;
onChange: (value?: BigNumber) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

is value undefined if the input is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep! This is what made most sense to me.


export interface AssetAmountInputProps extends AmountInputProps {
assetData?: string;
onChange: (value?: BigNumber, assetData?: string) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

are we ever going to have a case where we have a value but not an assetData?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In practice no, but those two things can be undefined independently and that case needs to be handled somewhere. The other option is to not render something, but in the event that we are rendering, we need to handle it here.

</Container>
);
}
private readonly _getLabel = (): string => {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe this can have a clearer name like _getAssetSymbolLabel

const { assetData, onChange, ...rest } = this.props;
return (
<Container>
<AmountInput {...rest} onChange={this._handleChange} />
Copy link
Contributor

Choose a reason for hiding this comment

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

this is a design / polish nit, I think we want the text in this input to be right justified so that if you have typed just '5' then the character '5' is close to the symbol label

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do in a future PR (added to my list)

</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.

}) => {
const fontWeight = shouldEmphasize ? 700 : 400;
const usdFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd;
const ethFormatter = shouldConvertEthToUnitAmount ? format.ethBaseAmount : format.ethUnitAmount;
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need to bind format here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't use this in format, and even if we did we would be fine because we are using arrow functions. In general I think it's a bit awkward to use the function keyword in es6 since we have classes and arrow functions. In es5, all we had was function and that was for both classes and functions.

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),
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?

const provider = getProvider();

export const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, sraApiUrl, {
expiryBufferSeconds: 300,
Copy link
Contributor

Choose a reason for hiding this comment

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

this should already be the default

import { BigNumber } from '@0xproject/utils';

const baseEndpoint = 'https://api.coinbase.com/v2';
export const coinbaseApi = {
Copy link
Contributor

Choose a reason for hiding this comment

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

is coinbase the best choice for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure, jw what thought process you had when choosing the coinbase API to supply the price. Some things to consider, support for other currencies for localization, rate limiting, downtime, etc.

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 looked into a few options but did not do a crazy thorough comparison. Coinbase has 3 req / sec, 10k req / hour per IP, which seems reasonable. They also support other (fiat) currencies. Not much info available on downtime. Do you know of other APIs?

@@ -10,10 +10,13 @@ export interface AmountInputProps {
fontColor?: ColorOption;
fontSize?: string;
value?: BigNumber;
onChange?: (value?: BigNumber) => void;
onChange: (value?: BigNumber) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

just curious -- why move this from being optional to being required but having a default no-op? is this a pattern we want to follow for callbacks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So yea this is a bit confusing. @types/react and ts > 3.0.0 added understanding of defaultProps. So the way you think about the Props interface is that it's the internal interface (in this case, callback is not optional). However, if you have a default value the compiler knows that you actually don't have to pass the prop in (ie. the external interface is a function of internal interface + defaultProps).

So in practice it's not required, has default no-ops, so internally you can assume it's not undefined.

And yea I like this pattern :P

UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE',
}

export const actions = {
Copy link
Contributor

Choose a reason for hiding this comment

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

typed actions! 🎉 🙌 💯


const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({
// use the worst case quote info
buyQuoteInfo: _.get(state, 'latestBuyQuote.worstCaseQuoteInfo'),
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: I would prefer to grab the worstCaseInfo without using _.get to keep the typesafety (i.e. ensuring worstCaseQuoteInfo is actually a property of latestBuyQuote

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 added a package https://github.com/rimeto/ts-optchain. The API is a bit ugly but it gives us the best of both worlds. Open to discussing, but didn't take long to include.

@@ -0,0 +1,6 @@
import { BigNumber } from '@0xproject/utils';
export const BIG_NUMBER_ZERO = new BigNumber(0);
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?

import { ethDecimals } from '../constants';

export const format = {
ethBaseAmount: (ethBaseAmount?: BigNumber, decimalPlaces: number = 4, defaultText: string = '0 ETH'): string => {
Copy link
Contributor

Choose a reason for hiding this comment

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

might be good to add unit tests here

if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) {
return defaultText;
}
return `$${ethUnitAmount.mul(ethUsdPrice).round(decimalPlaces)}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

sometimes this results in strings with 1 decimal place like: "$3.7"

…into feature/instant/move-features-over-from-zrx-buyer
@fragosti fragosti force-pushed the feature/instant/move-features-over-from-zrx-buyer branch from a377e21 to dbf5be6 Compare October 16, 2018 21:54
@coveralls
Copy link

coveralls commented Oct 16, 2018

Coverage Status

Coverage decreased (-0.009%) to 84.679% when pulling 32beeae on feature/instant/move-features-over-from-zrx-buyer into 72f5719 on development.

@steveklebanoff steveklebanoff mentioned this pull request Oct 17, 2018
8 tasks
@fragosti fragosti merged commit 336e456 into development Oct 17, 2018
@fragosti fragosti deleted the feature/instant/move-features-over-from-zrx-buyer branch March 13, 2019 21:07
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants