aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts')
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx9
-rw-r--r--packages/website/ts/components/onboarding/add_eth_onboarding_step.tsx18
-rw-r--r--packages/website/ts/components/onboarding/congrats_onboarding_step.tsx15
-rw-r--r--packages/website/ts/components/onboarding/install_wallet_onboarding_step.tsx18
-rw-r--r--packages/website/ts/components/onboarding/intro_onboarding_step.tsx23
-rw-r--r--packages/website/ts/components/onboarding/onboarding_flow.tsx12
-rw-r--r--packages/website/ts/components/onboarding/onboarding_tooltip.tsx103
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx135
-rw-r--r--packages/website/ts/components/onboarding/set_allowances_onboarding_step.tsx27
-rw-r--r--packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx16
-rw-r--r--packages/website/ts/components/onboarding/wrap_eth_onboarding_step.tsx73
-rw-r--r--packages/website/ts/components/portal/portal.tsx30
-rw-r--r--packages/website/ts/components/token_balances.tsx5
-rw-r--r--packages/website/ts/components/ui/button.tsx17
-rw-r--r--packages/website/ts/components/ui/container.tsx7
-rw-r--r--packages/website/ts/components/ui/icon_button.tsx9
-rw-r--r--packages/website/ts/components/ui/pointer.tsx67
-rw-r--r--packages/website/ts/components/ui/text.tsx31
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx13
-rw-r--r--packages/website/ts/containers/inputs/allowance_toggle.ts41
-rw-r--r--packages/website/ts/containers/portal_onboarding_flow.ts3
-rw-r--r--packages/website/ts/utils/constants.ts1
-rw-r--r--packages/website/ts/utils/utils.ts21
23 files changed, 592 insertions, 102 deletions
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index 09791f125..0dd2a5aa5 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -1,5 +1,6 @@
import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
+import * as _ from 'lodash';
import Toggle from 'material-ui/Toggle';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
@@ -16,11 +17,11 @@ interface AllowanceToggleProps {
networkId: number;
blockchain: Blockchain;
dispatcher: Dispatcher;
- onErrorOccurred: (errType: BalanceErrs) => void;
token: Token;
tokenState: TokenState;
userAddress: string;
- isDisabled: boolean;
+ isDisabled?: boolean;
+ onErrorOccurred?: (errType: BalanceErrs) => void;
refetchTokenStateAsync: () => Promise<void>;
}
@@ -55,6 +56,10 @@ const styles: Styles = {
};
export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> {
+ public static defaultProps = {
+ onErrorOccurred: _.noop,
+ isDisabled: false,
+ };
constructor(props: AllowanceToggleProps) {
super(props);
this.state = {
diff --git a/packages/website/ts/components/onboarding/add_eth_onboarding_step.tsx b/packages/website/ts/components/onboarding/add_eth_onboarding_step.tsx
new file mode 100644
index 000000000..31ce99d31
--- /dev/null
+++ b/packages/website/ts/components/onboarding/add_eth_onboarding_step.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface AddEthOnboardingStepProps {}
+
+export const AddEthOnboardingStep: React.StatelessComponent<AddEthOnboardingStepProps> = () => (
+ <div className="flex items-center flex-column">
+ <Text> Before you begin you will need to send some ETH to your metamask wallet.</Text>
+ <Container marginTop="15px" marginBottom="15px">
+ <img src="/images/ether_alt.svg" height="50px" width="50px" />
+ </Container>
+ <Text>
+ Click on the <img src="/images/metamask_icon.png" height="20px" width="20px" /> metamask extension in your
+ browser and click either <b>BUY</b> or <b>DEPOSIT</b>.
+ </Text>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/congrats_onboarding_step.tsx b/packages/website/ts/components/onboarding/congrats_onboarding_step.tsx
new file mode 100644
index 000000000..3a8db8c36
--- /dev/null
+++ b/packages/website/ts/components/onboarding/congrats_onboarding_step.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface CongratsOnboardingStepProps {}
+
+export const CongratsOnboardingStep: React.StatelessComponent<CongratsOnboardingStepProps> = () => (
+ <div className="flex items-center flex-column">
+ <Text>Your wallet is now set up for trading. Use it on any relayer in the 0x ecosystem.</Text>
+ <Container marginTop="25px" marginBottom="15px" className="flex justify-center">
+ <img src="/images/zrx_ecosystem.svg" height="150px" />
+ </Container>
+ <Text>No need to log in. Each relayer automatically detects and connects to your metamask wallet.</Text>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/install_wallet_onboarding_step.tsx b/packages/website/ts/components/onboarding/install_wallet_onboarding_step.tsx
new file mode 100644
index 000000000..a54496186
--- /dev/null
+++ b/packages/website/ts/components/onboarding/install_wallet_onboarding_step.tsx
@@ -0,0 +1,18 @@
+import { colors } from '@0xproject/react-shared';
+import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface InstallWalletOnboardingStepProps {}
+
+export const InstallWalletOnboardingStep: React.StatelessComponent<InstallWalletOnboardingStepProps> = () => (
+ <div className="flex items-center flex-column">
+ <Container marginTop="15px" marginBottom="15px">
+ <ActionAccountBalanceWallet style={{ width: '30px', height: '30px' }} color={colors.orange} />
+ </Container>
+ <Text>
+ Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps.
+ </Text>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/intro_onboarding_step.tsx b/packages/website/ts/components/onboarding/intro_onboarding_step.tsx
new file mode 100644
index 000000000..548839218
--- /dev/null
+++ b/packages/website/ts/components/onboarding/intro_onboarding_step.tsx
@@ -0,0 +1,23 @@
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface IntroOnboardingStepProps {}
+
+export const IntroOnboardingStep: React.StatelessComponent<IntroOnboardingStepProps> = () => (
+ <div className="flex items-center flex-column">
+ <Text>
+ In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps.
+ </Text>
+ <Container width="100%" marginTop="25px" marginBottom="15px" className="flex justify-around">
+ <div className="flex flex-column items-center">
+ <img src="/images/eth_token.svg" height="50px" width="50x" />
+ <Text> Wrap ETH </Text>
+ </div>
+ <div className="flex flex-column items-center">
+ <img src="/images/fake_toggle.svg" height="50px" width="50px" />
+ <Text> Unlock tokens </Text>
+ </div>
+ </Container>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx
index 9879cd387..34aeace82 100644
--- a/packages/website/ts/components/onboarding/onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx
@@ -10,9 +10,10 @@ export interface Step {
title?: string;
content: React.ReactNode;
placement?: Placement;
- hideBackButton?: boolean;
- hideNextButton?: boolean;
+ shouldHideBackButton?: boolean;
+ shouldHideNextButton?: boolean;
continueButtonDisplay?: ContinueButtonDisplay;
+ continueButtonText?: string;
}
export interface OnboardingFlowProps {
@@ -54,17 +55,18 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
const step = steps[stepIndex];
const isLastStep = steps.length - 1 === stepIndex;
return (
- <Container marginLeft="15px">
+ <Container marginLeft="30px">
<OnboardingTooltip
title={step.title}
content={step.content}
isLastStep={isLastStep}
- hideBackButton={step.hideBackButton}
- hideNextButton={step.hideNextButton}
+ shouldHideBackButton={step.shouldHideBackButton}
+ shouldHideNextButton={step.shouldHideNextButton}
onClose={this.props.onClose}
onClickNext={this._goToNextStep.bind(this)}
onClickBack={this._goToPrevStep.bind(this)}
continueButtonDisplay={step.continueButtonDisplay}
+ continueButtonText={step.continueButtonText}
/>
</Container>
);
diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx
index 155c70c5f..45851b4de 100644
--- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx
+++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx
@@ -1,7 +1,12 @@
+import { colors } from '@0xproject/react-shared';
import * as React from 'react';
+import { Button } from 'ts/components/ui/button';
import { Container } from 'ts/components/ui/container';
+import { IconButton } from 'ts/components/ui/icon_button';
import { Island } from 'ts/components/ui/island';
+import { Pointer, PointerDirection } from 'ts/components/ui/pointer';
+import { Text, Title } from 'ts/components/ui/text';
export type ContinueButtonDisplay = 'enabled' | 'disabled';
@@ -13,43 +18,73 @@ export interface OnboardingTooltipProps {
onClickNext: () => void;
onClickBack: () => void;
continueButtonDisplay?: ContinueButtonDisplay;
- hideBackButton?: boolean;
- hideNextButton?: boolean;
+ shouldHideBackButton?: boolean;
+ shouldHideNextButton?: boolean;
+ pointerDirection?: PointerDirection;
+ continueButtonText?: string;
+ className?: string;
}
-// TODO: Make this more general button.
-export interface ContinueButtonProps {
- display: ContinueButtonDisplay;
- children?: string;
- onClick: () => void;
-}
+export const OnboardingTooltip: React.StatelessComponent<OnboardingTooltipProps> = ({
+ title,
+ content,
+ continueButtonDisplay,
+ continueButtonText,
+ onClickNext,
+ onClickBack,
+ onClose,
+ shouldHideBackButton,
+ shouldHideNextButton,
+ pointerDirection,
+ className,
+}) => (
+ <Pointer className={className} direction={pointerDirection}>
+ <Island>
+ <Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px">
+ <div className="flex flex-column">
+ <div className="flex justify-between">
+ <Title>{title}</Title>
+ <Container position="relative" bottom="20px" left="15px">
+ <IconButton color={colors.grey} iconName="zmdi-close" onClick={onClose}>
+ Close
+ </IconButton>
+ </Container>
+ </div>
+ <Container marginBottom="15px">
+ <Text>{content}</Text>
+ </Container>
+ {continueButtonDisplay && (
+ <Button
+ isDisabled={continueButtonDisplay === 'disabled'}
+ onClick={onClickNext}
+ fontColor={colors.white}
+ fontSize="15px"
+ backgroundColor={colors.mediumBlue}
+ >
+ {continueButtonText}
+ </Button>
+ )}
+ <Container className="flex justify-between" marginTop="15px">
+ {!shouldHideBackButton && (
+ <Text fontColor={colors.grey} onClick={onClickBack}>
+ Back
+ </Text>
+ )}
+ {!shouldHideNextButton && (
+ <Text fontColor={colors.grey} onClick={onClickNext}>
+ Skip
+ </Text>
+ )}
+ </Container>
+ </div>
+ </Container>
+ </Island>
+ </Pointer>
+);
-export const ContinueButton: React.StatelessComponent<ContinueButtonProps> = (props: ContinueButtonProps) => {
- const isDisabled = props.display === 'disabled';
- return (
- <button disabled={isDisabled} onClick={isDisabled ? undefined : props.onClick}>
- {props.children}
- </button>
- );
+OnboardingTooltip.defaultProps = {
+ pointerDirection: 'left',
+ continueButtonText: 'Continue',
};
-export const OnboardingTooltip: React.StatelessComponent<OnboardingTooltipProps> = (props: OnboardingTooltipProps) => (
- <Island>
- <Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px">
- <div className="flex flex-column">
- {props.title}
- {props.content}
- {props.continueButtonDisplay && (
- <ContinueButton onClick={props.onClickNext} display={props.continueButtonDisplay}>
- Continue
- </ContinueButton>
- )}
- {!props.hideBackButton && <button onClick={props.onClickBack}>Back</button>}
- {!props.hideNextButton && <button onClick={props.onClickNext}>Skip</button>}
- <button onClick={props.onClose}>Close</button>
- </div>
- </Container>
- </Island>
-);
-
OnboardingTooltip.displayName = 'OnboardingTooltip';
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
index bf52684d7..4283022e2 100644
--- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
@@ -2,11 +2,21 @@ import * as _ from 'lodash';
import * as React from 'react';
import { BigNumber } from '@0xproject/utils';
+import { Blockchain } from 'ts/blockchain';
+import { AddEthOnboardingStep } from 'ts/components/onboarding/add_eth_onboarding_step';
+import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step';
+import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_onboarding_step';
+import { IntroOnboardingStep } from 'ts/components/onboarding/intro_onboarding_step';
import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow';
-import { ProviderType, TokenByAddress, TokenStateByAddress } from 'ts/types';
+import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowances_onboarding_step';
+import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step';
+import { WrapEthOnboardingStep } from 'ts/components/onboarding/wrap_eth_onboarding_step';
+import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle';
+import { ProviderType, Token, TokenByAddress, TokenStateByAddress } from 'ts/types';
import { utils } from 'ts/utils/utils';
export interface PortalOnboardingFlowProps {
+ blockchain: Blockchain;
stepIndex: number;
isRunning: boolean;
userAddress: string;
@@ -19,6 +29,7 @@ export interface PortalOnboardingFlowProps {
trackedTokenStateByAddress: TokenStateByAddress;
updateIsRunning: (isRunning: boolean) => void;
updateOnboardingStep: (stepIndex: number) => void;
+ refetchTokenStateAsync: (tokenAddress: string) => Promise<void>;
}
export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowProps> {
@@ -39,73 +50,117 @@ export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowPr
/>
);
}
-
private _getSteps(): Step[] {
const steps: Step[] = [
{
target: '.wallet',
- content:
- 'Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps',
+ title: '0x Ecosystem Setup',
+ content: <InstallWalletOnboardingStep />,
placement: 'right',
- hideBackButton: true,
- hideNextButton: true,
+ shouldHideBackButton: true,
+ shouldHideNextButton: true,
},
{
target: '.wallet',
- content: 'Unlock your metamask extension to begin',
+ title: '0x Ecosystem Setup',
+ content: <UnlockWalletOnboardingStep />,
placement: 'right',
- hideBackButton: true,
- hideNextButton: true,
+ shouldHideBackButton: true,
+ shouldHideNextButton: true,
},
{
target: '.wallet',
- content:
- 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps',
+ title: '0x Ecosystem Account Setup',
+ content: <IntroOnboardingStep />,
placement: 'right',
- hideBackButton: true,
+ shouldHideBackButton: true,
continueButtonDisplay: 'enabled',
},
{
target: '.eth-row',
- content: 'Before you begin you will need to send some ETH to your metamask wallet',
+ title: 'Add ETH',
+ content: <AddEthOnboardingStep />,
placement: 'right',
continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled',
},
{
target: '.weth-row',
- content: 'You need to convert some of your ETH into tradeable Wrapped ETH (WETH)',
+ title: 'Step 1/2',
+ content: (
+ <WrapEthOnboardingStep
+ formattedEthBalanceIfExists={
+ this._userHasVisibleWeth() ? this._getFormattedWethBalance() : undefined
+ }
+ />
+ ),
+ placement: 'right',
+ continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : undefined,
+ },
+ {
+ target: '.weth-row',
+ title: 'Step 2/2',
+ content: (
+ <SetAllowancesOnboardingStep
+ zrxAllowanceToggle={this._renderZrxAllowanceToggle()}
+ ethAllowanceToggle={this._renderEthAllowanceToggle()}
+ />
+ ),
+ placement: 'right',
+ continueButtonDisplay: this._userHasAllowancesForWethAndZrx() ? 'enabled' : 'disabled',
+ },
+ {
+ target: '.wallet',
+ title: '🎉 Congrats! The ecosystem awaits.',
+ content: <CongratsOnboardingStep />,
placement: 'right',
- continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
+ continueButtonDisplay: 'enabled',
+ shouldHideNextButton: true,
+ continueButtonText: 'Enter the 0x Ecosystem',
},
];
return steps;
}
-
private _isAddressAvailable(): boolean {
return !_.isEmpty(this.props.userAddress);
}
-
private _userHasVisibleEth(): boolean {
return this.props.userEtherBalanceInWei > new BigNumber(0);
}
-
- private _userHasVisibleWeth(): boolean {
+ private _getWethBalance(): BigNumber {
const ethToken = utils.getEthToken(this.props.tokenByAddress);
if (!ethToken) {
- return false;
+ return new BigNumber(0);
}
- const wethTokenState = this.props.trackedTokenStateByAddress[ethToken.address];
- return wethTokenState.balance > new BigNumber(0);
+ const ethTokenState = this.props.trackedTokenStateByAddress[ethToken.address];
+ return ethTokenState.balance;
+ }
+ private _getFormattedWethBalance(): string {
+ const ethToken = utils.getEthToken(this.props.tokenByAddress);
+ const ethTokenState = this.props.trackedTokenStateByAddress[ethToken.address];
+ return utils.getFormattedAmountFromToken(ethToken, ethTokenState);
+ }
+ private _userHasVisibleWeth(): boolean {
+ return this._getWethBalance() > new BigNumber(0);
+ }
+ private _userHasAllowancesForWethAndZrx(): boolean {
+ const ethToken = utils.getEthToken(this.props.tokenByAddress);
+ const zrxToken = utils.getZrxToken(this.props.tokenByAddress);
+ if (ethToken && zrxToken) {
+ const ethTokenAllowance = this.props.trackedTokenStateByAddress[ethToken.address].allowance;
+ const zrxTokenAllowance = this.props.trackedTokenStateByAddress[zrxToken.address].allowance;
+ return ethTokenAllowance > new BigNumber(0) && zrxTokenAllowance > new BigNumber(0);
+ }
+ return false;
}
-
private _overrideOnboardingStateIfShould(): void {
this._autoStartOnboardingIfShould();
this._adjustStepIfShould();
}
private _adjustStepIfShould(): void {
+ const stepIndex = this.props.stepIndex;
if (this._isAddressAvailable()) {
- if (this.props.stepIndex < 2) {
+ if (stepIndex < 2) {
this.props.updateOnboardingStep(2);
}
return;
@@ -115,14 +170,42 @@ export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowPr
this.props.injectedProviderName,
);
if (isExternallyInjected) {
- this.props.updateOnboardingStep(1);
+ if (stepIndex !== 1) {
+ this.props.updateOnboardingStep(1);
+ }
return;
}
- this.props.updateOnboardingStep(0);
+ if (stepIndex !== 0) {
+ this.props.updateOnboardingStep(0);
+ }
}
private _autoStartOnboardingIfShould(): void {
if (!this.props.isRunning && !this.props.hasBeenSeen && this.props.blockchainIsLoaded) {
this.props.updateIsRunning(true);
}
}
+ private _renderZrxAllowanceToggle(): React.ReactNode {
+ const zrxToken = utils.getZrxToken(this.props.tokenByAddress);
+ return this._renderAllowanceToggle(zrxToken);
+ }
+ private _renderEthAllowanceToggle(): React.ReactNode {
+ const ethToken = utils.getEthToken(this.props.tokenByAddress);
+ return this._renderAllowanceToggle(ethToken);
+ }
+ private _renderAllowanceToggle(token: Token): React.ReactNode {
+ if (!token) {
+ return null;
+ }
+ const tokenState = this.props.trackedTokenStateByAddress[token.address];
+ return (
+ <AllowanceToggle
+ token={token}
+ tokenState={tokenState}
+ isDisabled={!tokenState.isLoaded}
+ blockchain={this.props.blockchain}
+ // tslint:disable-next-line:jsx-no-lambda
+ refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(token.address)}
+ />
+ );
+ }
}
diff --git a/packages/website/ts/components/onboarding/set_allowances_onboarding_step.tsx b/packages/website/ts/components/onboarding/set_allowances_onboarding_step.tsx
new file mode 100644
index 000000000..1ff248c40
--- /dev/null
+++ b/packages/website/ts/components/onboarding/set_allowances_onboarding_step.tsx
@@ -0,0 +1,27 @@
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface SetAllowancesOnboardingStepProps {
+ zrxAllowanceToggle: React.ReactNode;
+ ethAllowanceToggle: React.ReactNode;
+}
+
+export const SetAllowancesOnboardingStep: React.StatelessComponent<SetAllowancesOnboardingStepProps> = ({
+ ethAllowanceToggle,
+ zrxAllowanceToggle,
+}) => (
+ <div className="flex items-center flex-column">
+ <Text>Unlock your tokens for trading. You only need to do this once for each token.</Text>
+ <Container width="100%" marginTop="25px" marginBottom="15px" className="flex justify-around">
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> Enable WETH </Text>
+ <Container marginTop="10px">{ethAllowanceToggle}</Container>
+ </div>
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> Enable ZRX </Text>
+ <Container marginTop="10px">{zrxAllowanceToggle}</Container>
+ </div>
+ </Container>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx b/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx
new file mode 100644
index 000000000..6e6a74a06
--- /dev/null
+++ b/packages/website/ts/components/onboarding/unlock_wallet_onboarding_step.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+
+export interface UnlockWalletOnboardingStepProps {}
+
+export const UnlockWalletOnboardingStep: React.StatelessComponent<UnlockWalletOnboardingStepProps> = () => (
+ <div className="flex items-center flex-column">
+ <div className="flex items-center flex-column">
+ <Container marginTop="15px" marginBottom="15px">
+ <img src="/images/metamask_icon.png" height="50px" width="50px" />
+ </Container>
+ <Text>Unlock your metamask extension to begin.</Text>
+ </div>
+ </div>
+);
diff --git a/packages/website/ts/components/onboarding/wrap_eth_onboarding_step.tsx b/packages/website/ts/components/onboarding/wrap_eth_onboarding_step.tsx
new file mode 100644
index 000000000..b21b39341
--- /dev/null
+++ b/packages/website/ts/components/onboarding/wrap_eth_onboarding_step.tsx
@@ -0,0 +1,73 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { Container } from 'ts/components/ui/container';
+import { IconButton } from 'ts/components/ui/icon_button';
+import { Text } from 'ts/components/ui/text';
+
+export interface WrapEthOnboardingStepProps {
+ formattedEthBalanceIfExists?: string;
+}
+
+export const WrapEthOnboardingStep: React.StatelessComponent<WrapEthOnboardingStepProps> = ({
+ formattedEthBalanceIfExists,
+}) => {
+ if (formattedEthBalanceIfExists) {
+ return (
+ <div className="flex items-center flex-column">
+ <Text>Congrats you now have {formattedEthBalanceIfExists} in your wallet.</Text>
+ <Container width="100%" marginTop="25px" marginBottom="15px" className="flex justify-center">
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> 1 ETH </Text>
+ <img src="/images/eth_dollar.svg" height="75px" width="75x" />
+ </div>
+ <Container marginRight="25px" marginLeft="25px" position="relative" top="20px">
+ <Text fontSize="25px">
+ <i className="zmdi zmdi-long-arrow-right" />
+ </Text>
+ </Container>
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> 1 WETH </Text>
+ <img src="/images/eth_token_erc20.svg" height="75px" width="75px" />
+ </div>
+ </Container>
+ </div>
+ );
+ } else {
+ return (
+ <div className="flex items-center flex-column">
+ <Text>
+ You need to convert some of your ETH into tradeable <b>Wrapped ETH (WETH)</b>.
+ </Text>
+ <Container width="100%" marginTop="25px" marginBottom="15px" className="flex justify-center">
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> 1 ETH </Text>
+ <img src="/images/eth_dollar.svg" height="75px" width="75x" />
+ </div>
+ <Container marginRight="25px" marginLeft="25px" position="relative" top="20px">
+ <Text fontSize="36px">=</Text>
+ </Container>
+ <div className="flex flex-column items-center">
+ <Text fontWeight={700}> 1 WETH </Text>
+ <img src="/images/eth_token_erc20.svg" height="75px" width="75px" />
+ </div>
+ </Container>
+ <Text>
+ Think of it like the coin version of a paper note. It has the same value, but some machines only
+ take coins.
+ </Text>
+ <Text>
+ Click
+ <Container display="inline-block" marginLeft="10px" marginRight="10px">
+ <IconButton
+ iconName="zmdi-long-arrow-down"
+ color={colors.mediumBlue}
+ labelText="wrap"
+ display="inline-flex"
+ />
+ </Container>
+ to wrap your ETH.
+ </Text>
+ </div>
+ );
+ }
+};
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 1b3bf3dae..d1d499314 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -1,6 +1,7 @@
import { colors, Styles } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
+import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import * as React from 'react';
import * as DocumentTitle from 'react-document-title';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
@@ -24,6 +25,7 @@ import { TradeHistory } from 'ts/components/trade_history/trade_history';
import { Container } from 'ts/components/ui/container';
import { FlashMessage } from 'ts/components/ui/flash_message';
import { Island } from 'ts/components/ui/island';
+import { Text } from 'ts/components/ui/text';
import { Wallet } from 'ts/components/wallet/wallet';
import { GenerateOrderForm } from 'ts/containers/generate_order_form';
import { PortalOnboardingFlow } from 'ts/containers/portal_onboarding_flow';
@@ -235,7 +237,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
: TokenVisibility.TRACKED;
return (
<div style={styles.root}>
- <PortalOnboardingFlow trackedTokenStateByAddress={this.state.trackedTokenStateByAddress} />
+ <PortalOnboardingFlow
+ blockchain={this._blockchain}
+ trackedTokenStateByAddress={this.state.trackedTokenStateByAddress}
+ refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
+ />
<DocumentTitle title="0x Portal DApp" />
<TopBar
userAddress={this.props.userAddress}
@@ -349,8 +355,26 @@ export class Portal extends React.Component<PortalProps, PortalState> {
/>
<Container marginTop="15px">
<Island>
- {/** TODO: Implement real styles. */}
- <p onClick={this._startOnboarding.bind(this)}>Start onboarding flow.</p>
+ <Container
+ marginTop="30px"
+ marginBottom="30px"
+ marginLeft="30px"
+ marginRight="30px"
+ className="flex justify-around items-center"
+ >
+ <ActionAccountBalanceWallet
+ style={{ width: '30px', height: '30px' }}
+ color={colors.orange}
+ />
+ <Text
+ fontColor={colors.grey}
+ fontSize="16px"
+ center={true}
+ onClick={this._startOnboarding.bind(this)}
+ >
+ Learn how to set up your account
+ </Text>
+ </Container>
</Island>
</Container>
</div>
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 2a051651d..7af80745c 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -20,11 +20,11 @@ import ReactTooltip = require('react-tooltip');
import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
import { AssetPicker } from 'ts/components/generate_order/asset_picker';
-import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle';
import { SendButton } from 'ts/components/send_button';
import { HelpTooltip } from 'ts/components/ui/help_tooltip';
import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button';
import { TokenIcon } from 'ts/components/ui/token_icon';
+import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle';
import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
@@ -362,13 +362,10 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
</TableRowColumn>
<TableRowColumn>
<AllowanceToggle
- networkId={this.props.networkId}
blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
token={token}
tokenState={tokenState}
onErrorOccurred={this._onErrorOccurred.bind(this)}
- userAddress={this.props.userAddress}
isDisabled={!tokenState.isLoaded}
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this, token.address)}
/>
diff --git a/packages/website/ts/components/ui/button.tsx b/packages/website/ts/components/ui/button.tsx
index 1f88297de..cb542a386 100644
--- a/packages/website/ts/components/ui/button.tsx
+++ b/packages/website/ts/components/ui/button.tsx
@@ -1,5 +1,5 @@
import { colors } from '@0xproject/react-shared';
-import { darken } from 'polished';
+import { darken, grayscale } from 'polished';
import * as React from 'react';
import { styled } from 'ts/style/theme';
@@ -12,32 +12,34 @@ export interface ButtonProps {
borderColor?: string;
width?: string;
type?: string;
+ isDisabled?: boolean;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
}
-const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, onClick, type, className }) => (
- <button type={type} className={className} onClick={onClick}>
+const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => (
+ <button type={type} className={className} onClick={isDisabled ? undefined : onClick}>
{children}
</button>
);
export const Button = styled(PlainButton)`
- cursor: pointer;
+ cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
font-size: ${props => props.fontSize};
color: ${props => props.fontColor};
+ transition: background-color 0.5s ease;
padding: 0.8em 2.2em;
border-radius: 6px;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
font-weight: 500;
font-family: ${props => props.fontFamily};
width: ${props => props.width};
- background-color: ${props => props.backgroundColor};
+ background-color: ${props => (props.isDisabled ? grayscale(props.backgroundColor) : props.backgroundColor)};
border: ${props => (props.borderColor ? `1px solid ${props.borderColor}` : 'none')};
&:hover {
- background-color: ${props => darken(0.1, props.backgroundColor)};
+ background-color: ${props => (!props.isDisabled ? darken(0.1, props.backgroundColor) : '')};
}
&:active {
- background-color: ${props => darken(0.2, props.backgroundColor)};
+ background-color: ${props => (!props.isDisabled ? darken(0.2, props.backgroundColor) : '')};
}
`;
@@ -46,6 +48,7 @@ Button.defaultProps = {
backgroundColor: colors.white,
width: 'auto',
fontFamily: 'Roboto',
+ isDisabled: false,
};
Button.displayName = 'Button';
diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx
index c6a78e181..1776345da 100644
--- a/packages/website/ts/components/ui/container.tsx
+++ b/packages/website/ts/components/ui/container.tsx
@@ -14,8 +14,15 @@ export interface ContainerProps {
backgroundColor?: string;
borderRadius?: StringOrNum;
maxWidth?: StringOrNum;
+ width?: StringOrNum;
isHidden?: boolean;
className?: string;
+ position?: 'absolute' | 'fixed' | 'relative' | 'unset';
+ display?: 'inline-block' | 'block' | 'inline-flex' | 'inline';
+ top?: string;
+ left?: string;
+ right?: string;
+ bottom?: string;
}
export const Container: React.StatelessComponent<ContainerProps> = ({ children, className, isHidden, ...style }) => {
diff --git a/packages/website/ts/components/ui/icon_button.tsx b/packages/website/ts/components/ui/icon_button.tsx
index 2f5172f05..13cd239da 100644
--- a/packages/website/ts/components/ui/icon_button.tsx
+++ b/packages/website/ts/components/ui/icon_button.tsx
@@ -5,15 +5,15 @@ import * as React from 'react';
export interface IconButtonProps {
iconName: string;
labelText?: string;
- onClick: () => void;
+ onClick?: () => void;
color?: string;
+ display?: string;
}
interface IconButtonState {
isHovering: boolean;
}
export class IconButton extends React.Component<IconButtonProps, IconButtonState> {
public static defaultProps: Partial<IconButtonProps> = {
- onClick: _.noop,
labelText: '',
color: colors.mediumBlue,
};
@@ -26,8 +26,9 @@ export class IconButton extends React.Component<IconButtonProps, IconButtonState
public render(): React.ReactNode {
const styles: Styles = {
root: {
- cursor: 'pointer',
- opacity: this.state.isHovering ? 0.5 : 1,
+ cursor: this.props.onClick ? 'pointer' : 'undefined',
+ opacity: this.state.isHovering && this.props.onClick ? 0.5 : 1,
+ display: this.props.display,
},
icon: {
color: this.props.color,
diff --git a/packages/website/ts/components/ui/pointer.tsx b/packages/website/ts/components/ui/pointer.tsx
new file mode 100644
index 000000000..448786bb4
--- /dev/null
+++ b/packages/website/ts/components/ui/pointer.tsx
@@ -0,0 +1,67 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+
+export type PointerDirection = 'top' | 'right' | 'bottom' | 'left';
+
+export interface PointerProps {
+ className?: string;
+ color?: string;
+ size?: number;
+ direction: PointerDirection;
+}
+
+const PlainPointer: React.StatelessComponent<PointerProps> = props => <div {...props} />;
+
+const positionToCss = (props: PointerProps) => {
+ const position = {
+ top: `bottom: 100%; left: 50%;`,
+ right: `left: 100%; top: 50%;`,
+ bottom: `top: 100%; left: 50%;`,
+ left: `right: 100%; top: 50%;`,
+ }[props.direction];
+
+ const borderColorSide = {
+ top: 'border-bottom-color',
+ right: 'border-left-color',
+ bottom: 'border-top-color',
+ left: 'border-right-color',
+ }[props.direction];
+ const border = `${borderColorSide}: ${props.color};`;
+ const marginSide = {
+ top: 'margin-left',
+ right: 'margin-top',
+ bottom: 'margin-left',
+ left: 'margin-top',
+ }[props.direction];
+ const margin = `${marginSide}: -${props.size}px`;
+ return {
+ position,
+ border,
+ margin,
+ };
+};
+
+export const Pointer = styled(PlainPointer)`
+ position: relative;
+ &:after {
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-color: rgba(136, 183, 213, 0);
+ border-width: ${props => `${props.size}px`};
+ ${props => positionToCss(props).position}
+ ${props => positionToCss(props).border}
+ ${props => positionToCss(props).margin}
+ }
+`;
+
+Pointer.defaultProps = {
+ color: colors.white,
+ size: 16,
+};
+
+Pointer.displayName = 'Pointer';
diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx
index 7e47f1d09..1e2a123b7 100644
--- a/packages/website/ts/components/ui/text.tsx
+++ b/packages/website/ts/components/ui/text.tsx
@@ -1,8 +1,9 @@
import { colors } from '@0xproject/react-shared';
+import { darken } from 'polished';
import * as React from 'react';
import { styled } from 'ts/style/theme';
-export type TextTag = 'p' | 'div' | 'span' | 'label';
+export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4';
export interface TextProps {
className?: string;
@@ -14,10 +15,13 @@ export interface TextProps {
minHeight?: string;
center?: boolean;
fontWeight?: number | string;
+ onClick?: () => void;
}
-const PlainText: React.StatelessComponent<TextProps> = ({ children, className, Tag }) => (
- <Tag className={className}>{children}</Tag>
+const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick, Tag }) => (
+ <Tag className={className} onClick={onClick}>
+ {children}
+ </Tag>
);
export const Text = styled(PlainText)`
@@ -28,14 +32,31 @@ export const Text = styled(PlainText)`
${props => (props.center ? 'text-align: center' : '')};
color: ${props => props.fontColor};
${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')};
+ ${props => (props.onClick ? 'cursor: pointer' : '')};
+ transition: color 0.5s ease;
+ &:hover {
+ ${props => (props.onClick ? `color: ${darken(0.1, props.fontColor)}` : '')};
+ }
`;
Text.defaultProps = {
fontFamily: 'Roboto',
fontWeight: 400,
- fontColor: colors.white,
- fontSize: '14px',
+ fontColor: colors.black,
+ fontSize: '15px',
+ lineHeight: '1.5em',
Tag: 'div',
};
Text.displayName = 'Text';
+
+export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />;
+
+Title.defaultProps = {
+ Tag: 'h2',
+ fontSize: '20px',
+ fontWeight: 600,
+ fontColor: colors.black,
+};
+
+Title.displayName = 'Title';
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index f80be6313..3a6d9942d 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -13,7 +13,6 @@ import { Link } from 'react-router-dom';
import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
-import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle';
import { Container } from 'ts/components/ui/container';
import { IconButton } from 'ts/components/ui/icon_button';
import { Identicon } from 'ts/components/ui/identicon';
@@ -21,6 +20,7 @@ import { Island } from 'ts/components/ui/island';
import { TokenIcon } from 'ts/components/ui/token_icon';
import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item';
import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
+import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { zIndex } from 'ts/style/z_index';
@@ -124,7 +124,6 @@ const styles: Styles = {
const ETHER_ICON_PATH = '/images/ether.png';
const ICON_DIMENSION = 28;
-const TOKEN_AMOUNT_DISPLAY_PRECISION = 5;
const BODY_ITEM_KEY = 'BODY';
const HEADER_ITEM_KEY = 'HEADER';
const FOOTER_ITEM_KEY = 'FOOTER';
@@ -414,15 +413,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
private _renderAllowanceToggle(config: AllowanceToggleConfig): React.ReactNode {
+ // TODO: Error handling
return (
<AllowanceToggle
- networkId={this.props.networkId}
blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
token={config.token}
tokenState={config.tokenState}
- onErrorOccurred={_.noop} // TODO: Error handling
- userAddress={this.props.userAddress}
isDisabled={!config.tokenState.isLoaded}
refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(config.token.address)}
/>
@@ -441,10 +437,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</PlaceHolder>
);
} else {
- const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
- const precision = Math.min(TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces());
- const formattedAmount = unitAmount.toFixed(precision);
- const result = `${formattedAmount} ${symbol}`;
+ const result = utils.getFormattedAmount(amount, decimals, symbol);
return <div style={styles.amountLabel}>{result}</div>;
}
}
diff --git a/packages/website/ts/containers/inputs/allowance_toggle.ts b/packages/website/ts/containers/inputs/allowance_toggle.ts
new file mode 100644
index 000000000..545708f92
--- /dev/null
+++ b/packages/website/ts/containers/inputs/allowance_toggle.ts
@@ -0,0 +1,41 @@
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { Blockchain } from 'ts/blockchain';
+import { State } from 'ts/redux/reducer';
+import { BalanceErrs, Token, TokenState } from 'ts/types';
+
+import { AllowanceToggle as AllowanceToggleComponent } from 'ts/components/inputs/allowance_toggle';
+import { Dispatcher } from 'ts/redux/dispatcher';
+
+interface AllowanceToggleProps {
+ blockchain: Blockchain;
+ onErrorOccurred?: (errType: BalanceErrs) => void;
+ token: Token;
+ tokenState: TokenState;
+ isDisabled?: boolean;
+ refetchTokenStateAsync: () => Promise<void>;
+}
+
+interface ConnectedState {
+ networkId: number;
+ userAddress: string;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, _ownProps: AllowanceToggleProps): ConnectedState => ({
+ networkId: state.networkId,
+ userAddress: state.userAddress,
+});
+
+const mapDispatchTopProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const AllowanceToggle: React.ComponentClass<AllowanceToggleProps> = connect(
+ mapStateToProps,
+ mapDispatchTopProps,
+)(AllowanceToggleComponent);
diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts
index 4298ab275..746adf0ba 100644
--- a/packages/website/ts/containers/portal_onboarding_flow.ts
+++ b/packages/website/ts/containers/portal_onboarding_flow.ts
@@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils';
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
+import { Blockchain } from 'ts/blockchain';
import { ActionTypes, ProviderType, TokenByAddress, TokenStateByAddress } from 'ts/types';
import { PortalOnboardingFlow as PortalOnboardingFlowComponent } from 'ts/components/onboarding/portal_onboarding_flow';
@@ -9,6 +10,8 @@ import { State } from 'ts/redux/reducer';
interface PortalOnboardingFlowProps {
trackedTokenStateByAddress: TokenStateByAddress;
+ blockchain: Blockchain;
+ refetchTokenStateAsync: (tokenAddress: string) => Promise<void>;
}
interface ConnectedState {
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index d281c5738..25670ef27 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -6,6 +6,7 @@ export const constants = {
ETHER_TOKEN_SYMBOL: 'WETH',
ZRX_TOKEN_SYMBOL: 'ZRX',
ETHER_SYMBOL: 'ETH',
+ TOKEN_AMOUNT_DISPLAY_PRECISION: 5,
GENESIS_ORDER_BLOCK_BY_NETWORK_ID: {
1: 4145578,
42: 3117574,
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index eb384fbb4..414361c1b 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -3,6 +3,7 @@ import { OrderError } from '@0xproject/order-utils';
import { constants as sharedConstants, Networks } from '@0xproject/react-shared';
import { ECSignature, Provider } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import deepEqual = require('deep-equal');
import * as _ from 'lodash';
import * as moment from 'moment';
@@ -17,6 +18,7 @@ import {
SideToAssetToken,
Token,
TokenByAddress,
+ TokenState,
} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
@@ -322,8 +324,23 @@ export const utils = {
return this.isDevelopment() || this.isStaging() || this.isDogfood();
},
getEthToken(tokenByAddress: TokenByAddress): Token {
+ return utils.getTokenBySymbol(constants.ETHER_TOKEN_SYMBOL, tokenByAddress);
+ },
+ getZrxToken(tokenByAddress: TokenByAddress): Token {
+ return utils.getTokenBySymbol(constants.ZRX_TOKEN_SYMBOL, tokenByAddress);
+ },
+ getTokenBySymbol(symbol: string, tokenByAddress: TokenByAddress): Token {
const tokens = _.values(tokenByAddress);
- const etherToken = _.find(tokens, { symbol: constants.ETHER_TOKEN_SYMBOL });
- return etherToken;
+ const token = _.find(tokens, { symbol });
+ return token;
+ },
+ getFormattedAmountFromToken(token: Token, tokenState: TokenState): string {
+ return utils.getFormattedAmount(tokenState.balance, token.decimals, token.symbol);
+ },
+ getFormattedAmount(amount: BigNumber, decimals: number, symbol: string): string {
+ const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
+ const precision = Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces());
+ const formattedAmount = unitAmount.toFixed(precision);
+ return `${formattedAmount} ${symbol}`;
},
};