aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/instant/src')
-rw-r--r--packages/instant/src/components/animations/position_animation.tsx2
-rw-r--r--packages/instant/src/components/animations/slide_animation.tsx3
-rw-r--r--packages/instant/src/components/buy_button.tsx28
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx8
-rw-r--r--packages/instant/src/components/css_reset.tsx1
-rw-r--r--packages/instant/src/components/erc20_asset_amount_input.tsx2
-rw-r--r--packages/instant/src/components/erc20_token_selector.tsx6
-rw-r--r--packages/instant/src/components/payment_method.tsx45
-rw-r--r--packages/instant/src/components/payment_method_dropdown.tsx44
-rw-r--r--packages/instant/src/components/sliding_error.tsx2
-rw-r--r--packages/instant/src/components/sliding_panel.tsx5
-rw-r--r--packages/instant/src/components/ui/circle.tsx19
-rw-r--r--packages/instant/src/components/ui/container.tsx3
-rw-r--r--packages/instant/src/components/ui/dropdown.tsx134
-rw-r--r--packages/instant/src/components/ui/flex.tsx1
-rw-r--r--packages/instant/src/components/ui/icon.tsx9
-rw-r--r--packages/instant/src/components/ui/overlay.tsx31
-rw-r--r--packages/instant/src/components/ui/text.tsx1
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx4
-rw-r--r--packages/instant/src/components/zero_ex_instant_overlay.tsx23
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx2
-rw-r--r--packages/instant/src/constants.ts14
-rw-r--r--packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts14
-rw-r--r--packages/instant/src/redux/actions.ts13
-rw-r--r--packages/instant/src/redux/async_data.ts40
-rw-r--r--packages/instant/src/redux/reducer.ts43
-rw-r--r--packages/instant/src/style/theme.ts5
-rw-r--r--packages/instant/src/style/z_index.ts10
-rw-r--r--packages/instant/src/types.ts5
-rw-r--r--packages/instant/src/util/balance.ts13
-rw-r--r--packages/instant/src/util/etherscan.ts7
-rw-r--r--packages/instant/src/util/format.ts3
-rw-r--r--packages/instant/src/util/provider_state_factory.ts10
33 files changed, 455 insertions, 95 deletions
diff --git a/packages/instant/src/components/animations/position_animation.tsx b/packages/instant/src/components/animations/position_animation.tsx
index 576d29c07..8b3b294b7 100644
--- a/packages/instant/src/components/animations/position_animation.tsx
+++ b/packages/instant/src/components/animations/position_animation.tsx
@@ -77,6 +77,7 @@ const generatePositionAnimationCss = (positionSettings: PositionAnimationSetting
export interface PositionAnimationProps {
positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
zIndex?: OptionallyScreenSpecific<number>;
+ height?: string;
}
const defaultAnimation = (positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>) => {
@@ -104,5 +105,6 @@ export const PositionAnimation =
${props => animationForSize(props.positionSettings, 'sm', media.small)}
${props => animationForSize(props.positionSettings, 'md', media.medium)}
${props => animationForSize(props.positionSettings, 'lg', media.large)}
+ ${props => (props.height ? `height: ${props.height};` : '')}
}
`;
diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx
index 122229dee..9adb1c674 100644
--- a/packages/instant/src/components/animations/slide_animation.tsx
+++ b/packages/instant/src/components/animations/slide_animation.tsx
@@ -10,6 +10,7 @@ export interface SlideAnimationProps {
slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
slideOutSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
zIndex?: OptionallyScreenSpecific<number>;
+ height?: string;
}
export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
@@ -18,7 +19,7 @@ export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = pro
}
const positionSettings = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
return (
- <PositionAnimation positionSettings={positionSettings} zIndex={props.zIndex}>
+ <PositionAnimation height={props.height} positionSettings={positionSettings} zIndex={props.zIndex}>
{props.children}
</PositionAnimation>
);
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 5b07e7416..877ab275c 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -1,4 +1,5 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import * as React from 'react';
@@ -7,16 +8,17 @@ import { oc } from 'ts-optchain';
import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
import { ColorOption } from '../style/theme';
import { AffiliateInfo, ZeroExInstantError } from '../types';
-import { getBestAddress } from '../util/address';
-import { balanceUtil } from '../util/balance';
import { gasPriceEstimator } from '../util/gas_price_estimator';
import { util } from '../util/util';
import { Button } from './ui/button';
export interface BuyButtonProps {
+ accountAddress?: string;
+ accountEthBalanceInWei?: BigNumber;
buyQuote?: BuyQuote;
assetBuyer: AssetBuyer;
+ web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
onValidationPending: (buyQuote: BuyQuote) => void;
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
@@ -33,7 +35,8 @@ export class BuyButton extends React.Component<BuyButtonProps> {
onBuyFailure: util.boundNoop,
};
public render(): React.ReactNode {
- const shouldDisableButton = _.isUndefined(this.props.buyQuote);
+ const { buyQuote, accountAddress } = this.props;
+ const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress);
return (
<Button
width="100%"
@@ -48,30 +51,25 @@ export class BuyButton extends React.Component<BuyButtonProps> {
}
private readonly _handleClick = async () => {
// The button is disabled when there is no buy quote anyway.
- const { buyQuote, assetBuyer, affiliateInfo } = this.props;
- if (_.isUndefined(buyQuote)) {
+ const { buyQuote, assetBuyer, affiliateInfo, accountAddress, accountEthBalanceInWei, web3Wrapper } = this.props;
+ if (_.isUndefined(buyQuote) || _.isUndefined(accountAddress)) {
return;
}
-
this.props.onValidationPending(buyQuote);
-
- // TODO(bmillman): move address and balance fetching to the async state
- const web3Wrapper = new Web3Wrapper(assetBuyer.provider);
- const takerAddress = await getBestAddress(web3Wrapper);
-
- const hasSufficientEth = await balanceUtil.hasSufficientEth(takerAddress, buyQuote, web3Wrapper);
+ const ethNeededForBuy = buyQuote.worstCaseQuoteInfo.totalEthAmount;
+ // if we don't have a balance for the user, let the transaction through, it will be handled by the wallet
+ const hasSufficientEth = _.isUndefined(accountEthBalanceInWei) || accountEthBalanceInWei.gte(ethNeededForBuy);
if (!hasSufficientEth) {
this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH);
return;
}
-
let txHash: string | undefined;
const gasInfo = await gasPriceEstimator.getGasInfoAsync();
const feeRecipient = oc(affiliateInfo).feeRecipient();
try {
txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, {
feeRecipient,
- takerAddress,
+ takerAddress: accountAddress,
gasPrice: gasInfo.gasPriceInWei,
});
} catch (e) {
@@ -86,7 +84,6 @@ export class BuyButton extends React.Component<BuyButtonProps> {
}
throw e;
}
-
const startTimeUnix = new Date().getTime();
const expectedEndTimeUnix = startTimeUnix + gasInfo.estimatedTimeMs;
this.props.onBuyProcessing(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
@@ -99,7 +96,6 @@ export class BuyButton extends React.Component<BuyButtonProps> {
}
throw e;
}
-
this.props.onBuySuccess(buyQuote, txHash);
};
}
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index bdac25cf2..6041bf4f5 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -1,4 +1,6 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as React from 'react';
import { ColorOption } from '../style/theme';
@@ -12,9 +14,12 @@ import { Button } from './ui/button';
import { Flex } from './ui/flex';
export interface BuyOrderStateButtonProps {
+ accountAddress?: string;
+ accountEthBalanceInWei?: BigNumber;
buyQuote?: BuyQuote;
buyOrderProcessingState: OrderProcessState;
assetBuyer: AssetBuyer;
+ web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
onViewTransaction: () => void;
onValidationPending: (buyQuote: BuyQuote) => void;
@@ -49,8 +54,11 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP
return (
<BuyButton
+ accountAddress={props.accountAddress}
+ accountEthBalanceInWei={props.accountEthBalanceInWei}
buyQuote={props.buyQuote}
assetBuyer={props.assetBuyer}
+ web3Wrapper={props.web3Wrapper}
affiliateInfo={props.affiliateInfo}
onValidationPending={props.onValidationPending}
onValidationFail={props.onValidationFail}
diff --git a/packages/instant/src/components/css_reset.tsx b/packages/instant/src/components/css_reset.tsx
index a1dd2e05c..0bef85389 100644
--- a/packages/instant/src/components/css_reset.tsx
+++ b/packages/instant/src/components/css_reset.tsx
@@ -27,7 +27,6 @@ export const CSSReset = createGlobalStyle`
text-align: left;
text-decoration: none;
vertical-align: baseline;
- z-index: 1;
}
}
`;
diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx
index f21c21b87..520ac33d5 100644
--- a/packages/instant/src/components/erc20_asset_amount_input.tsx
+++ b/packages/instant/src/components/erc20_asset_amount_input.tsx
@@ -113,7 +113,7 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
}
return (
<Container marginLeft="5px">
- <Icon icon="chevron" width={12} onClick={this._handleSelectAssetClick} />
+ <Icon icon="chevron" width={12} stroke={ColorOption.white} onClick={this._handleSelectAssetClick} />
</Container>
);
};
diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx
index 76d5c66ff..3503ff31a 100644
--- a/packages/instant/src/components/erc20_token_selector.tsx
+++ b/packages/instant/src/components/erc20_token_selector.tsx
@@ -28,14 +28,14 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps>
public render(): React.ReactNode {
const { tokens, onTokenSelect } = this.props;
return (
- <Container>
+ <Container height="100%">
<SearchInput
placeholder="Search tokens..."
width="100%"
value={this.state.searchQuery}
onChange={this._handleSearchInputChange}
/>
- <Container overflow="scroll" height={{ default: '275px', sm: '75vh' }} marginTop="10px">
+ <Container overflow="scroll" height="calc(100% - 80px)" marginTop="10px">
{_.map(tokens, token => {
if (!this._isTokenQueryMatch(token)) {
return null;
@@ -85,7 +85,7 @@ class TokenSelectorRow extends React.Component<TokenSelectorRowProps> {
<Container marginLeft="5px">
<Flex justify="flex-start">
<Container marginRight="10px">
- <Circle diameter={30} fillColor={token.metaData.primaryColor}>
+ <Circle diameter={30} rawColor={token.metaData.primaryColor}>
<Flex height="100%">
<Text fontColor={ColorOption.white} fontSize="8px">
{displaySymbol}
diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx
new file mode 100644
index 000000000..8c0b47d72
--- /dev/null
+++ b/packages/instant/src/components/payment_method.tsx
@@ -0,0 +1,45 @@
+import { BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+import { Network } from '../types';
+
+import { PaymentMethodDropdown } from './payment_method_dropdown';
+import { Circle } from './ui/circle';
+import { Container } from './ui/container';
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface PaymentMethodProps {}
+
+export const PaymentMethod: React.StatelessComponent<PaymentMethodProps> = () => (
+ <Container padding="20px" width="100%">
+ <Container marginBottom="10px">
+ <Flex justify="space-between">
+ <Text
+ letterSpacing="1px"
+ fontColor={ColorOption.primaryColor}
+ fontWeight={600}
+ textTransform="uppercase"
+ fontSize="14px"
+ >
+ Payment Method
+ </Text>
+ <Flex>
+ <Circle color={ColorOption.green} diameter={8} />
+ <Container marginLeft="3px">
+ <Text fontColor={ColorOption.darkGrey} fontSize="12px">
+ MetaMask
+ </Text>
+ </Container>
+ </Flex>
+ </Flex>
+ </Container>
+ <PaymentMethodDropdown
+ accountAddress="0xa1b2c3d4e5f6g7h8j9k10"
+ accountEthBalanceInWei={new BigNumber(10500000000000000000)}
+ network={Network.Mainnet}
+ />
+ </Container>
+);
diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx
new file mode 100644
index 000000000..bdce2a49d
--- /dev/null
+++ b/packages/instant/src/components/payment_method_dropdown.tsx
@@ -0,0 +1,44 @@
+import { BigNumber } from '@0x/utils';
+import copy from 'copy-to-clipboard';
+import * as React from 'react';
+
+import { Network } from '../types';
+import { etherscanUtil } from '../util/etherscan';
+import { format } from '../util/format';
+
+import { Dropdown, DropdownItemConfig } from './ui/dropdown';
+
+export interface PaymentMethodDropdownProps {
+ accountAddress: string;
+ accountEthBalanceInWei?: BigNumber;
+ network: Network;
+}
+
+export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdownProps> {
+ public render(): React.ReactNode {
+ const { accountAddress, accountEthBalanceInWei } = this.props;
+ const value = format.ethAddress(accountAddress);
+ const label = format.ethBaseAmount(accountEthBalanceInWei, 4, '') as string;
+ return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />;
+ }
+ private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => {
+ const viewOnEtherscan = {
+ text: 'View on Etherscan',
+ onClick: this._handleEtherscanClick,
+ };
+ const copyAddressToClipboard = {
+ text: 'Copy address to clipboard',
+ onClick: this._handleCopyToClipboardClick,
+ };
+ return [viewOnEtherscan, copyAddressToClipboard];
+ };
+ private readonly _handleEtherscanClick = (): void => {
+ const { accountAddress, network } = this.props;
+ const etherscanUrl = etherscanUtil.getEtherScanEthAddressIfExists(accountAddress, network);
+ window.open(etherscanUrl, '_blank');
+ };
+ private readonly _handleCopyToClipboardClick = (): void => {
+ const { accountAddress } = this.props;
+ copy(accountAddress);
+ };
+}
diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx
index 462199d78..a8d4e391c 100644
--- a/packages/instant/src/components/sliding_error.tsx
+++ b/packages/instant/src/components/sliding_error.tsx
@@ -86,7 +86,7 @@ export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props =
<SlideAnimation
slideInSettings={slideUpSettings}
slideOutSettings={slideOutSettings}
- zIndex={{ sm: zIndex.errorPopUp, default: zIndex.errorPopBehind }}
+ zIndex={{ sm: zIndex.errorPopup, default: zIndex.errorPopBehind }}
animationState={props.animationState}
>
<Error icon={props.icon} message={props.message} />
diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx
index a5d15c401..9d16f9560 100644
--- a/packages/instant/src/components/sliding_panel.tsx
+++ b/packages/instant/src/components/sliding_panel.tsx
@@ -30,7 +30,9 @@ export const Panel: React.StatelessComponent<PanelProps> = ({ title, children, o
<Icon width={12} color={ColorOption.lightGrey} icon="closeX" onClick={onClose} />
</Container>
</Flex>
- <Container marginTop="10px">{children}</Container>
+ <Container marginTop="10px" height="100%">
+ {children}
+ </Container>
</Container>
);
@@ -67,6 +69,7 @@ export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props =
slideInSettings={slideUpSettings}
slideOutSettings={slideDownSettings}
animationState={animationState}
+ height="100%"
>
<Panel {...rest} />
</SlideAnimation>
diff --git a/packages/instant/src/components/ui/circle.tsx b/packages/instant/src/components/ui/circle.tsx
index 26764ec71..4f9f56f12 100644
--- a/packages/instant/src/components/ui/circle.tsx
+++ b/packages/instant/src/components/ui/circle.tsx
@@ -1,24 +1,27 @@
-import { styled } from '../../style/theme';
+import { ColorOption, styled, Theme, withTheme } from '../../style/theme';
export interface CircleProps {
diameter: number;
- fillColor?: string;
+ rawColor?: string;
+ color?: ColorOption;
+ theme: Theme;
}
-export const Circle =
+export const Circle = withTheme(
styled.div <
- CircleProps >
- `
+ CircleProps >
+ `
&& {
width: ${props => props.diameter}px;
height: ${props => props.diameter}px;
- background-color: ${props => props.fillColor};
+ background-color: ${props => (props.rawColor ? props.rawColor : props.theme[props.color || ColorOption.white])};
border-radius: 50%;
}
-`;
+`,
+);
Circle.displayName = 'Circle';
Circle.defaultProps = {
- fillColor: 'white',
+ color: ColorOption.white,
};
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index c42082ed5..8aa5db9e5 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -34,6 +34,7 @@ export interface ContainerProps {
cursor?: string;
overflow?: string;
darkenOnHover?: boolean;
+ boxShadowOnHover?: boolean;
flexGrow?: string | number;
}
@@ -42,7 +43,6 @@ export const Container =
ContainerProps >
`
&& {
- all: initial;
box-sizing: border-box;
${props => cssRuleIfExists(props, 'flex-grow')}
${props => cssRuleIfExists(props, 'position')}
@@ -79,6 +79,7 @@ export const Container =
props.backgroundColor ? darken(0.05, props.theme[props.backgroundColor]) : 'none'
}`
: ''};
+ ${props => (props.boxShadowOnHover ? 'box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)' : '')};
}
}
`;
diff --git a/packages/instant/src/components/ui/dropdown.tsx b/packages/instant/src/components/ui/dropdown.tsx
new file mode 100644
index 000000000..3a23f456d
--- /dev/null
+++ b/packages/instant/src/components/ui/dropdown.tsx
@@ -0,0 +1,134 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { ColorOption, completelyTransparent } from '../../style/theme';
+import { zIndex } from '../../style/z_index';
+
+import { Container } from './container';
+import { Flex } from './flex';
+import { Icon } from './icon';
+import { Overlay } from './overlay';
+import { Text } from './text';
+
+export interface DropdownItemConfig {
+ text: string;
+ onClick?: () => void;
+}
+
+export interface DropdownProps {
+ value: string;
+ label?: string;
+ items: DropdownItemConfig[];
+}
+
+export interface DropdownState {
+ isOpen: boolean;
+}
+
+export class Dropdown extends React.Component<DropdownProps, DropdownState> {
+ public static defaultProps = {
+ items: [],
+ };
+ public state: DropdownState = {
+ isOpen: false,
+ };
+ public render(): React.ReactNode {
+ const { value, label, items } = this.props;
+ const { isOpen } = this.state;
+ const hasItems = !_.isEmpty(items);
+ const borderRadius = isOpen ? '4px 4px 0px 0px' : '4px';
+ return (
+ <React.Fragment>
+ {isOpen && (
+ <Overlay
+ zIndex={zIndex.dropdownItems - 1}
+ backgroundColor={completelyTransparent}
+ onClick={this._closeDropdown}
+ />
+ )}
+ <Container position="relative">
+ <Container
+ cursor={hasItems ? 'pointer' : undefined}
+ onClick={this._handleDropdownClick}
+ hasBoxShadow={isOpen}
+ boxShadowOnHover={true}
+ borderRadius={borderRadius}
+ border="1px solid"
+ borderColor={ColorOption.feintGrey}
+ padding="0.8em"
+ >
+ <Flex justify="space-between">
+ <Text fontSize="16px" fontColor={ColorOption.darkGrey}>
+ {value}
+ </Text>
+ <Container>
+ {label && (
+ <Text fontSize="16px" fontColor={ColorOption.lightGrey}>
+ {label}
+ </Text>
+ )}
+ {hasItems && (
+ <Container marginLeft="5px" display="inline-block" position="relative" bottom="2px">
+ <Icon padding="3px" icon="chevron" width={12} stroke={ColorOption.grey} />
+ </Container>
+ )}
+ </Container>
+ </Flex>
+ </Container>
+ {isOpen && (
+ <Container
+ width="100%"
+ position="absolute"
+ onClick={this._closeDropdown}
+ backgroundColor={ColorOption.white}
+ hasBoxShadow={true}
+ zIndex={zIndex.dropdownItems}
+ >
+ {_.map(items, (item, index) => (
+ <DropdownItem key={item.text} {...item} isLast={index === items.length - 1} />
+ ))}
+ </Container>
+ )}
+ </Container>
+ </React.Fragment>
+ );
+ }
+ private readonly _handleDropdownClick = (): void => {
+ if (_.isEmpty(this.props.items)) {
+ return;
+ }
+ this.setState({
+ isOpen: !this.state.isOpen,
+ });
+ };
+ private readonly _closeDropdown = (): void => {
+ this.setState({
+ isOpen: false,
+ });
+ };
+}
+
+export interface DropdownItemProps extends DropdownItemConfig {
+ text: string;
+ onClick?: () => void;
+ isLast: boolean;
+}
+
+export const DropdownItem: React.StatelessComponent<DropdownItemProps> = ({ text, onClick, isLast }) => (
+ <Container
+ onClick={onClick}
+ cursor="pointer"
+ darkenOnHover={true}
+ backgroundColor={ColorOption.white}
+ padding="0.8em"
+ borderTop="0"
+ border="1px solid"
+ borderRadius={isLast ? '0px 0px 4px 4px' : undefined}
+ width="100%"
+ borderColor={ColorOption.feintGrey}
+ >
+ <Text fontSize="14px" fontColor={ColorOption.darkGrey}>
+ {text}
+ </Text>
+ </Container>
+);
diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx
index 5b00138b8..274c46b9e 100644
--- a/packages/instant/src/components/ui/flex.tsx
+++ b/packages/instant/src/components/ui/flex.tsx
@@ -19,7 +19,6 @@ export const Flex =
FlexProps >
`
&& {
- all: initial;
display: ${props => (props.inline ? 'inline-flex' : 'flex')};
flex-direction: ${props => props.direction};
flex-wrap: ${props => props.flexWrap};
diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx
index 2679dad1a..a88fa87dd 100644
--- a/packages/instant/src/components/ui/icon.tsx
+++ b/packages/instant/src/components/ui/icon.tsx
@@ -9,7 +9,6 @@ interface IconInfo {
path: string;
fillRule?: svgRule;
clipRule?: svgRule;
- stroke?: string;
strokeOpacity?: number;
strokeWidth?: number;
strokeLinecap?: 'butt' | 'round' | 'square' | 'inherit';
@@ -47,7 +46,6 @@ const ICONS: IconInfoMapping = {
chevron: {
viewBox: '0 0 12 7',
path: 'M11 1L6 6L1 1',
- stroke: 'white',
strokeOpacity: 0.5,
strokeWidth: 1.5,
strokeLinecap: 'round',
@@ -67,6 +65,7 @@ export interface IconProps {
width: number;
height?: number;
color?: ColorOption;
+ stroke?: ColorOption;
icon: keyof IconInfoMapping;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
padding?: string;
@@ -75,6 +74,7 @@ export interface IconProps {
const PlainIcon: React.StatelessComponent<IconProps> = props => {
const iconInfo = ICONS[props.icon];
const colorValue = _.isUndefined(props.color) ? undefined : props.theme[props.color];
+ const strokeValue = _.isUndefined(props.stroke) ? undefined : props.theme[props.stroke];
return (
<div onClick={props.onClick} className={props.className}>
<svg
@@ -89,7 +89,7 @@ const PlainIcon: React.StatelessComponent<IconProps> = props => {
fill={colorValue}
fillRule={iconInfo.fillRule || 'nonzero'}
clipRule={iconInfo.clipRule || 'nonzero'}
- stroke={iconInfo.stroke}
+ stroke={strokeValue}
strokeOpacity={iconInfo.strokeOpacity}
strokeWidth={iconInfo.strokeWidth}
strokeLinecap={iconInfo.strokeLinecap}
@@ -102,7 +102,8 @@ const PlainIcon: React.StatelessComponent<IconProps> = props => {
export const Icon = withTheme(styled(PlainIcon)`
&& {
- cursor: ${props => (!_.isUndefined(props.onClick) ? 'pointer' : 'default')};
+ display: inline-block;
+ ${props => (!_.isUndefined(props.onClick) ? 'cursor: pointer' : '')};
transition: opacity 0.5s ease;
padding: ${props => props.padding};
opacity: ${props => (!_.isUndefined(props.onClick) ? 0.7 : 1)};
diff --git a/packages/instant/src/components/ui/overlay.tsx b/packages/instant/src/components/ui/overlay.tsx
index 8c9572615..c5f55f9c0 100644
--- a/packages/instant/src/components/ui/overlay.tsx
+++ b/packages/instant/src/components/ui/overlay.tsx
@@ -1,29 +1,17 @@
import * as _ from 'lodash';
-import * as React from 'react';
-import { ColorOption, overlayBlack, styled } from '../../style/theme';
-
-import { Container } from './container';
-import { Flex } from './flex';
-import { Icon } from './icon';
+import { overlayBlack, styled } from '../../style/theme';
+import { zIndex } from '../../style/z_index';
export interface OverlayProps {
- className?: string;
- onClose?: () => void;
zIndex?: number;
+ backgroundColor?: string;
}
-const PlainOverlay: React.StatelessComponent<OverlayProps> = ({ children, className, onClose }) => (
- <Flex height="100vh" className={className}>
- <Container position="absolute" top="0px" right="0px" display={{ default: 'initial', sm: 'none' }}>
- <Icon height={18} width={18} color={ColorOption.white} icon="closeX" onClick={onClose} padding="2em 2em" />
- </Container>
- <Container width={{ default: 'auto', sm: '100%' }} height={{ default: 'auto', sm: '100%' }}>
- {children}
- </Container>
- </Flex>
-);
-export const Overlay = styled(PlainOverlay)`
+export const Overlay =
+ styled.div <
+ OverlayProps >
+ `
&& {
position: fixed;
top: 0;
@@ -31,12 +19,13 @@ export const Overlay = styled(PlainOverlay)`
bottom: 0;
left: 0;
z-index: ${props => props.zIndex}
- background-color: ${overlayBlack};
+ background-color: ${props => props.backgroundColor};
}
`;
Overlay.defaultProps = {
- zIndex: 100,
+ zIndex: zIndex.overlayDefault,
+ backgroundColor: overlayBlack,
};
Overlay.displayName = 'Overlay';
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index c6a76ff18..4fe429d25 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -28,7 +28,6 @@ export const Text =
TextProps >
`
&& {
- all: initial;
font-family: 'Inter UI', sans-serif;
font-style: ${props => props.fontStyle};
font-weight: ${props => props.fontWeight};
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index d2216b54f..5748e064e 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -3,11 +3,9 @@ import * as React from 'react';
import { AvailableERC20TokenSelector } from '../containers/available_erc20_token_selector';
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
import { LatestError } from '../containers/latest_error';
+import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress';
import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_buy_order_state_buttons';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
-
-import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress';
-
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx
index 3461600e1..10438ab7a 100644
--- a/packages/instant/src/components/zero_ex_instant_overlay.tsx
+++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx
@@ -1,5 +1,10 @@
import * as React from 'react';
+import { ColorOption } from '../style/theme';
+
+import { Container } from './ui/container';
+import { Flex } from './ui/flex';
+import { Icon } from './ui/icon';
import { Overlay } from './ui/overlay';
import { ZeroExInstantContainer } from './zero_ex_instant_container';
import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider';
@@ -13,8 +18,22 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay
const { onClose, zIndex, ...rest } = props;
return (
<ZeroExInstantProvider {...rest}>
- <Overlay onClose={onClose} zIndex={zIndex}>
- <ZeroExInstantContainer />
+ <Overlay zIndex={zIndex}>
+ <Flex height="100vh">
+ <Container position="absolute" top="0px" right="0px" display={{ default: 'initial', sm: 'none' }}>
+ <Icon
+ height={18}
+ width={18}
+ color={ColorOption.white}
+ icon="closeX"
+ onClick={onClose}
+ padding="2em 2em"
+ />
+ </Container>
+ <Container width={{ default: 'auto', sm: '100%' }} height={{ default: 'auto', sm: '100%' }}>
+ <ZeroExInstantContainer />
+ </Container>
+ </Flex>
</Overlay>
</ZeroExInstantProvider>
);
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index 58e78c522..cceb44377 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -93,6 +93,8 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store);
}
// tslint:disable-next-line:no-floating-promises
+ asyncData.fetchAccountInfoAndDispatchToStore(this._store);
+ // tslint:disable-next-line:no-floating-promises
asyncData.fetchCurrentBuyQuoteAndDispatchToStore(this._store);
// warm up the gas price estimator cache just in case we can't
// grab the gas price estimate when submitting the transaction
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index 34548f26f..b5c4f96e4 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -1,6 +1,6 @@
import { BigNumber } from '@0x/utils';
-import { Network } from './types';
+import { AccountNotReady, AccountState, Network } from './types';
export const BIG_NUMBER_ZERO = new BigNumber(0);
export const ETH_DECIMALS = 18;
@@ -22,3 +22,15 @@ export const ETHEREUM_NODE_URL_BY_NETWORK = {
[Network.Kovan]: 'https://kovan.infura.io/',
};
export const BLOCK_POLLING_INTERVAL_MS = 10000; // 10s
+export const NO_ACCOUNT: AccountNotReady = {
+ state: AccountState.None,
+};
+export const LOADING_ACCOUNT: AccountNotReady = {
+ state: AccountState.Loading,
+};
+export const LOCKED_ACCOUNT: AccountNotReady = {
+ state: AccountState.Locked,
+};
+export const ERROR_ACCOUNT: AccountNotReady = {
+ state: AccountState.Error,
+};
diff --git a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
index c3a5e88b9..610335243 100644
--- a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
+++ b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
@@ -1,4 +1,6 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
@@ -7,14 +9,17 @@ import { Dispatch } from 'redux';
import { BuyOrderStateButtons } from '../components/buy_order_state_buttons';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
-import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
+import { AccountState, AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
import { errorFlasher } from '../util/error_flasher';
import { etherscanUtil } from '../util/etherscan';
interface ConnectedState {
+ accountAddress?: string;
+ accountEthBalanceInWei?: BigNumber;
buyQuote?: BuyQuote;
buyOrderProcessingState: OrderProcessState;
assetBuyer: AssetBuyer;
+ web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
onViewTransaction: () => void;
}
@@ -31,9 +36,16 @@ interface ConnectedDispatch {
export interface SelectedAssetBuyOrderStateButtons {}
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButtons): ConnectedState => {
const assetBuyer = state.providerState.assetBuyer;
+ const web3Wrapper = state.providerState.web3Wrapper;
+ const account = state.providerState.account;
+ const accountAddress = account.state === AccountState.Ready ? account.address : undefined;
+ const accountEthBalanceInWei = account.state === AccountState.Ready ? account.ethBalanceInWei : undefined;
return {
+ accountAddress,
+ accountEthBalanceInWei,
buyOrderProcessingState: state.buyOrderState.processState,
assetBuyer,
+ web3Wrapper,
buyQuote: state.latestBuyQuote,
affiliateInfo: state.affiliateInfo,
onViewTransaction: () => {
diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts
index c41c5054b..fc89e3d0e 100644
--- a/packages/instant/src/redux/actions.ts
+++ b/packages/instant/src/redux/actions.ts
@@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
-import { ActionsUnion, Asset } from '../types';
+import { ActionsUnion, AddressAndEthBalanceInWei, Asset } from '../types';
export interface PlainAction<T extends string> {
type: T;
@@ -21,6 +21,11 @@ function createAction<T extends string, P>(type: T, data?: P): PlainAction<T> |
}
export enum ActionTypes {
+ SET_ACCOUNT_STATE_LOADING = 'SET_ACCOUNT_STATE_LOADING',
+ SET_ACCOUNT_STATE_LOCKED = 'SET_ACCOUNT_STATE_LOCKED',
+ SET_ACCOUNT_STATE_ERROR = 'SET_ACCOUNT_STATE_ERROR',
+ 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',
SET_BUY_ORDER_STATE_NONE = 'SET_BUY_ORDER_STATE_NONE',
@@ -40,6 +45,12 @@ export enum ActionTypes {
}
export const actions = {
+ setAccountStateLoading: () => createAction(ActionTypes.SET_ACCOUNT_STATE_LOADING),
+ setAccountStateLocked: () => createAction(ActionTypes.SET_ACCOUNT_STATE_LOCKED),
+ setAccountStateError: () => createAction(ActionTypes.SET_ACCOUNT_STATE_ERROR),
+ setAccountStateReady: (address: string) => createAction(ActionTypes.SET_ACCOUNT_STATE_READY, address),
+ 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),
setBuyOrderStateNone: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_NONE),
diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts
index 839a90778..a8f632009 100644
--- a/packages/instant/src/redux/async_data.ts
+++ b/packages/instant/src/redux/async_data.ts
@@ -2,7 +2,7 @@ import { AssetProxyId } from '@0x/types';
import * as _ from 'lodash';
import { BIG_NUMBER_ZERO } from '../constants';
-import { ERC20Asset } from '../types';
+import { AccountState, ERC20Asset } from '../types';
import { assetUtils } from '../util/asset';
import { buyQuoteUpdater } from '../util/buy_quote_updater';
import { coinbaseApi } from '../util/coinbase_api';
@@ -36,6 +36,44 @@ export const asyncData = {
store.dispatch(actions.setAvailableAssets([]));
}
},
+ fetchAccountInfoAndDispatchToStore: async (store: Store) => {
+ const { providerState } = store.getState();
+ const web3Wrapper = providerState.web3Wrapper;
+ if (providerState.account.state !== AccountState.Loading) {
+ store.dispatch(actions.setAccountStateLoading());
+ }
+ let availableAddresses: string[];
+ try {
+ availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
+ } catch (e) {
+ store.dispatch(actions.setAccountStateError());
+ return;
+ }
+ if (!_.isEmpty(availableAddresses)) {
+ const activeAddress = availableAddresses[0];
+ store.dispatch(actions.setAccountStateReady(activeAddress));
+ // tslint:disable-next-line:no-floating-promises
+ asyncData.fetchAccountBalanceAndDispatchToStore(store);
+ } else {
+ store.dispatch(actions.setAccountStateLocked());
+ }
+ },
+ fetchAccountBalanceAndDispatchToStore: async (store: Store) => {
+ const { providerState } = store.getState();
+ const web3Wrapper = providerState.web3Wrapper;
+ const account = providerState.account;
+ if (account.state !== AccountState.Ready) {
+ return;
+ }
+ try {
+ const address = account.address;
+ const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(address);
+ store.dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei }));
+ } catch (e) {
+ // leave balance as is
+ return;
+ }
+ },
fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store) => {
const { providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState();
const assetBuyer = providerState.assetBuyer;
diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts
index 4a939839a..a5a1b6f7d 100644
--- a/packages/instant/src/redux/reducer.ts
+++ b/packages/instant/src/redux/reducer.ts
@@ -4,8 +4,12 @@ import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
+import { ERROR_ACCOUNT, LOADING_ACCOUNT, LOCKED_ACCOUNT } from '../constants';
import { assetMetaDataMap } from '../data/asset_meta_data_map';
import {
+ Account,
+ AccountReady,
+ AccountState,
AffiliateInfo,
Asset,
AssetMetaData,
@@ -57,6 +61,32 @@ export const DEFAULT_STATE: DefaultState = {
export const createReducer = (initialState: State) => {
const reducer = (state: State = initialState, action: Action): State => {
switch (action.type) {
+ case ActionTypes.SET_ACCOUNT_STATE_LOADING:
+ return reduceStateWithAccount(state, LOADING_ACCOUNT);
+ case ActionTypes.SET_ACCOUNT_STATE_LOCKED:
+ return reduceStateWithAccount(state, LOCKED_ACCOUNT);
+ case ActionTypes.SET_ACCOUNT_STATE_ERROR:
+ return reduceStateWithAccount(state, ERROR_ACCOUNT);
+ case ActionTypes.SET_ACCOUNT_STATE_READY: {
+ const account: AccountReady = {
+ state: AccountState.Ready,
+ address: action.data,
+ };
+ return reduceStateWithAccount(state, account);
+ }
+ case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE: {
+ const { address, ethBalanceInWei } = action.data;
+ const currentAccount = state.providerState.account;
+ if (currentAccount.state !== AccountState.Ready || currentAccount.address !== address) {
+ return state;
+ } else {
+ const newAccount: AccountReady = {
+ ...currentAccount,
+ ethBalanceInWei,
+ };
+ return reduceStateWithAccount(state, newAccount);
+ }
+ }
case ActionTypes.UPDATE_ETH_USD_PRICE:
return {
...state,
@@ -80,7 +110,6 @@ export const createReducer = (initialState: State) => {
} else {
return state;
}
-
case ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING:
return {
...state,
@@ -191,6 +220,18 @@ export const createReducer = (initialState: State) => {
return reducer;
};
+const reduceStateWithAccount = (state: State, account: Account) => {
+ const oldProviderState = state.providerState;
+ const newProviderState: ProviderState = {
+ ...oldProviderState,
+ account,
+ };
+ return {
+ ...state,
+ providerState: newProviderState,
+ };
+};
+
const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => {
const selectedAssetIfExists = state.selectedAsset;
const selectedAssetAmountIfExists = state.selectedAssetAmount;
diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts
index 8dada2d28..2653c38f7 100644
--- a/packages/instant/src/style/theme.ts
+++ b/packages/instant/src/style/theme.ts
@@ -15,6 +15,8 @@ export enum ColorOption {
white = 'white',
lightOrange = 'lightOrange',
darkOrange = 'darkOrange',
+ green = 'green',
+ red = 'red',
}
export const theme: Theme = {
@@ -28,9 +30,12 @@ export const theme: Theme = {
white: 'white',
lightOrange: '#F9F2ED',
darkOrange: '#F2994C',
+ green: '#3CB34F',
+ red: '#D00000',
};
export const transparentWhite = 'rgba(255,255,255,0.3)';
export const overlayBlack = 'rgba(0, 0, 0, 0.6)';
+export const completelyTransparent = 'rga(0, 0, 0, 0)';
export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider };
diff --git a/packages/instant/src/style/z_index.ts b/packages/instant/src/style/z_index.ts
index 03623f044..bd034182e 100644
--- a/packages/instant/src/style/z_index.ts
+++ b/packages/instant/src/style/z_index.ts
@@ -1,6 +1,8 @@
export const zIndex = {
- errorPopBehind: 1,
- mainContainer: 2,
- panel: 3,
- errorPopUp: 4,
+ errorPopBehind: 10,
+ mainContainer: 20,
+ dropdownItems: 30,
+ panel: 40,
+ errorPopup: 50,
+ overlayDefault: 100,
};
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index d65f70008..20ad2ed95 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -120,3 +120,8 @@ export interface AccountNotReady {
export type Account = AccountReady | AccountNotReady;
export type OrderSource = string | SignedOrder[];
+
+export interface AddressAndEthBalanceInWei {
+ address: string;
+ ethBalanceInWei: BigNumber;
+}
diff --git a/packages/instant/src/util/balance.ts b/packages/instant/src/util/balance.ts
deleted file mode 100644
index f2271495b..000000000
--- a/packages/instant/src/util/balance.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { BuyQuote } from '@0x/asset-buyer';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import * as _ from 'lodash';
-
-export const balanceUtil = {
- hasSufficientEth: async (takerAddress: string | undefined, buyQuote: BuyQuote, web3Wrapper: Web3Wrapper) => {
- if (_.isUndefined(takerAddress)) {
- return false;
- }
- const balanceWei = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- return balanceWei.gte(buyQuote.worstCaseQuoteInfo.totalEthAmount);
- },
-};
diff --git a/packages/instant/src/util/etherscan.ts b/packages/instant/src/util/etherscan.ts
index cfc2578a3..4d62c4d9f 100644
--- a/packages/instant/src/util/etherscan.ts
+++ b/packages/instant/src/util/etherscan.ts
@@ -21,4 +21,11 @@ export const etherscanUtil = {
}
return `https://${prefix}etherscan.io/tx/${txHash}`;
},
+ getEtherScanEthAddressIfExists: (ethAddress: string, networkId: number) => {
+ const prefix = etherscanPrefix(networkId);
+ if (_.isUndefined(prefix)) {
+ return;
+ }
+ return `https://${prefix}etherscan.io/address/${ethAddress}`;
+ },
};
diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts
index 4a48dec9d..44661d697 100644
--- a/packages/instant/src/util/format.ts
+++ b/packages/instant/src/util/format.ts
@@ -50,4 +50,7 @@ export const format = {
}
return `$${ethUnitAmount.mul(ethUsdPrice).toFixed(decimalPlaces)}`;
},
+ ethAddress: (address: string): string => {
+ return `0x${address.slice(2, 7)}…${address.slice(-5)}`;
+ },
};
diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts
index 18b188d89..3281f6bfb 100644
--- a/packages/instant/src/util/provider_state_factory.ts
+++ b/packages/instant/src/util/provider_state_factory.ts
@@ -2,18 +2,12 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
-import { AccountNotReady, AccountState, Maybe, Network, OrderSource, ProviderState } from '../types';
+import { LOADING_ACCOUNT, NO_ACCOUNT } from '../constants';
+import { Maybe, Network, OrderSource, ProviderState } from '../types';
import { assetBuyerFactory } from './asset_buyer_factory';
import { providerFactory } from './provider_factory';
-const LOADING_ACCOUNT: AccountNotReady = {
- state: AccountState.Loading,
-};
-const NO_ACCOUNT: AccountNotReady = {
- state: AccountState.None,
-};
-
export const providerStateFactory = {
getInitialProviderState: (orderSource: OrderSource, network: Network, provider?: Provider): ProviderState => {
if (!_.isUndefined(provider)) {