diff options
author | Brandon Millman <brandon@0xproject.com> | 2018-11-14 06:35:09 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-14 06:35:09 +0800 |
commit | e02dc13805349a770506350c69e9061f596b48b2 (patch) | |
tree | 7e0b7165ba370af5088a51183d39a7fba9c535a0 | |
parent | 7b4f63a39ca4b4e05123d2b6871c6e01f8a132a2 (diff) | |
parent | 820ab062a6b24f246770f3700ecd6fbb5e3dcfa4 (diff) | |
download | dexon-0x-contracts-e02dc13805349a770506350c69e9061f596b48b2.tar.gz dexon-0x-contracts-e02dc13805349a770506350c69e9061f596b48b2.tar.zst dexon-0x-contracts-e02dc13805349a770506350c69e9061f596b48b2.zip |
Merge pull request #1252 from 0xProject/fix/asset-buyer/price-per-token
[asset-buyer][instant] Fix incorrect token prices for non 18-decimal tokens
18 files changed, 118 insertions, 89 deletions
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 26b3e65d4..826b6150d 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "3.0.0", + "changes": [ + { + "note": "update `getBuyQuoteAsync` to return eth spent on assets instead of per unit amount", + "pr": 1252 + } + ] + }, + { "timestamp": 1542134075, "version": "2.2.2", "changes": [ diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 3f1e6ff21..3b573edca 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -54,12 +54,12 @@ export interface BuyQuote { } /** - * ethPerAssetPrice: The price of one unit of the desired asset in ETH + * assetEthAmount: The amount of eth required to pay for the requested asset. * feeEthAmount: The amount of eth required to pay the affiliate fee. - * totalEthAmount: the total amount of eth required to complete the buy. (Filling orders, feeOrders, and paying affiliate fee) + * totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee). */ export interface BuyQuoteInfo { - ethPerAssetPrice: BigNumber; + assetEthAmount: BigNumber; feeEthAmount: BigNumber; totalEthAmount: BigNumber; } diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 2466f53a4..fcf9b0d0e 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -18,7 +18,7 @@ export const assert = { } }, isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void { - sharedAssert.isBigNumber(`${variableName}.ethPerAssetPrice`, buyQuoteInfo.ethPerAssetPrice); + sharedAssert.isBigNumber(`${variableName}.assetEthAmount`, buyQuoteInfo.assetEthAmount); sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount); sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount); }, diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 6a67ed1ed..b15b880c2 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -106,28 +106,28 @@ function calculateQuoteInfo( isMakerAssetZrxToken: boolean, ): BuyQuoteInfo { // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right - let ethAmountToBuyAsset = constants.ZERO_AMOUNT; - let ethAmountToBuyZrx = constants.ZERO_AMOUNT; + let assetEthAmount = constants.ZERO_AMOUNT; + let zrxEthAmount = constants.ZERO_AMOUNT; if (isMakerAssetZrxToken) { - ethAmountToBuyAsset = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); + assetEthAmount = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); } else { // find eth and zrx amounts needed to buy const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount); - ethAmountToBuyAsset = ethAndZrxAmountToBuyAsset[0]; + assetEthAmount = ethAndZrxAmountToBuyAsset[0]; const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1]; // find eth amount needed to buy zrx - ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); + zrxEthAmount = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); } - /// find the eth amount needed to buy the affiliate fee - const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage).ceil(); - const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyZrx); - const ethAmountTotal = totalEthAmountWithoutAffiliateFee.plus(ethAmountToBuyAffiliateFee); - // divide into the assetBuyAmount in order to find rate of makerAsset / WETH - const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount); + // eth amount needed to buy the affiliate fee + const affiliateFeeEthAmount = assetEthAmount.mul(feePercentage).ceil(); + // eth amount needed for fees is the sum of affiliate fee and zrx fee + const feeEthAmount = affiliateFeeEthAmount.plus(zrxEthAmount); + // eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees + const totalEthAmount = assetEthAmount.plus(feeEthAmount); return { - totalEthAmount: ethAmountTotal, - feeEthAmount: ethAmountToBuyAffiliateFee, - ethPerAssetPrice, + assetEthAmount, + feeEthAmount, + totalEthAmount, }; } diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts index 0ea371982..a30017b72 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -108,17 +108,17 @@ describe('buyQuoteCalculator', () => { // 50 eth to fill the first order + 100 eth for fees const expectedEthAmountForAsset = new BigNumber(50); const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); - const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFillEthAmount = expectedEthAmountForAsset; + const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); + expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // because we have no slippage protection, minRate is equal to maxRate + expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // test if feePercentage gets passed through expect(buyQuote.feePercentage).to.equal(feePercentage); }); @@ -146,23 +146,23 @@ describe('buyQuoteCalculator', () => { // 50 eth to fill the first order + 100 eth for fees const expectedEthAmountForAsset = new BigNumber(50); const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); - const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFillEthAmount = expectedEthAmountForAsset; + const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); + expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // 100 eth to fill the first order + 208 eth for fees const expectedWorstEthAmountForAsset = new BigNumber(100); const expectedWorstEthAmountForZrxFees = new BigNumber(208); - const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees); - const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); + const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset; + const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); + const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees); const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount); - const expectedWorstEthPerAssetPrice = expectedWorstFillEthAmount.div(assetBuyAmount); + expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount); expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount); expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount); - expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedWorstEthPerAssetPrice); // test if feePercentage gets passed through expect(buyQuote.feePercentage).to.equal(feePercentage); }); diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index b07776b2c..7f9567454 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -15,8 +15,8 @@ import { Spinner } from './ui/spinner'; import { Text } from './ui/text'; export interface InstantHeadingProps { - selectedAssetAmount?: BigNumber; - totalEthBaseAmount?: BigNumber; + selectedAssetUnitAmount?: BigNumber; + totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; quoteRequestState: AsyncProcessState; buyOrderState: OrderState; @@ -104,7 +104,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { if (this.props.quoteRequestState === AsyncProcessState.Pending) { return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />; } - if (_.isUndefined(this.props.selectedAssetAmount)) { + if (_.isUndefined(this.props.selectedAssetUnitAmount)) { return <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />; } return amountFunction(); @@ -113,8 +113,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { private readonly _renderEthAmount = (): React.ReactNode => { return ( <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}> - {format.ethBaseAmount( - this.props.totalEthBaseAmount, + {format.ethBaseUnitAmount( + this.props.totalEthBaseUnitAmount, 4, <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />, )} @@ -125,8 +125,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { private readonly _renderDollarAmount = (): React.ReactNode => { return ( <Text fontSize="16px" fontColor={ColorOption.white}> - {format.ethBaseAmountInUsd( - this.props.totalEthBaseAmount, + {format.ethBaseUnitAmountInUsd( + this.props.totalEthBaseUnitAmount, this.props.ethUsdPrice, 2, <AmountPlaceholder isPulsating={false} color={ColorOption.white} />, diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 9abd7137e..5fc956e1c 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -4,6 +4,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { oc } from 'ts-optchain'; +import { BIG_NUMBER_ZERO } from '../constants'; import { ColorOption } from '../style/theme'; import { format } from '../util/format'; @@ -15,16 +16,23 @@ import { Text } from './ui/text'; export interface OrderDetailsProps { buyQuoteInfo?: BuyQuoteInfo; + selectedAssetUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; isLoading: boolean; } export class OrderDetails extends React.Component<OrderDetailsProps> { public render(): React.ReactNode { - const { buyQuoteInfo, ethUsdPrice } = this.props; + const { buyQuoteInfo, ethUsdPrice, selectedAssetUnitAmount } = this.props; const buyQuoteAccessor = oc(buyQuoteInfo); - const ethAssetPrice = buyQuoteAccessor.ethPerAssetPrice(); - const ethTokenFee = buyQuoteAccessor.feeEthAmount(); - const totalEthAmount = buyQuoteAccessor.totalEthAmount(); + const assetEthBaseUnitAmount = buyQuoteAccessor.assetEthAmount(); + const feeEthBaseUnitAmount = buyQuoteAccessor.feeEthAmount(); + const totalEthBaseUnitAmount = buyQuoteAccessor.totalEthAmount(); + const pricePerTokenEth = + !_.isUndefined(assetEthBaseUnitAmount) && + !_.isUndefined(selectedAssetUnitAmount) && + !selectedAssetUnitAmount.eq(BIG_NUMBER_ZERO) + ? assetEthBaseUnitAmount.div(selectedAssetUnitAmount).ceil() + : undefined; return ( <Container padding="20px" width="100%" flexGrow={1}> <Container marginBottom="10px"> @@ -40,20 +48,19 @@ export class OrderDetails extends React.Component<OrderDetailsProps> { </Container> <EthAmountRow rowLabel="Token Price" - ethAmount={ethAssetPrice} + ethAmount={pricePerTokenEth} ethUsdPrice={ethUsdPrice} - isEthAmountInBaseUnits={false} isLoading={this.props.isLoading} /> <EthAmountRow rowLabel="Fee" - ethAmount={ethTokenFee} + ethAmount={feeEthBaseUnitAmount} ethUsdPrice={ethUsdPrice} isLoading={this.props.isLoading} /> <EthAmountRow rowLabel="Total Cost" - ethAmount={totalEthAmount} + ethAmount={totalEthBaseUnitAmount} ethUsdPrice={ethUsdPrice} shouldEmphasize={true} isLoading={this.props.isLoading} @@ -81,7 +88,7 @@ export class EthAmountRow extends React.Component<EthAmountRowProps> { const { rowLabel, ethAmount, isEthAmountInBaseUnits, shouldEmphasize, isLoading } = this.props; const fontWeight = shouldEmphasize ? 700 : 400; - const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount; + const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseUnitAmount : format.ethUnitAmount; return ( <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}> <Flex justify="space-between"> @@ -105,7 +112,9 @@ export class EthAmountRow extends React.Component<EthAmountRowProps> { ); } private _renderUsdSection(): React.ReactNode { - const usdFormatter = this.props.isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; + const usdFormatter = this.props.isEthAmountInBaseUnits + ? format.ethBaseUnitAmountInUsd + : format.ethUnitAmountInUsd; const shouldHideUsdPriceSection = _.isUndefined(this.props.ethUsdPrice) || _.isUndefined(this.props.ethAmount); return shouldHideUsdPriceSection ? null : ( <Container marginRight="3px" display="inline-block"> diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx index bdce2a49d..58f1cc044 100644 --- a/packages/instant/src/components/payment_method_dropdown.tsx +++ b/packages/instant/src/components/payment_method_dropdown.tsx @@ -18,7 +18,7 @@ export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdown public render(): React.ReactNode { const { accountAddress, accountEthBalanceInWei } = this.props; const value = format.ethAddress(accountAddress); - const label = format.ethBaseAmount(accountEthBalanceInWei, 4, '') as string; + const label = format.ethBaseUnitAmount(accountEthBalanceInWei, 4, '') as string; return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />; } private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => { diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 411f118cc..e64579518 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -73,7 +73,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider completeAssetMetaDataMap, networkId, ), - selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount) + selectedAssetUnitAmount: _.isUndefined(props.defaultAssetBuyAmount) ? undefined : new BigNumber(props.defaultAssetBuyAmount), availableAssets: _.isUndefined(props.availableAssetDatas) 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 2b59ed3ae..5dfe535e7 100644 --- a/packages/instant/src/containers/latest_buy_quote_order_details.ts +++ b/packages/instant/src/containers/latest_buy_quote_order_details.ts @@ -14,6 +14,7 @@ export interface LatestBuyQuoteOrderDetailsProps {} interface ConnectedState { buyQuoteInfo?: BuyQuoteInfo; + selectedAssetUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; isLoading: boolean; } @@ -21,6 +22,7 @@ interface ConnectedState { const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({ // use the worst case quote info buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(), + selectedAssetUnitAmount: state.selectedAssetUnitAmount, ethUsdPrice: state.ethUsdPrice, isLoading: state.quoteRequestState === AsyncProcessState.Pending, }); diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts index a407279e6..8dc127e1d 100644 --- a/packages/instant/src/containers/selected_asset_instant_heading.ts +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -14,16 +14,16 @@ export interface InstantHeadingProps { } interface ConnectedState { - selectedAssetAmount?: BigNumber; - totalEthBaseAmount?: BigNumber; + selectedAssetUnitAmount?: BigNumber; + totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; quoteRequestState: AsyncProcessState; buyOrderState: OrderState; } const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ - selectedAssetAmount: state.selectedAssetAmount, - totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), + selectedAssetUnitAmount: state.selectedAssetUnitAmount, + totalEthBaseUnitAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), ethUsdPrice: state.ethUsdPrice, quoteRequestState: state.quoteRequestState, buyOrderState: state.buyOrderState, diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index 93ff3db70..7dd0d874e 100644 --- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -59,7 +59,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP const assetBuyer = state.providerState.assetBuyer; return { assetBuyer, - value: state.selectedAssetAmount, + value: state.selectedAssetUnitAmount, asset: selectedAsset, isDisabled, numberOfAssetsAvailable, diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index 8947c6c97..ba50fab7a 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -26,7 +26,7 @@ export enum ActionTypes { SET_ACCOUNT_STATE_READY = 'SET_ACCOUNT_STATE_READY', UPDATE_ACCOUNT_ETH_BALANCE = 'UPDATE_ACCOUNT_ETH_BALANCE', UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE', - UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT', + UPDATE_SELECTED_ASSET_UNIT_AMOUNT = 'UPDATE_SELECTED_ASSET_UNIT_AMOUNT', SET_BUY_ORDER_STATE_NONE = 'SET_BUY_ORDER_STATE_NONE', SET_BUY_ORDER_STATE_VALIDATING = 'SET_BUY_ORDER_STATE_VALIDATING', SET_BUY_ORDER_STATE_PROCESSING = 'SET_BUY_ORDER_STATE_PROCESSING', @@ -50,7 +50,8 @@ export const actions = { updateAccountEthBalance: (addressAndBalance: AddressAndEthBalanceInWei) => createAction(ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE, addressAndBalance), updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price), - updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), + updateSelectedAssetAmount: (amount?: BigNumber) => + createAction(ActionTypes.UPDATE_SELECTED_ASSET_UNIT_AMOUNT, amount), setBuyOrderStateNone: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_NONE), setBuyOrderStateValidating: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_VALIDATING), setBuyOrderStateProcessing: (txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) => diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index b920ac914..3f5c06974 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -82,10 +82,16 @@ export const asyncData = { }, fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; shouldSetPending: boolean }) => { const { store, shouldSetPending } = options; - const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); + const { + buyOrderState, + providerState, + selectedAsset, + selectedAssetUnitAmount, + affiliateInfo, + } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( - !_.isUndefined(selectedAssetAmount) && + !_.isUndefined(selectedAssetUnitAmount) && !_.isUndefined(selectedAsset) && buyOrderState.processState === OrderProcessState.None && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 @@ -94,7 +100,7 @@ export const asyncData = { assetBuyer, store.dispatch, selectedAsset as ERC20Asset, - selectedAssetAmount, + selectedAssetUnitAmount, shouldSetPending, affiliateInfo, ); diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index ef46fdd9d..77c99627a 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -41,7 +41,7 @@ interface PropsDerivedState { interface OptionalState { selectedAsset: Asset; availableAssets: Asset[]; - selectedAssetAmount: BigNumber; + selectedAssetUnitAmount: BigNumber; ethUsdPrice: BigNumber; latestBuyQuote: BuyQuote; latestErrorMessage: string; @@ -90,10 +90,10 @@ export const createReducer = (initialState: State) => { ...state, ethUsdPrice: action.data, }; - case ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT: + case ActionTypes.UPDATE_SELECTED_ASSET_UNIT_AMOUNT: return { ...state, - selectedAssetAmount: action.data, + selectedAssetUnitAmount: action.data, }; case ActionTypes.UPDATE_LATEST_BUY_QUOTE: const newBuyQuoteIfExists = action.data; @@ -204,7 +204,7 @@ export const createReducer = (initialState: State) => { latestBuyQuote: undefined, quoteRequestState: AsyncProcessState.None, buyOrderState: { processState: OrderProcessState.None }, - selectedAssetAmount: undefined, + selectedAssetUnitAmount: undefined, }; case ActionTypes.SET_AVAILABLE_ASSETS: return { @@ -232,9 +232,9 @@ const reduceStateWithAccount = (state: State, account: Account) => { const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { const selectedAssetIfExists = state.selectedAsset; - const selectedAssetAmountIfExists = state.selectedAssetAmount; + const selectedAssetUnitAmountIfExists = state.selectedAssetUnitAmount; // if no selectedAsset or selectedAssetAmount exists on the current state, return false - if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetAmountIfExists)) { + if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetUnitAmountIfExists)) { return false; } // if buyQuote's assetData does not match that of the current selected asset, return false @@ -246,7 +246,7 @@ const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { const selectedAssetMetaData = selectedAssetIfExists.metaData; if (selectedAssetMetaData.assetProxyId === AssetProxyId.ERC20) { const selectedAssetAmountBaseUnits = Web3Wrapper.toBaseUnitAmount( - selectedAssetAmountIfExists, + selectedAssetUnitAmountIfExists, selectedAssetMetaData.decimals, ); const doesAssetAmountMatch = selectedAssetAmountBaseUnits.eq(buyQuote.assetBuyAmount); diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index c33e28f1c..fcdded0a9 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -15,12 +15,12 @@ export const buyQuoteUpdater = { assetBuyer: AssetBuyer, dispatch: Dispatch<Action>, asset: ERC20Asset, - assetAmount: BigNumber, + assetUnitAmount: BigNumber, setPending = true, affiliateInfo?: AffiliateInfo, ): Promise<void> => { // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); + const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals); if (setPending) { // mark quote as pending dispatch(actions.setQuoteRequestStatePending()); diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts index 44661d697..e9c432b2f 100644 --- a/packages/instant/src/util/format.ts +++ b/packages/instant/src/util/format.ts @@ -5,15 +5,15 @@ import * as _ from 'lodash'; import { ETH_DECIMALS } from '../constants'; export const format = { - ethBaseAmount: ( - ethBaseAmount?: BigNumber, + ethBaseUnitAmount: ( + ethBaseUnitAmount?: BigNumber, decimalPlaces: number = 4, defaultText: React.ReactNode = '0 ETH', ): React.ReactNode => { - if (_.isUndefined(ethBaseAmount)) { + if (_.isUndefined(ethBaseUnitAmount)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS); return format.ethUnitAmount(ethUnitAmount, decimalPlaces); }, ethUnitAmount: ( @@ -27,16 +27,16 @@ export const format = { const roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces); return `${roundedAmount} ETH`; }, - ethBaseAmountInUsd: ( - ethBaseAmount?: BigNumber, + ethBaseUnitAmountInUsd: ( + ethBaseUnitAmount?: BigNumber, ethUsdPrice?: BigNumber, decimalPlaces: number = 2, defaultText: React.ReactNode = '$0.00', ): React.ReactNode => { - if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) { + if (_.isUndefined(ethBaseUnitAmount) || _.isUndefined(ethUsdPrice)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS); return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces); }, ethUnitAmountInUsd: ( diff --git a/packages/instant/test/util/format.test.ts b/packages/instant/test/util/format.test.ts index c346b7604..fe0a63e6e 100644 --- a/packages/instant/test/util/format.test.ts +++ b/packages/instant/test/util/format.test.ts @@ -15,20 +15,20 @@ const BIG_NUMBER_FAKE_ETH_USD_PRICE = new BigNumber(2.534); describe('format', () => { describe('ethBaseAmount', () => { it('converts 1 ETH in base units to the string `1 ETH`', () => { - expect(format.ethBaseAmount(ONE_ETH_IN_BASE_UNITS)).toBe('1 ETH'); + expect(format.ethBaseUnitAmount(ONE_ETH_IN_BASE_UNITS)).toBe('1 ETH'); }); it('converts .432414 ETH in base units to the string `.4324 ETH`', () => { - expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH'); + expect(format.ethBaseUnitAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH'); }); it('converts 5.3014059295032 ETH in base units to the string `5.301 ETH`', () => { - expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH'); + expect(format.ethBaseUnitAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH'); }); it('returns defaultText param when ethBaseAmount is not defined', () => { const defaultText = 'defaultText'; - expect(format.ethBaseAmount(undefined, 4, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmount(undefined, 4, defaultText)).toBe(defaultText); }); it('it allows for configurable decimal places', () => { - expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS, 2)).toBe('0.43 ETH'); + expect(format.ethBaseUnitAmount(DECIMAL_ETH_IN_BASE_UNITS, 2)).toBe('0.43 ETH'); }); }); describe('ethUnitAmount', () => { @@ -52,24 +52,26 @@ describe('format', () => { }); describe('ethBaseAmountInUsd', () => { it('correctly formats 1 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(ONE_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53'); + expect(format.ethBaseUnitAmountInUsd(ONE_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53'); }); it('correctly formats .432414 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$1.10'); + expect(format.ethBaseUnitAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( + '$1.10', + ); }); it('correctly formats 5.3014059295032 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(IRRATIONAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( + expect(format.ethBaseUnitAmountInUsd(IRRATIONAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( '$13.43', ); }); it('returns defaultText param when ethBaseAmountInUsd or ethUsdPrice is not defined', () => { const defaultText = 'defaultText'; - expect(format.ethBaseAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText); - expect(format.ethBaseAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText); - expect(format.ethBaseAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText); }); it('it allows for configurable decimal places', () => { - expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe( + expect(format.ethBaseUnitAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe( '$1.0957', ); }); |