Merge pull request #1424 from 0xProject/feature/instant/usd-eth-toggle
[instant] ETH/USD toggle
This commit is contained in:
commit
737d1dc54d
@ -113,20 +113,23 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly _renderEthAmount = (): React.ReactNode => {
|
private readonly _renderEthAmount = (): React.ReactNode => {
|
||||||
|
const ethAmount = format.ethBaseUnitAmount(
|
||||||
|
this.props.totalEthBaseUnitAmount,
|
||||||
|
4,
|
||||||
|
<AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fontSize = _.isString(ethAmount) && ethAmount.length >= 13 ? '14px' : '16px';
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
fontSize="16px"
|
fontSize={fontSize}
|
||||||
textAlign="right"
|
textAlign="right"
|
||||||
width="100%"
|
width="100%"
|
||||||
fontColor={ColorOption.white}
|
fontColor={ColorOption.white}
|
||||||
fontWeight={500}
|
fontWeight={500}
|
||||||
noWrap={true}
|
noWrap={true}
|
||||||
>
|
>
|
||||||
{format.ethBaseUnitAmount(
|
{ethAmount}
|
||||||
this.props.totalEthBaseUnitAmount,
|
|
||||||
4,
|
|
||||||
<AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />,
|
|
||||||
)}
|
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,124 +4,227 @@ import * as _ from 'lodash';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { oc } from 'ts-optchain';
|
import { oc } from 'ts-optchain';
|
||||||
|
|
||||||
import { BIG_NUMBER_ZERO } from '../constants';
|
import { BIG_NUMBER_ZERO, DEFAULT_UNKOWN_ASSET_NAME } from '../constants';
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { BaseCurrency } from '../types';
|
||||||
import { format } from '../util/format';
|
import { format } from '../util/format';
|
||||||
|
|
||||||
import { AmountPlaceholder } from './amount_placeholder';
|
import { AmountPlaceholder } from './amount_placeholder';
|
||||||
|
import { SectionHeader } from './section_header';
|
||||||
|
|
||||||
import { Container } from './ui/container';
|
import { Container } from './ui/container';
|
||||||
import { Flex } from './ui/flex';
|
import { Flex } from './ui/flex';
|
||||||
import { Text } from './ui/text';
|
import { Text, TextProps } from './ui/text';
|
||||||
|
|
||||||
export interface OrderDetailsProps {
|
export interface OrderDetailsProps {
|
||||||
buyQuoteInfo?: BuyQuoteInfo;
|
buyQuoteInfo?: BuyQuoteInfo;
|
||||||
selectedAssetUnitAmount?: BigNumber;
|
selectedAssetUnitAmount?: BigNumber;
|
||||||
ethUsdPrice?: BigNumber;
|
ethUsdPrice?: BigNumber;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
assetName?: string;
|
||||||
|
baseCurrency: BaseCurrency;
|
||||||
|
onBaseCurrencySwitchEth: () => void;
|
||||||
|
onBaseCurrencySwitchUsd: () => void;
|
||||||
}
|
}
|
||||||
export class OrderDetails extends React.Component<OrderDetailsProps> {
|
export class OrderDetails extends React.Component<OrderDetailsProps> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { buyQuoteInfo, ethUsdPrice, selectedAssetUnitAmount } = this.props;
|
const shouldShowUsdError = this.props.baseCurrency === BaseCurrency.USD && this._hadErrorFetchingUsdPrice();
|
||||||
const buyQuoteAccessor = oc(buyQuoteInfo);
|
|
||||||
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 (
|
return (
|
||||||
<Container width="100%" flexGrow={1} padding="20px 20px 0px 20px">
|
<Container width="100%" flexGrow={1} padding="20px 20px 0px 20px">
|
||||||
<Container marginBottom="10px">
|
<Container marginBottom="10px">{this._renderHeader()}</Container>
|
||||||
<Text
|
{shouldShowUsdError ? this._renderErrorFetchingUsdPrice() : this._renderRows()}
|
||||||
letterSpacing="1px"
|
|
||||||
fontColor={ColorOption.primaryColor}
|
|
||||||
fontWeight={600}
|
|
||||||
textTransform="uppercase"
|
|
||||||
fontSize="14px"
|
|
||||||
>
|
|
||||||
Order Details
|
|
||||||
</Text>
|
|
||||||
</Container>
|
|
||||||
<EthAmountRow
|
|
||||||
rowLabel="Token Price"
|
|
||||||
ethAmount={pricePerTokenEth}
|
|
||||||
ethUsdPrice={ethUsdPrice}
|
|
||||||
isLoading={this.props.isLoading}
|
|
||||||
/>
|
|
||||||
<EthAmountRow
|
|
||||||
rowLabel="Fee"
|
|
||||||
ethAmount={feeEthBaseUnitAmount}
|
|
||||||
ethUsdPrice={ethUsdPrice}
|
|
||||||
isLoading={this.props.isLoading}
|
|
||||||
/>
|
|
||||||
<EthAmountRow
|
|
||||||
rowLabel="Total Cost"
|
|
||||||
ethAmount={totalEthBaseUnitAmount}
|
|
||||||
ethUsdPrice={ethUsdPrice}
|
|
||||||
shouldEmphasize={true}
|
|
||||||
isLoading={this.props.isLoading}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderRows(): React.ReactNode {
|
||||||
|
const { buyQuoteInfo } = this.props;
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<OrderDetailsRow
|
||||||
|
labelText={this._assetAmountLabel()}
|
||||||
|
primaryValue={this._displayAmountOrPlaceholder(buyQuoteInfo && buyQuoteInfo.assetEthAmount)}
|
||||||
|
/>
|
||||||
|
<OrderDetailsRow
|
||||||
|
labelText="Fee"
|
||||||
|
primaryValue={this._displayAmountOrPlaceholder(buyQuoteInfo && buyQuoteInfo.feeEthAmount)}
|
||||||
|
/>
|
||||||
|
<OrderDetailsRow
|
||||||
|
labelText="Total Cost"
|
||||||
|
isLabelBold={true}
|
||||||
|
primaryValue={this._displayAmountOrPlaceholder(buyQuoteInfo && buyQuoteInfo.totalEthAmount)}
|
||||||
|
isPrimaryValueBold={true}
|
||||||
|
secondaryValue={this._totalCostSecondaryValue()}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderErrorFetchingUsdPrice(): React.ReactNode {
|
||||||
|
return (
|
||||||
|
<Text>
|
||||||
|
There was an error fetching the USD price.
|
||||||
|
<Text
|
||||||
|
onClick={this.props.onBaseCurrencySwitchEth}
|
||||||
|
fontWeight={700}
|
||||||
|
fontColor={ColorOption.primaryColor}
|
||||||
|
>
|
||||||
|
Click here
|
||||||
|
</Text>
|
||||||
|
{' to view ETH prices'}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hadErrorFetchingUsdPrice(): boolean {
|
||||||
|
return this.props.ethUsdPrice ? this.props.ethUsdPrice.equals(BIG_NUMBER_ZERO) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _totalCostSecondaryValue(): React.ReactNode {
|
||||||
|
const secondaryCurrency = this.props.baseCurrency === BaseCurrency.USD ? BaseCurrency.ETH : BaseCurrency.USD;
|
||||||
|
|
||||||
|
const canDisplayCurrency =
|
||||||
|
secondaryCurrency === BaseCurrency.ETH ||
|
||||||
|
(secondaryCurrency === BaseCurrency.USD && this.props.ethUsdPrice && !this._hadErrorFetchingUsdPrice());
|
||||||
|
|
||||||
|
if (this.props.buyQuoteInfo && canDisplayCurrency) {
|
||||||
|
return this._displayAmount(secondaryCurrency, this.props.buyQuoteInfo.totalEthAmount);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _displayAmountOrPlaceholder(weiAmount?: BigNumber): React.ReactNode {
|
||||||
|
const { baseCurrency, isLoading } = this.props;
|
||||||
|
|
||||||
|
if (_.isUndefined(weiAmount)) {
|
||||||
|
return (
|
||||||
|
<Container opacity={0.5}>
|
||||||
|
<AmountPlaceholder color={ColorOption.lightGrey} isPulsating={isLoading} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._displayAmount(baseCurrency, weiAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _displayAmount(currency: BaseCurrency, weiAmount: BigNumber): React.ReactNode {
|
||||||
|
switch (currency) {
|
||||||
|
case BaseCurrency.USD:
|
||||||
|
return format.ethBaseUnitAmountInUsd(weiAmount, this.props.ethUsdPrice, 2, '');
|
||||||
|
case BaseCurrency.ETH:
|
||||||
|
return format.ethBaseUnitAmount(weiAmount, 4, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _assetAmountLabel(): React.ReactNode {
|
||||||
|
const { assetName, baseCurrency } = this.props;
|
||||||
|
const numTokens = this.props.selectedAssetUnitAmount;
|
||||||
|
|
||||||
|
// Display as 0 if we have a selected asset
|
||||||
|
const displayNumTokens =
|
||||||
|
assetName && assetName !== DEFAULT_UNKOWN_ASSET_NAME && _.isUndefined(numTokens)
|
||||||
|
? new BigNumber(0)
|
||||||
|
: numTokens;
|
||||||
|
if (!_.isUndefined(displayNumTokens)) {
|
||||||
|
let numTokensWithSymbol: React.ReactNode = displayNumTokens.toString();
|
||||||
|
if (assetName) {
|
||||||
|
numTokensWithSymbol += ` ${assetName}`;
|
||||||
|
}
|
||||||
|
const pricePerTokenWei = this._pricePerTokenWei();
|
||||||
|
if (pricePerTokenWei) {
|
||||||
|
const atPriceDisplay = (
|
||||||
|
<Text fontColor={ColorOption.lightGrey}>
|
||||||
|
@ {this._displayAmount(baseCurrency, pricePerTokenWei)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
numTokensWithSymbol = (
|
||||||
|
<React.Fragment>
|
||||||
|
{numTokensWithSymbol} {atPriceDisplay}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return numTokensWithSymbol;
|
||||||
|
}
|
||||||
|
return 'Token Amount';
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pricePerTokenWei(): BigNumber | undefined {
|
||||||
|
const buyQuoteAccessor = oc(this.props.buyQuoteInfo);
|
||||||
|
const assetTotalInWei = buyQuoteAccessor.assetEthAmount();
|
||||||
|
const selectedAssetUnitAmount = this.props.selectedAssetUnitAmount;
|
||||||
|
return !_.isUndefined(assetTotalInWei) &&
|
||||||
|
!_.isUndefined(selectedAssetUnitAmount) &&
|
||||||
|
!selectedAssetUnitAmount.eq(BIG_NUMBER_ZERO)
|
||||||
|
? assetTotalInWei.div(selectedAssetUnitAmount).ceil()
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _baseCurrencyChoice(choice: BaseCurrency): React.ReactNode {
|
||||||
|
const onClick =
|
||||||
|
choice === BaseCurrency.ETH ? this.props.onBaseCurrencySwitchEth : this.props.onBaseCurrencySwitchUsd;
|
||||||
|
const isSelected = this.props.baseCurrency === choice;
|
||||||
|
|
||||||
|
const textStyle: TextProps = { onClick, fontSize: '12px' };
|
||||||
|
if (isSelected) {
|
||||||
|
textStyle.fontColor = ColorOption.primaryColor;
|
||||||
|
textStyle.fontWeight = 700;
|
||||||
|
} else {
|
||||||
|
textStyle.fontColor = ColorOption.lightGrey;
|
||||||
|
}
|
||||||
|
return <Text {...textStyle}>{choice}</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderHeader(): React.ReactNode {
|
||||||
|
return (
|
||||||
|
<Flex justify="space-between">
|
||||||
|
<SectionHeader>Order Details</SectionHeader>
|
||||||
|
<Container>
|
||||||
|
{this._baseCurrencyChoice(BaseCurrency.ETH)}
|
||||||
|
<Container marginLeft="5px" marginRight="5px" display="inline">
|
||||||
|
<Text fontSize="12px" fontColor={ColorOption.feintGrey}>
|
||||||
|
/
|
||||||
|
</Text>
|
||||||
|
</Container>
|
||||||
|
{this._baseCurrencyChoice(BaseCurrency.USD)}
|
||||||
|
</Container>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EthAmountRowProps {
|
export interface OrderDetailsRowProps {
|
||||||
rowLabel: string;
|
labelText: React.ReactNode;
|
||||||
ethAmount?: BigNumber;
|
isLabelBold?: boolean;
|
||||||
isEthAmountInBaseUnits?: boolean;
|
isPrimaryValueBold?: boolean;
|
||||||
ethUsdPrice?: BigNumber;
|
primaryValue: React.ReactNode;
|
||||||
shouldEmphasize?: boolean;
|
secondaryValue?: React.ReactNode;
|
||||||
isLoading: boolean;
|
|
||||||
}
|
}
|
||||||
|
export class OrderDetailsRow extends React.Component<OrderDetailsRowProps, {}> {
|
||||||
export class EthAmountRow extends React.Component<EthAmountRowProps> {
|
|
||||||
public static defaultProps = {
|
|
||||||
shouldEmphasize: false,
|
|
||||||
isEthAmountInBaseUnits: true,
|
|
||||||
};
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { rowLabel, ethAmount, isEthAmountInBaseUnits, shouldEmphasize, isLoading } = this.props;
|
|
||||||
|
|
||||||
const fontWeight = shouldEmphasize ? 700 : 400;
|
|
||||||
const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseUnitAmount : format.ethUnitAmount;
|
|
||||||
return (
|
return (
|
||||||
<Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
|
<Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
|
||||||
<Flex justify="space-between">
|
<Flex justify="space-between">
|
||||||
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
<Text fontWeight={this.props.isLabelBold ? 700 : 400} fontColor={ColorOption.grey}>
|
||||||
{rowLabel}
|
{this.props.labelText}
|
||||||
</Text>
|
</Text>
|
||||||
<Container>
|
<Container>{this._renderValues()}</Container>
|
||||||
{this._renderUsdSection()}
|
|
||||||
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
|
||||||
{ethFormatter(
|
|
||||||
ethAmount,
|
|
||||||
4,
|
|
||||||
<Container opacity={0.5}>
|
|
||||||
<AmountPlaceholder color={ColorOption.lightGrey} isPulsating={isLoading} />
|
|
||||||
</Container>,
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</Container>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private _renderUsdSection(): React.ReactNode {
|
|
||||||
const usdFormatter = this.props.isEthAmountInBaseUnits
|
private _renderValues(): React.ReactNode {
|
||||||
? format.ethBaseUnitAmountInUsd
|
const secondaryValueNode: React.ReactNode = this.props.secondaryValue && (
|
||||||
: format.ethUnitAmountInUsd;
|
|
||||||
const shouldHideUsdPriceSection = _.isUndefined(this.props.ethUsdPrice) || _.isUndefined(this.props.ethAmount);
|
|
||||||
return shouldHideUsdPriceSection ? null : (
|
|
||||||
<Container marginRight="3px" display="inline-block">
|
<Container marginRight="3px" display="inline-block">
|
||||||
<Text fontColor={ColorOption.lightGrey}>
|
<Text fontColor={ColorOption.lightGrey}>({this.props.secondaryValue})</Text>
|
||||||
({usdFormatter(this.props.ethAmount, this.props.ethUsdPrice)})
|
|
||||||
</Text>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{secondaryValueNode}
|
||||||
|
<Text fontWeight={this.props.isPrimaryValueBold ? 700 : 400}>{this.props.primaryValue}</Text>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import { envUtil } from '../util/env';
|
|||||||
import { CoinbaseWalletLogo } from './coinbase_wallet_logo';
|
import { CoinbaseWalletLogo } from './coinbase_wallet_logo';
|
||||||
import { MetaMaskLogo } from './meta_mask_logo';
|
import { MetaMaskLogo } from './meta_mask_logo';
|
||||||
import { PaymentMethodDropdown } from './payment_method_dropdown';
|
import { PaymentMethodDropdown } from './payment_method_dropdown';
|
||||||
|
import { SectionHeader } from './section_header';
|
||||||
import { Circle } from './ui/circle';
|
import { Circle } from './ui/circle';
|
||||||
import { Container } from './ui/container';
|
import { Container } from './ui/container';
|
||||||
import { Flex } from './ui/flex';
|
import { Flex } from './ui/flex';
|
||||||
@ -29,15 +30,7 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> {
|
|||||||
<Container width="100%" height="120px" padding="20px 20px 0px 20px">
|
<Container width="100%" height="120px" padding="20px 20px 0px 20px">
|
||||||
<Container marginBottom="12px">
|
<Container marginBottom="12px">
|
||||||
<Flex justify="space-between">
|
<Flex justify="space-between">
|
||||||
<Text
|
<SectionHeader>{this._renderTitleText()}</SectionHeader>
|
||||||
letterSpacing="1px"
|
|
||||||
fontColor={ColorOption.primaryColor}
|
|
||||||
fontWeight={600}
|
|
||||||
textTransform="uppercase"
|
|
||||||
fontSize="14px"
|
|
||||||
>
|
|
||||||
{this._renderTitleText()}
|
|
||||||
</Text>
|
|
||||||
{this._renderTitleLabel()}
|
{this._renderTitleLabel()}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
20
packages/instant/src/components/section_header.tsx
Normal file
20
packages/instant/src/components/section_header.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ColorOption } from '../style/theme';
|
||||||
|
|
||||||
|
import { Text } from './ui/text';
|
||||||
|
|
||||||
|
export interface SectionHeaderProps {}
|
||||||
|
export const SectionHeader: React.StatelessComponent<SectionHeaderProps> = props => {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
letterSpacing="1px"
|
||||||
|
fontColor={ColorOption.primaryColor}
|
||||||
|
fontWeight={600}
|
||||||
|
textTransform="uppercase"
|
||||||
|
fontSize="12px"
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
@ -122,6 +122,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
window,
|
window,
|
||||||
state.selectedAsset,
|
state.selectedAsset,
|
||||||
this.props.affiliateInfo,
|
this.props.affiliateInfo,
|
||||||
|
state.baseCurrency,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
analytics.trackInstantOpened();
|
analytics.trackInstantOpened();
|
||||||
|
@ -17,6 +17,7 @@ export const ONE_MINUTE_MS = ONE_SECOND_MS * 60;
|
|||||||
export const GIT_SHA = process.env.GIT_SHA;
|
export const GIT_SHA = process.env.GIT_SHA;
|
||||||
export const NODE_ENV = process.env.NODE_ENV;
|
export const NODE_ENV = process.env.NODE_ENV;
|
||||||
export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION;
|
export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION;
|
||||||
|
export const DEFAULT_UNKOWN_ASSET_NAME = '???';
|
||||||
export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5;
|
export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5;
|
||||||
export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15;
|
export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15;
|
||||||
export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
|
export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
|
||||||
|
@ -1,32 +1,41 @@
|
|||||||
import { BuyQuoteInfo } from '@0x/asset-buyer';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { oc } from 'ts-optchain';
|
import { oc } from 'ts-optchain';
|
||||||
|
|
||||||
|
import { Action, actions } from '../redux/actions';
|
||||||
import { State } from '../redux/reducer';
|
import { State } from '../redux/reducer';
|
||||||
|
|
||||||
import { OrderDetails } from '../components/order_details';
|
import { OrderDetails, OrderDetailsProps } from '../components/order_details';
|
||||||
import { AsyncProcessState } from '../types';
|
import { AsyncProcessState, BaseCurrency, Omit } from '../types';
|
||||||
|
import { assetUtils } from '../util/asset';
|
||||||
|
|
||||||
export interface LatestBuyQuoteOrderDetailsProps {}
|
type DispatchProperties = 'onBaseCurrencySwitchEth' | 'onBaseCurrencySwitchUsd';
|
||||||
|
|
||||||
interface ConnectedState {
|
|
||||||
buyQuoteInfo?: BuyQuoteInfo;
|
|
||||||
selectedAssetUnitAmount?: BigNumber;
|
|
||||||
ethUsdPrice?: BigNumber;
|
|
||||||
isLoading: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface ConnectedState extends Omit<OrderDetailsProps, DispatchProperties> {}
|
||||||
const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({
|
const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({
|
||||||
// use the worst case quote info
|
// use the worst case quote info
|
||||||
buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(),
|
buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(),
|
||||||
selectedAssetUnitAmount: state.selectedAssetUnitAmount,
|
selectedAssetUnitAmount: state.selectedAssetUnitAmount,
|
||||||
ethUsdPrice: state.ethUsdPrice,
|
ethUsdPrice: state.ethUsdPrice,
|
||||||
isLoading: state.quoteRequestState === AsyncProcessState.Pending,
|
isLoading: state.quoteRequestState === AsyncProcessState.Pending,
|
||||||
|
assetName: assetUtils.bestNameForAsset(state.selectedAsset),
|
||||||
|
baseCurrency: state.baseCurrency,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface ConnectedDispatch extends Pick<OrderDetailsProps, DispatchProperties> {}
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch<Action>): ConnectedDispatch => ({
|
||||||
|
onBaseCurrencySwitchEth: () => {
|
||||||
|
dispatch(actions.updateBaseCurrency(BaseCurrency.ETH));
|
||||||
|
},
|
||||||
|
onBaseCurrencySwitchUsd: () => {
|
||||||
|
dispatch(actions.updateBaseCurrency(BaseCurrency.USD));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface LatestBuyQuoteOrderDetailsProps {}
|
||||||
export const LatestBuyQuoteOrderDetails: React.ComponentClass<LatestBuyQuoteOrderDetailsProps> = connect(
|
export const LatestBuyQuoteOrderDetails: React.ComponentClass<LatestBuyQuoteOrderDetailsProps> = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
)(OrderDetails);
|
)(OrderDetails);
|
||||||
|
@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer';
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ActionsUnion, AddressAndEthBalanceInWei, Asset, StandardSlidingPanelContent } from '../types';
|
import { ActionsUnion, AddressAndEthBalanceInWei, Asset, BaseCurrency, StandardSlidingPanelContent } from '../types';
|
||||||
|
|
||||||
export interface PlainAction<T extends string> {
|
export interface PlainAction<T extends string> {
|
||||||
type: T;
|
type: T;
|
||||||
@ -43,6 +43,7 @@ export enum ActionTypes {
|
|||||||
RESET_AMOUNT = 'RESET_AMOUNT',
|
RESET_AMOUNT = 'RESET_AMOUNT',
|
||||||
OPEN_STANDARD_SLIDING_PANEL = 'OPEN_STANDARD_SLIDING_PANEL',
|
OPEN_STANDARD_SLIDING_PANEL = 'OPEN_STANDARD_SLIDING_PANEL',
|
||||||
CLOSE_STANDARD_SLIDING_PANEL = 'CLOSE_STANDARD_SLIDING_PANEL',
|
CLOSE_STANDARD_SLIDING_PANEL = 'CLOSE_STANDARD_SLIDING_PANEL',
|
||||||
|
UPDATE_BASE_CURRENCY = 'UPDATE_BASE_CURRENCY',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
@ -72,4 +73,5 @@ export const actions = {
|
|||||||
openStandardSlidingPanel: (content: StandardSlidingPanelContent) =>
|
openStandardSlidingPanel: (content: StandardSlidingPanelContent) =>
|
||||||
createAction(ActionTypes.OPEN_STANDARD_SLIDING_PANEL, content),
|
createAction(ActionTypes.OPEN_STANDARD_SLIDING_PANEL, content),
|
||||||
closeStandardSlidingPanel: () => createAction(ActionTypes.CLOSE_STANDARD_SLIDING_PANEL),
|
closeStandardSlidingPanel: () => createAction(ActionTypes.CLOSE_STANDARD_SLIDING_PANEL),
|
||||||
|
updateBaseCurrency: (baseCurrency: BaseCurrency) => createAction(ActionTypes.UPDATE_BASE_CURRENCY, baseCurrency),
|
||||||
};
|
};
|
||||||
|
@ -99,6 +99,9 @@ export const analyticsMiddleware: Middleware = store => next => middlewareAction
|
|||||||
analytics.trackInstallWalletModalClosed();
|
analytics.trackInstallWalletModalClosed();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.UPDATE_BASE_CURRENCY:
|
||||||
|
analytics.trackBaseCurrencyChanged(curState.baseCurrency);
|
||||||
|
analytics.addEventProperties({ baseCurrency: curState.baseCurrency });
|
||||||
}
|
}
|
||||||
|
|
||||||
return nextAction;
|
return nextAction;
|
||||||
|
@ -4,7 +4,7 @@ import * as _ from 'lodash';
|
|||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { BIG_NUMBER_ZERO } from '../constants';
|
import { BIG_NUMBER_ZERO } from '../constants';
|
||||||
import { AccountState, ERC20Asset, OrderProcessState, ProviderState, QuoteFetchOrigin } from '../types';
|
import { AccountState, BaseCurrency, ERC20Asset, OrderProcessState, ProviderState, QuoteFetchOrigin } from '../types';
|
||||||
import { analytics } from '../util/analytics';
|
import { analytics } from '../util/analytics';
|
||||||
import { assetUtils } from '../util/asset';
|
import { assetUtils } from '../util/asset';
|
||||||
import { buyQuoteUpdater } from '../util/buy_quote_updater';
|
import { buyQuoteUpdater } from '../util/buy_quote_updater';
|
||||||
@ -24,7 +24,9 @@ export const asyncData = {
|
|||||||
const errorMessage = 'Error fetching ETH/USD price';
|
const errorMessage = 'Error fetching ETH/USD price';
|
||||||
errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
|
errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
|
||||||
dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO));
|
dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO));
|
||||||
|
dispatch(actions.updateBaseCurrency(BaseCurrency.ETH));
|
||||||
errorReporter.report(e);
|
errorReporter.report(e);
|
||||||
|
analytics.trackUsdPriceFailed();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => {
|
fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => {
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
Asset,
|
Asset,
|
||||||
AssetMetaData,
|
AssetMetaData,
|
||||||
AsyncProcessState,
|
AsyncProcessState,
|
||||||
|
BaseCurrency,
|
||||||
DisplayStatus,
|
DisplayStatus,
|
||||||
Network,
|
Network,
|
||||||
OrderProcessState,
|
OrderProcessState,
|
||||||
@ -33,6 +34,7 @@ export interface DefaultState {
|
|||||||
latestErrorDisplayStatus: DisplayStatus;
|
latestErrorDisplayStatus: DisplayStatus;
|
||||||
quoteRequestState: AsyncProcessState;
|
quoteRequestState: AsyncProcessState;
|
||||||
standardSlidingPanelSettings: StandardSlidingPanelSettings;
|
standardSlidingPanelSettings: StandardSlidingPanelSettings;
|
||||||
|
baseCurrency: BaseCurrency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// State that is required but needs to be derived from the props
|
// State that is required but needs to be derived from the props
|
||||||
@ -64,6 +66,7 @@ export const DEFAULT_STATE: DefaultState = {
|
|||||||
animationState: 'none',
|
animationState: 'none',
|
||||||
content: StandardSlidingPanelContent.None,
|
content: StandardSlidingPanelContent.None,
|
||||||
},
|
},
|
||||||
|
baseCurrency: BaseCurrency.USD,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createReducer = (initialState: State) => {
|
export const createReducer = (initialState: State) => {
|
||||||
@ -243,6 +246,11 @@ export const createReducer = (initialState: State) => {
|
|||||||
animationState: 'slidOut',
|
animationState: 'slidOut',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
case ActionTypes.UPDATE_BASE_CURRENCY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
baseCurrency: action.data,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ export enum QuoteFetchOrigin {
|
|||||||
Heartbeat = 'Heartbeat',
|
Heartbeat = 'Heartbeat',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BaseCurrency {
|
||||||
|
USD = 'USD',
|
||||||
|
ETH = 'ETH',
|
||||||
|
}
|
||||||
|
|
||||||
export interface SimulatedProgress {
|
export interface SimulatedProgress {
|
||||||
startTimeUnix: number;
|
startTimeUnix: number;
|
||||||
expectedEndTimeUnix: number;
|
expectedEndTimeUnix: number;
|
||||||
|
@ -6,6 +6,7 @@ import { GIT_SHA, HEAP_ENABLED, INSTANT_DISCHARGE_TARGET, NODE_ENV, NPM_PACKAGE_
|
|||||||
import {
|
import {
|
||||||
AffiliateInfo,
|
AffiliateInfo,
|
||||||
Asset,
|
Asset,
|
||||||
|
BaseCurrency,
|
||||||
Network,
|
Network,
|
||||||
OrderProcessState,
|
OrderProcessState,
|
||||||
OrderSource,
|
OrderSource,
|
||||||
@ -37,6 +38,7 @@ enum EventNames {
|
|||||||
ACCOUNT_UNLOCK_REQUESTED = 'Account - Unlock Requested',
|
ACCOUNT_UNLOCK_REQUESTED = 'Account - Unlock Requested',
|
||||||
ACCOUNT_UNLOCK_DENIED = 'Account - Unlock Denied',
|
ACCOUNT_UNLOCK_DENIED = 'Account - Unlock Denied',
|
||||||
ACCOUNT_ADDRESS_CHANGED = 'Account - Address Changed',
|
ACCOUNT_ADDRESS_CHANGED = 'Account - Address Changed',
|
||||||
|
BASE_CURRENCY_CHANGED = 'Base Currency - Changed',
|
||||||
PAYMENT_METHOD_DROPDOWN_OPENED = 'Payment Method - Dropdown Opened',
|
PAYMENT_METHOD_DROPDOWN_OPENED = 'Payment Method - Dropdown Opened',
|
||||||
PAYMENT_METHOD_OPENED_ETHERSCAN = 'Payment Method - Opened Etherscan',
|
PAYMENT_METHOD_OPENED_ETHERSCAN = 'Payment Method - Opened Etherscan',
|
||||||
PAYMENT_METHOD_COPIED_ADDRESS = 'Payment Method - Copied Address',
|
PAYMENT_METHOD_COPIED_ADDRESS = 'Payment Method - Copied Address',
|
||||||
@ -47,6 +49,7 @@ enum EventNames {
|
|||||||
BUY_TX_SUBMITTED = 'Buy - Tx Submitted',
|
BUY_TX_SUBMITTED = 'Buy - Tx Submitted',
|
||||||
BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded',
|
BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded',
|
||||||
BUY_TX_FAILED = 'Buy - Tx Failed',
|
BUY_TX_FAILED = 'Buy - Tx Failed',
|
||||||
|
USD_PRICE_FETCH_FAILED = 'USD Price - Fetch Failed',
|
||||||
INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked',
|
INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked',
|
||||||
INSTALL_WALLET_MODAL_OPENED = 'Install Wallet - Modal - Opened',
|
INSTALL_WALLET_MODAL_OPENED = 'Install Wallet - Modal - Opened',
|
||||||
INSTALL_WALLET_MODAL_CLICKED_EXPLANATION = 'Install Wallet - Modal - Clicked Explanation',
|
INSTALL_WALLET_MODAL_CLICKED_EXPLANATION = 'Install Wallet - Modal - Clicked Explanation',
|
||||||
@ -118,6 +121,7 @@ export interface AnalyticsEventOptions {
|
|||||||
selectedAssetSymbol?: string;
|
selectedAssetSymbol?: string;
|
||||||
selectedAssetData?: string;
|
selectedAssetData?: string;
|
||||||
selectedAssetDecimals?: number;
|
selectedAssetDecimals?: number;
|
||||||
|
baseCurrency?: string;
|
||||||
}
|
}
|
||||||
export enum TokenSelectorClosedVia {
|
export enum TokenSelectorClosedVia {
|
||||||
ClickedX = 'Clicked X',
|
ClickedX = 'Clicked X',
|
||||||
@ -141,6 +145,7 @@ export const analytics = {
|
|||||||
window: Window,
|
window: Window,
|
||||||
selectedAsset?: Asset,
|
selectedAsset?: Asset,
|
||||||
affiliateInfo?: AffiliateInfo,
|
affiliateInfo?: AffiliateInfo,
|
||||||
|
baseCurrency?: BaseCurrency,
|
||||||
): AnalyticsEventOptions => {
|
): AnalyticsEventOptions => {
|
||||||
const affiliateAddress = affiliateInfo ? affiliateInfo.feeRecipient : 'none';
|
const affiliateAddress = affiliateInfo ? affiliateInfo.feeRecipient : 'none';
|
||||||
const affiliateFeePercent = affiliateInfo ? parseFloat(affiliateInfo.feePercentage.toFixed(4)) : 0;
|
const affiliateFeePercent = affiliateInfo ? parseFloat(affiliateInfo.feePercentage.toFixed(4)) : 0;
|
||||||
@ -159,6 +164,7 @@ export const analytics = {
|
|||||||
selectedAssetName: selectedAsset ? selectedAsset.metaData.name : 'none',
|
selectedAssetName: selectedAsset ? selectedAsset.metaData.name : 'none',
|
||||||
selectedAssetData: selectedAsset ? selectedAsset.assetData : 'none',
|
selectedAssetData: selectedAsset ? selectedAsset.assetData : 'none',
|
||||||
instantEnvironment: INSTANT_DISCHARGE_TARGET || `Local ${NODE_ENV}`,
|
instantEnvironment: INSTANT_DISCHARGE_TARGET || `Local ${NODE_ENV}`,
|
||||||
|
baseCurrency,
|
||||||
};
|
};
|
||||||
return eventOptions;
|
return eventOptions;
|
||||||
},
|
},
|
||||||
@ -170,6 +176,8 @@ export const analytics = {
|
|||||||
trackAccountUnlockDenied: trackingEventFnWithoutPayload(EventNames.ACCOUNT_UNLOCK_DENIED),
|
trackAccountUnlockDenied: trackingEventFnWithoutPayload(EventNames.ACCOUNT_UNLOCK_DENIED),
|
||||||
trackAccountAddressChanged: (address: string) =>
|
trackAccountAddressChanged: (address: string) =>
|
||||||
trackingEventFnWithPayload(EventNames.ACCOUNT_ADDRESS_CHANGED)({ address }),
|
trackingEventFnWithPayload(EventNames.ACCOUNT_ADDRESS_CHANGED)({ address }),
|
||||||
|
trackBaseCurrencyChanged: (currencyChangedTo: BaseCurrency) =>
|
||||||
|
trackingEventFnWithPayload(EventNames.BASE_CURRENCY_CHANGED)({ currencyChangedTo }),
|
||||||
trackPaymentMethodDropdownOpened: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_DROPDOWN_OPENED),
|
trackPaymentMethodDropdownOpened: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_DROPDOWN_OPENED),
|
||||||
trackPaymentMethodOpenedEtherscan: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_OPENED_ETHERSCAN),
|
trackPaymentMethodOpenedEtherscan: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_OPENED_ETHERSCAN),
|
||||||
trackPaymentMethodCopiedAddress: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_COPIED_ADDRESS),
|
trackPaymentMethodCopiedAddress: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_COPIED_ADDRESS),
|
||||||
@ -230,4 +238,5 @@ export const analytics = {
|
|||||||
fetchOrigin,
|
fetchOrigin,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
trackUsdPriceFailed: trackingEventFnWithoutPayload(EventNames.USD_PRICE_FETCH_FAILED),
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import { AssetBuyerError } from '@0x/asset-buyer';
|
|||||||
import { AssetProxyId, ObjectMap } from '@0x/types';
|
import { AssetProxyId, ObjectMap } from '@0x/types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { DEFAULT_UNKOWN_ASSET_NAME } from '../constants';
|
||||||
import { assetDataNetworkMapping } from '../data/asset_data_network_mapping';
|
import { assetDataNetworkMapping } from '../data/asset_data_network_mapping';
|
||||||
import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types';
|
import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types';
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ export const assetUtils = {
|
|||||||
}
|
}
|
||||||
return metaData;
|
return metaData;
|
||||||
},
|
},
|
||||||
bestNameForAsset: (asset?: Asset, defaultName: string = '???'): string => {
|
bestNameForAsset: (asset?: Asset, defaultName: string = DEFAULT_UNKOWN_ASSET_NAME): string => {
|
||||||
if (_.isUndefined(asset)) {
|
if (_.isUndefined(asset)) {
|
||||||
return defaultName;
|
return defaultName;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ETH_DECIMALS } from '../constants';
|
import { BIG_NUMBER_ZERO, ETH_DECIMALS } from '../constants';
|
||||||
|
|
||||||
export const format = {
|
export const format = {
|
||||||
ethBaseUnitAmount: (
|
ethBaseUnitAmount: (
|
||||||
@ -20,24 +20,38 @@ export const format = {
|
|||||||
ethUnitAmount?: BigNumber,
|
ethUnitAmount?: BigNumber,
|
||||||
decimalPlaces: number = 4,
|
decimalPlaces: number = 4,
|
||||||
defaultText: React.ReactNode = '0 ETH',
|
defaultText: React.ReactNode = '0 ETH',
|
||||||
|
minUnitAmountToDisplay: BigNumber = new BigNumber('0.00001'),
|
||||||
): React.ReactNode => {
|
): React.ReactNode => {
|
||||||
if (_.isUndefined(ethUnitAmount)) {
|
if (_.isUndefined(ethUnitAmount)) {
|
||||||
return defaultText;
|
return defaultText;
|
||||||
}
|
}
|
||||||
const roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces);
|
let roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces);
|
||||||
return `${roundedAmount} ETH`;
|
|
||||||
|
if (roundedAmount.eq(BIG_NUMBER_ZERO) && ethUnitAmount.greaterThan(BIG_NUMBER_ZERO)) {
|
||||||
|
// Sometimes for small ETH amounts (i.e. 0.000045) the amount rounded to 4 decimalPlaces is 0
|
||||||
|
// If that is the case, show to 1 significant digit
|
||||||
|
roundedAmount = new BigNumber(ethUnitAmount.toPrecision(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayAmount =
|
||||||
|
roundedAmount.greaterThan(BIG_NUMBER_ZERO) && roundedAmount.lessThan(minUnitAmountToDisplay)
|
||||||
|
? `< ${minUnitAmountToDisplay.toString()}`
|
||||||
|
: roundedAmount.toString();
|
||||||
|
|
||||||
|
return `${displayAmount} ETH`;
|
||||||
},
|
},
|
||||||
ethBaseUnitAmountInUsd: (
|
ethBaseUnitAmountInUsd: (
|
||||||
ethBaseUnitAmount?: BigNumber,
|
ethBaseUnitAmount?: BigNumber,
|
||||||
ethUsdPrice?: BigNumber,
|
ethUsdPrice?: BigNumber,
|
||||||
decimalPlaces: number = 2,
|
decimalPlaces: number = 2,
|
||||||
defaultText: React.ReactNode = '$0.00',
|
defaultText: React.ReactNode = '$0.00',
|
||||||
|
minUnitAmountToDisplay: BigNumber = new BigNumber('0.00001'),
|
||||||
): React.ReactNode => {
|
): React.ReactNode => {
|
||||||
if (_.isUndefined(ethBaseUnitAmount) || _.isUndefined(ethUsdPrice)) {
|
if (_.isUndefined(ethBaseUnitAmount) || _.isUndefined(ethUsdPrice)) {
|
||||||
return defaultText;
|
return defaultText;
|
||||||
}
|
}
|
||||||
const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS);
|
const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS);
|
||||||
return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces);
|
return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces, minUnitAmountToDisplay);
|
||||||
},
|
},
|
||||||
ethUnitAmountInUsd: (
|
ethUnitAmountInUsd: (
|
||||||
ethUnitAmount?: BigNumber,
|
ethUnitAmount?: BigNumber,
|
||||||
@ -48,7 +62,13 @@ export const format = {
|
|||||||
if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) {
|
if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) {
|
||||||
return defaultText;
|
return defaultText;
|
||||||
}
|
}
|
||||||
return `$${ethUnitAmount.mul(ethUsdPrice).toFixed(decimalPlaces)}`;
|
const rawUsdPrice = ethUnitAmount.mul(ethUsdPrice);
|
||||||
|
const roundedUsdPrice = rawUsdPrice.toFixed(decimalPlaces);
|
||||||
|
if (roundedUsdPrice === '0.00' && rawUsdPrice.gt(BIG_NUMBER_ZERO)) {
|
||||||
|
return '<$0.01';
|
||||||
|
} else {
|
||||||
|
return `$${roundedUsdPrice}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ethAddress: (address: string): string => {
|
ethAddress: (address: string): string => {
|
||||||
return `0x${address.slice(2, 7)}…${address.slice(-5)}`;
|
return `0x${address.slice(2, 7)}…${address.slice(-5)}`;
|
||||||
|
@ -41,6 +41,18 @@ describe('format', () => {
|
|||||||
it('converts BigNumber(5.3014059295032) to the string `5.301 ETH`', () => {
|
it('converts BigNumber(5.3014059295032) to the string `5.301 ETH`', () => {
|
||||||
expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.301 ETH');
|
expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.301 ETH');
|
||||||
});
|
});
|
||||||
|
it('shows 1 significant digit when rounded amount would be 0', () => {
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.00003))).toBe('0.00003 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.000034))).toBe('0.00003 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.000035))).toBe('0.00004 ETH');
|
||||||
|
});
|
||||||
|
it('shows < 0.00001 when hits threshold', () => {
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.000011))).toBe('0.00001 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.00001))).toBe('0.00001 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.000009))).toBe('< 0.00001 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0.0000000009))).toBe('< 0.00001 ETH');
|
||||||
|
expect(format.ethUnitAmount(new BigNumber(0))).toBe('0 ETH');
|
||||||
|
});
|
||||||
it('returns defaultText param when ethUnitAmount is not defined', () => {
|
it('returns defaultText param when ethUnitAmount is not defined', () => {
|
||||||
const defaultText = 'defaultText';
|
const defaultText = 'defaultText';
|
||||||
expect(format.ethUnitAmount(undefined, 4, defaultText)).toBe(defaultText);
|
expect(format.ethUnitAmount(undefined, 4, defaultText)).toBe(defaultText);
|
||||||
@ -86,6 +98,12 @@ describe('format', () => {
|
|||||||
it('correctly formats 5.3014059295032 ETH to usd according to some price', () => {
|
it('correctly formats 5.3014059295032 ETH to usd according to some price', () => {
|
||||||
expect(format.ethUnitAmountInUsd(BIG_NUMBER_IRRATIONAL, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$13.43');
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_IRRATIONAL, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$13.43');
|
||||||
});
|
});
|
||||||
|
it('correctly formats amount that is less than 1 cent', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(new BigNumber(0.000001), BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('<$0.01');
|
||||||
|
});
|
||||||
|
it('correctly formats exactly 1 cent', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(new BigNumber(0.0039), BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$0.01');
|
||||||
|
});
|
||||||
it('returns defaultText param when ethUnitAmountInUsd or ethUsdPrice is not defined', () => {
|
it('returns defaultText param when ethUnitAmountInUsd or ethUsdPrice is not defined', () => {
|
||||||
const defaultText = 'defaultText';
|
const defaultText = 'defaultText';
|
||||||
expect(format.ethUnitAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText);
|
expect(format.ethUnitAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user