diff options
Diffstat (limited to 'packages')
41 files changed, 314 insertions, 74 deletions
diff --git a/packages/contracts/CHANGELOG.json b/packages/contracts/CHANGELOG.json new file mode 100644 index 000000000..3f57a33d6 --- /dev/null +++ b/packages/contracts/CHANGELOG.json @@ -0,0 +1,115 @@ +[ + { + "name": "Forwarder", + "version": "1.1.0", + "changes": [ + { + "note": "Round up when calculating remaining amounts in marketBuy functions", + "pr": 1162, + "networks": { + "1": "0x5468a1dc173652ee28d249c271fa9933144746b1", + "3": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e", + "42": "0x17992e4ffb22730138e4b62aaa6367fa9d3699a6" + } + } + ] + }, + { + "name": "Forwarder", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x7afc2d5107af94c462a194d2c21b5bdd238709d6", + "3": "0x3983e204b12b3c02fb0638caf2cd406a62e0ead3", + "42": "0xd85e2fa7e7e252b27b01bf0d65c946959d2f45b8" + } + } + ] + }, + { + "name": "OrderValidator", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x9463e518dea6810309563c81d5266c1b1d149138", + "3": "0x90431a90516ab49af23a0530e04e8c7836e7122f", + "42": "0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d" + } + } + ] + }, + { + "name": "Exchange", + "version": "2.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + "3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", + "42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2" + } + } + ] + }, + { + "name": "ERC20Proxy", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e", + "3": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa", + "42": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e" + } + } + ] + }, + { + "name": "ERC721Proxy", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x208e41fb445f1bb1b6780d58356e81405f3e6127", + "3": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", + "42": "0x2a9127c745688a165106c11cd4d647d2220af821" + } + } + ] + }, + { + "name": "AssetProxyOwner", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v2 deploy", + "networks": { + "1": "0x17992e4ffb22730138e4b62aaa6367fa9d3699a6", + "3": "0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b", + "42": "0x2c824d2882baa668e0d5202b1e7f2922278703f8" + } + } + ] + }, + { + "name": "ZRXToken", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v1 deploy", + "networks": { + "1": "0xe41d2489571d322189246dafa5ebde1f4699f498", + "3": "0xff67881f8d12f372d91baae9752eb3631ff0ed00", + "42": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa" + } + } + ] + } +] diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 5aedb249f..97a2816ff 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -1,6 +1,6 @@ ## Contracts -Smart contracts that implement the 0x protocol. Addresses of the deployed contracts can be found [here](https://0xproject.com/wiki#Deployed-Addresses). +Smart contracts that implement the 0x protocol. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [CHANGELOG](./CHANGELOG.json) of this package. ## Usage diff --git a/packages/instant/src/components/ui/overlay.tsx b/packages/instant/src/components/ui/overlay.tsx index c5f55f9c0..f67d6fb2f 100644 --- a/packages/instant/src/components/ui/overlay.tsx +++ b/packages/instant/src/components/ui/overlay.tsx @@ -1,11 +1,15 @@ import * as _ from 'lodash'; -import { overlayBlack, styled } from '../../style/theme'; +import { generateMediaWrapper, ScreenWidths } from '../../style/media'; +import { generateOverlayBlack, styled } from '../../style/theme'; import { zIndex } from '../../style/z_index'; export interface OverlayProps { zIndex?: number; backgroundColor?: string; + width?: string; + height?: string; + showMaxWidth?: ScreenWidths; } export const Overlay = @@ -20,12 +24,16 @@ export const Overlay = left: 0; z-index: ${props => props.zIndex} background-color: ${props => props.backgroundColor}; + ${props => props.width && `width: ${props.width};`} + ${props => props.height && `height: ${props.height};`} + display: ${props => (props.showMaxWidth ? 'none' : 'block')}; + ${props => props.showMaxWidth && generateMediaWrapper(props.showMaxWidth)`display: block;`} } `; Overlay.defaultProps = { zIndex: zIndex.overlayDefault, - backgroundColor: overlayBlack, + backgroundColor: generateOverlayBlack(0.6), }; Overlay.displayName = 'Overlay'; diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index cceb44377..411f118cc 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -5,15 +5,18 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Provider as ReduxProvider } from 'react-redux'; +import { ACCOUNT_UPDATE_INTERVAL_TIME_MS, BUY_QUOTE_UPDATE_INTERVAL_TIME_MS } from '../constants'; import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider'; import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; +import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; +import { Heartbeater } from '../util/heartbeater'; +import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory'; import { providerStateFactory } from '../util/provider_state_factory'; fonts.include(); @@ -37,6 +40,9 @@ export interface ZeroExInstantProviderOptionalProps { export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> { private readonly _store: Store; + private _accountUpdateHeartbeat?: Heartbeater; + private _buyQuoteHeartbeat?: Heartbeater; + // TODO(fragosti): Write tests for this beast once we inject a provider. private static _mergeDefaultStateWithProps( props: ZeroExInstantProviderProps, @@ -92,10 +98,21 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider // tslint:disable-next-line:no-floating-promises asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store); } + if (state.providerState.account.state !== AccountState.None) { + this._accountUpdateHeartbeat = generateAccountHeartbeater({ + store: this._store, + shouldPerformImmediatelyOnStart: true, + }); + this._accountUpdateHeartbeat.start(ACCOUNT_UPDATE_INTERVAL_TIME_MS); + } + + this._buyQuoteHeartbeat = generateBuyQuoteHeartbeater({ + store: this._store, + shouldPerformImmediatelyOnStart: false, + }); + this._buyQuoteHeartbeat.start(BUY_QUOTE_UPDATE_INTERVAL_TIME_MS); // tslint:disable-next-line:no-floating-promises - asyncData.fetchAccountInfoAndDispatchToStore(this._store); - // tslint:disable-next-line:no-floating-promises - asyncData.fetchCurrentBuyQuoteAndDispatchToStore(this._store); + asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store: this._store, shouldSetPending: true }); // warm up the gas price estimator cache just in case we can't // grab the gas price estimate when submitting the transaction // tslint:disable-next-line:no-floating-promises @@ -103,6 +120,14 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider // tslint:disable-next-line:no-floating-promises this._flashErrorIfWrongNetwork(); } + public componentWillUnmount(): void { + if (this._accountUpdateHeartbeat) { + this._accountUpdateHeartbeat.stop(); + } + if (this._buyQuoteHeartbeat) { + this._buyQuoteHeartbeat.stop(); + } + } public render(): React.ReactNode { return ( <ReduxProvider store={this._store}> diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index b5c4f96e4..110a8248a 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -11,6 +11,8 @@ export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction fa export const GWEI_IN_WEI = new BigNumber(1000000000); export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; +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 DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6); export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2; export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; @@ -31,6 +33,3 @@ export const LOADING_ACCOUNT: AccountNotReady = { export const LOCKED_ACCOUNT: AccountNotReady = { state: AccountState.Locked, }; -export const ERROR_ACCOUNT: AccountNotReady = { - state: AccountState.Error, -}; diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx index 99e55a6c4..c0da181f1 100644 --- a/packages/instant/src/containers/latest_error.tsx +++ b/packages/instant/src/containers/latest_error.tsx @@ -1,35 +1,60 @@ import * as React from 'react'; import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; import { SlideAnimationState } from '../components/animations/slide_animation'; import { SlidingError } from '../components/sliding_error'; +import { Overlay } from '../components/ui/overlay'; +import { Action } from '../redux/actions'; import { State } from '../redux/reducer'; -import { Asset, DisplayStatus } from '../types'; +import { ScreenWidths } from '../style/media'; +import { generateOverlayBlack } from '../style/theme'; +import { zIndex } from '../style/z_index'; +import { Asset, DisplayStatus, Omit } from '../types'; +import { errorFlasher } from '../util/error_flasher'; export interface LatestErrorComponentProps { asset?: Asset; latestErrorMessage?: string; animationState: SlideAnimationState; + shouldRenderOverlay: boolean; + onOverlayClick: () => void; } export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => { if (!props.latestErrorMessage) { return <div />; } - return <SlidingError animationState={props.animationState} icon="😢" message={props.latestErrorMessage} />; + return ( + <React.Fragment> + <SlidingError animationState={props.animationState} icon="😢" message={props.latestErrorMessage} /> + {props.shouldRenderOverlay && ( + <Overlay + onClick={props.onOverlayClick} + zIndex={zIndex.containerOverlay} + showMaxWidth={ScreenWidths.Sm} + backgroundColor={generateOverlayBlack(0.4)} + /> + )} + </React.Fragment> + ); }; -interface ConnectedState { - asset?: Asset; - latestErrorMessage?: string; - animationState: SlideAnimationState; -} export interface LatestErrorProps {} +interface ConnectedState extends Omit<LatestErrorComponentProps, 'onOverlayClick'> {} const mapStateToProps = (state: State, _ownProps: LatestErrorProps): ConnectedState => ({ asset: state.selectedAsset, latestErrorMessage: state.latestErrorMessage, animationState: state.latestErrorDisplayStatus === DisplayStatus.Present ? 'slidIn' : 'slidOut', + shouldRenderOverlay: state.latestErrorDisplayStatus === DisplayStatus.Present, +}); + +type ConnectedDispatch = Pick<LatestErrorComponentProps, 'onOverlayClick'>; +const mapDispatchToProps = (dispatch: Dispatch<Action>, _ownProps: LatestErrorProps): ConnectedDispatch => ({ + onOverlayClick: () => { + errorFlasher.clearError(dispatch); + }, }); -export const LatestError = connect(mapStateToProps)(LatestErrorComponent); +export const LatestError = connect(mapStateToProps, mapDispatchToProps)(LatestErrorComponent); diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index c550aef04..93ff3db70 100644 --- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -69,7 +69,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP const debouncedUpdateBuyQuoteAsync = _.debounce(buyQuoteUpdater.updateBuyQuoteAsync.bind(buyQuoteUpdater), 200, { trailing: true, -}); +}) as typeof buyQuoteUpdater.updateBuyQuoteAsync; const mapDispatchToProps = ( dispatch: Dispatch<Action>, @@ -87,7 +87,7 @@ const mapDispatchToProps = ( // even if it's debounced, give them the illusion it's loading dispatch(actions.setQuoteRequestStatePending()); // tslint:disable-next-line:no-floating-promises - debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, affiliateInfo); + debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, true, affiliateInfo); } }, }); diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index fc89e3d0e..8947c6c97 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -23,7 +23,6 @@ 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', @@ -47,7 +46,6 @@ 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), diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index a8f632009..b920ac914 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 { AccountState, ERC20Asset } from '../types'; +import { AccountState, ERC20Asset, OrderProcessState } from '../types'; import { assetUtils } from '../util/asset'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; import { coinbaseApi } from '../util/coinbase_api'; @@ -36,17 +36,23 @@ export const asyncData = { store.dispatch(actions.setAvailableAssets([])); } }, - fetchAccountInfoAndDispatchToStore: async (store: Store) => { + fetchAccountInfoAndDispatchToStore: async (options: { store: Store; shouldSetToLoading: boolean }) => { + const { store, shouldSetToLoading } = options; const { providerState } = store.getState(); const web3Wrapper = providerState.web3Wrapper; - if (providerState.account.state !== AccountState.Loading) { + const provider = providerState.provider; + if (shouldSetToLoading && providerState.account.state !== AccountState.Loading) { store.dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; try { - availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); + // TODO(bmillman): Add support at the web3Wrapper level for calling `eth_requestAccounts` instead of calling enable here + const isPrivacyModeEnabled = !_.isUndefined((provider as any).enable); + availableAddresses = isPrivacyModeEnabled + ? await (provider as any).enable() + : await web3Wrapper.getAvailableAddressesAsync(); } catch (e) { - store.dispatch(actions.setAccountStateError()); + store.dispatch(actions.setAccountStateLocked()); return; } if (!_.isEmpty(availableAddresses)) { @@ -74,12 +80,14 @@ export const asyncData = { return; } }, - fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store) => { - const { providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); + fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; shouldSetPending: boolean }) => { + const { store, shouldSetPending } = options; + const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( !_.isUndefined(selectedAssetAmount) && !_.isUndefined(selectedAsset) && + buyOrderState.processState === OrderProcessState.None && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 ) { await buyQuoteUpdater.updateBuyQuoteAsync( @@ -87,6 +95,7 @@ export const asyncData = { store.dispatch, selectedAsset as ERC20Asset, selectedAssetAmount, + shouldSetPending, affiliateInfo, ); } diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index a5a1b6f7d..ef46fdd9d 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -4,7 +4,7 @@ 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 { LOADING_ACCOUNT, LOCKED_ACCOUNT } from '../constants'; import { assetMetaDataMap } from '../data/asset_meta_data_map'; import { Account, @@ -65,8 +65,6 @@ export const createReducer = (initialState: State) => { 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, diff --git a/packages/instant/src/style/media.ts b/packages/instant/src/style/media.ts index 5e7aaba37..bbf376694 100644 --- a/packages/instant/src/style/media.ts +++ b/packages/instant/src/style/media.ts @@ -8,7 +8,7 @@ export enum ScreenWidths { Lg = 64, } -const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) => css` +export const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) => css` @media (max-width: ${screenWidth}em) { ${css.apply(css, args)}; } diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts index 2653c38f7..1e9f55e00 100644 --- a/packages/instant/src/style/theme.ts +++ b/packages/instant/src/style/theme.ts @@ -35,7 +35,10 @@ export const theme: Theme = { }; 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 const generateOverlayBlack = (opacity = 0.6) => { + return `rgba(0, 0, 0, ${opacity})`; +}; + 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 bd034182e..ba2d27a17 100644 --- a/packages/instant/src/style/z_index.ts +++ b/packages/instant/src/style/z_index.ts @@ -3,6 +3,7 @@ export const zIndex = { mainContainer: 20, dropdownItems: 30, panel: 40, + containerOverlay: 45, errorPopup: 50, overlayDefault: 100, }; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index 20ad2ed95..b43a82d46 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -4,6 +4,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; // Reusable +export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; export type Maybe<T> = T | undefined; export enum AsyncProcessState { None = 'NONE', @@ -101,11 +102,10 @@ export interface ProviderState { } export enum AccountState { + None = 'NONE,', Loading = 'LOADING', Ready = 'READY', - Locked = 'LOCKED', // TODO(bmillman): break this up into locked / privacy mode enabled - Error = 'ERROR', - None = 'NONE,', + Locked = 'LOCKED', } export interface AccountReady { @@ -114,7 +114,7 @@ export interface AccountReady { ethBalanceInWei?: BigNumber; } export interface AccountNotReady { - state: AccountState.None | AccountState.Loading | AccountState.Locked | AccountState.Error; + state: AccountState.None | AccountState.Loading | AccountState.Locked; } export type Account = AccountReady | AccountNotReady; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index e697d3ef7..c33e28f1c 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -16,12 +16,15 @@ export const buyQuoteUpdater = { dispatch: Dispatch<Action>, asset: ERC20Asset, assetAmount: BigNumber, + setPending = true, affiliateInfo?: AffiliateInfo, ): Promise<void> => { // get a new buy quote. const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); - // mark quote as pending - dispatch(actions.setQuoteRequestStatePending()); + if (setPending) { + // mark quote as pending + dispatch(actions.setQuoteRequestStatePending()); + } const feePercentage = oc(affiliateInfo).feePercentage(); let newBuyQuote: BuyQuote | undefined; try { diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts new file mode 100644 index 000000000..e700d489e --- /dev/null +++ b/packages/instant/src/util/heartbeater.ts @@ -0,0 +1,35 @@ +import { intervalUtils } from '@0x/utils'; +import * as _ from 'lodash'; + +type HeartbeatableFunction = () => Promise<void>; +export class Heartbeater { + private _intervalId?: NodeJS.Timer; + private readonly _performImmediatelyOnStart: boolean; + private readonly _performFunction: HeartbeatableFunction; + + public constructor(performingFunctionAsync: HeartbeatableFunction, performImmediatelyOnStart: boolean) { + this._performFunction = performingFunctionAsync; + this._performImmediatelyOnStart = performImmediatelyOnStart; + } + + public start(intervalTimeMs: number): void { + if (!_.isUndefined(this._intervalId)) { + throw new Error('Heartbeat is running, please stop before restarting'); + } + + if (this._performImmediatelyOnStart) { + // tslint:disable-next-line:no-floating-promises + this._performFunction(); + } + + // tslint:disable-next-line:no-unbound-method + this._intervalId = intervalUtils.setAsyncExcludingInterval(this._performFunction, intervalTimeMs, _.noop); + } + + public stop(): void { + if (this._intervalId) { + intervalUtils.clearInterval(this._intervalId); + } + this._intervalId = undefined; + } +} diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts new file mode 100644 index 000000000..96a8ac4e6 --- /dev/null +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -0,0 +1,22 @@ +import { asyncData } from '../redux/async_data'; +import { Store } from '../redux/store'; + +import { Heartbeater } from './heartbeater'; + +export interface HeartbeatFactoryOptions { + store: Store; + shouldPerformImmediatelyOnStart: boolean; +} +export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { + const { store, shouldPerformImmediatelyOnStart } = options; + return new Heartbeater(async () => { + await asyncData.fetchAccountInfoAndDispatchToStore({ store, shouldSetToLoading: false }); + }, shouldPerformImmediatelyOnStart); +}; + +export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { + const { store, shouldPerformImmediatelyOnStart } = options; + return new Heartbeater(async () => { + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, shouldSetPending: false }); + }, shouldPerformImmediatelyOnStart); +}; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 56877fef3..23204e616 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -533,9 +533,6 @@ export class Web3Wrapper { method: 'eth_call', params: [callDataHex, marshalledDefaultBlock], }); - if (rawCallResult === '0x') { - throw new Error('Contract call failed (returned null)'); - } return rawCallResult; } /** diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index b43c41739..25430c1c7 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -26,7 +26,7 @@ import { BlockParam, LogWithDecodedArgs, Provider, TransactionReceiptWithDecoded import * as _ from 'lodash'; import * as moment from 'moment'; import * as React from 'react'; -import contract = require('truffle-contract'); +import contract from 'truffle-contract'; import { BlockchainWatcher } from 'ts/blockchain_watcher'; import { AssetSendCompleted } from 'ts/components/flash_messages/asset_send_completed'; import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; @@ -55,9 +55,9 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { fakeTokenRegistry } from 'ts/utils/fake_token_registry'; import { tokenAddressOverrides } from 'ts/utils/token_address_overrides'; import { utils } from 'ts/utils/utils'; -import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); +import FilterSubprovider from 'web3-provider-engine/subproviders/filters'; -import * as MintableArtifacts from '../contracts/Mintable.json'; +import MintableArtifacts from '../contracts/Mintable.json'; const BLOCK_NUMBER_BACK_TRACK = 50; const GWEI_IN_WEI = 1000000000; diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index a9f591150..e15a2dd94 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -7,7 +7,7 @@ import FlatButton from 'material-ui/FlatButton'; import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; import TextField from 'material-ui/TextField'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { Blockchain } from 'ts/blockchain'; import { NetworkDropDown } from 'ts/components/dropdowns/network_drop_down'; import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 2af4252bd..4a9f3b2fe 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -6,7 +6,7 @@ import Divider from 'material-ui/Divider'; import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; import * as moment from 'moment'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { Blockchain } from 'ts/blockchain'; import { EthWethConversionButton } from 'ts/components/eth_weth_conversion_button'; import { Dispatcher } from 'ts/redux/dispatcher'; diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index 98aad6c62..e6ecd2ec8 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -3,7 +3,7 @@ import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; import * as moment from 'moment'; import * as React from 'react'; -import firstBy = require('thenby'); +import firstBy from 'thenby'; import { Blockchain } from 'ts/blockchain'; import { NewTokenForm } from 'ts/components/generate_order/new_token_form'; diff --git a/packages/website/ts/components/inputs/allowance_state_toggle.tsx b/packages/website/ts/components/inputs/allowance_state_toggle.tsx index fd7d3b174..5396295d2 100644 --- a/packages/website/ts/components/inputs/allowance_state_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_state_toggle.tsx @@ -2,7 +2,7 @@ import { colors } from '@0x/react-shared'; import { BigNumber, logUtils } from '@0x/utils'; import * as _ from 'lodash'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { Blockchain } from 'ts/blockchain'; import { AllowanceState, AllowanceStateView } from 'ts/components/ui/allowance_state_view'; import { Container } from 'ts/components/ui/container'; diff --git a/packages/website/ts/components/inputs/hash_input.tsx b/packages/website/ts/components/inputs/hash_input.tsx index 73b6615d4..7688ffe21 100644 --- a/packages/website/ts/components/inputs/hash_input.tsx +++ b/packages/website/ts/components/inputs/hash_input.tsx @@ -3,7 +3,7 @@ import { Styles } from '@0x/react-shared'; import { Order } from '@0x/types'; import * as _ from 'lodash'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { Blockchain } from 'ts/blockchain'; import { FakeTextField } from 'ts/components/ui/fake_text_field'; diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 9ba2bad84..92aecf046 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -16,8 +16,8 @@ import ContentAdd from 'material-ui/svg-icons/content/add'; import ContentRemove from 'material-ui/svg-icons/content/remove'; import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); -import firstBy = require('thenby'); +import ReactTooltip from 'react-tooltip'; +import firstBy from 'thenby'; import { Blockchain } from 'ts/blockchain'; import { AssetPicker } from 'ts/components/generate_order/asset_picker'; import { SendButton } from 'ts/components/send_button'; diff --git a/packages/website/ts/components/ui/copy_icon.tsx b/packages/website/ts/components/ui/copy_icon.tsx index 59e398cb6..403cd4607 100644 --- a/packages/website/ts/components/ui/copy_icon.tsx +++ b/packages/website/ts/components/ui/copy_icon.tsx @@ -2,7 +2,7 @@ import { colors } from '@0x/react-shared'; import * as React from 'react'; import * as CopyToClipboard from 'react-copy-to-clipboard'; import * as ReactDOM from 'react-dom'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; interface CopyIconProps { data: string; diff --git a/packages/website/ts/components/ui/ethereum_address.tsx b/packages/website/ts/components/ui/ethereum_address.tsx index 71d98af56..12f8636eb 100644 --- a/packages/website/ts/components/ui/ethereum_address.tsx +++ b/packages/website/ts/components/ui/ethereum_address.tsx @@ -1,6 +1,6 @@ import { EtherscanLinkSuffixes } from '@0x/react-shared'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { EtherScanIcon } from 'ts/components/ui/etherscan_icon'; import { utils } from 'ts/utils/utils'; diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx index 57ab91ba2..a7fba8a33 100644 --- a/packages/website/ts/components/ui/etherscan_icon.tsx +++ b/packages/website/ts/components/ui/etherscan_icon.tsx @@ -1,7 +1,7 @@ import { colors, EtherscanLinkSuffixes, utils as sharedUtils } from '@0x/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; interface EtherScanIconProps { addressOrTxHash: string; diff --git a/packages/website/ts/components/ui/help_tooltip.tsx b/packages/website/ts/components/ui/help_tooltip.tsx index d827eebb9..831d888f5 100644 --- a/packages/website/ts/components/ui/help_tooltip.tsx +++ b/packages/website/ts/components/ui/help_tooltip.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; interface HelpTooltipProps { style?: React.CSSProperties; diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx index b5b374973..9eca04a5d 100644 --- a/packages/website/ts/components/ui/identicon.tsx +++ b/packages/website/ts/components/ui/identicon.tsx @@ -1,4 +1,4 @@ -import blockies = require('blockies'); +import blockies from 'blockies'; import * as _ from 'lodash'; import * as React from 'react'; diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index 6c0572437..f9e0967d4 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -1,7 +1,7 @@ import { colors, EtherscanLinkSuffixes, utils as sharedUtils } from '@0x/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import ReactTooltip = require('react-tooltip'); +import ReactTooltip from 'react-tooltip'; import { EthereumAddress } from 'ts/components/ui/ethereum_address'; import { Identicon } from 'ts/components/ui/identicon'; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 326cd3cfa..d9da0b9d5 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; import * as React from 'react'; -import firstBy = require('thenby'); +import firstBy from 'thenby'; import { Blockchain } from 'ts/blockchain'; import { AccountConnection } from 'ts/components/ui/account_connection'; diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx index 91818b035..ef6a0a1aa 100644 --- a/packages/website/ts/local_storage/trade_history_storage.tsx +++ b/packages/website/ts/local_storage/trade_history_storage.tsx @@ -1,5 +1,5 @@ import { BigNumber } from '@0x/utils'; -import ethUtil = require('ethereumjs-util'); +import ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { localStorage } from 'ts/local_storage/local_storage'; import { Fill } from 'ts/types'; diff --git a/packages/website/ts/pages/documentation/developers_page.tsx b/packages/website/ts/pages/documentation/developers_page.tsx index 361dbc86e..89389e488 100644 --- a/packages/website/ts/pages/documentation/developers_page.tsx +++ b/packages/website/ts/pages/documentation/developers_page.tsx @@ -1,7 +1,7 @@ import { colors, constants as sharedConstants, utils as sharedUtils } from '@0x/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import DocumentTitle = require('react-document-title'); +import DocumentTitle from 'react-document-title'; import { DocsLogo } from 'ts/components/documentation/docs_logo'; import { DocsTopBar } from 'ts/components/documentation/docs_top_bar'; import { Container } from 'ts/components/ui/container'; diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx index 28bf2dba1..8392c90e4 100644 --- a/packages/website/ts/pages/documentation/doc_page.tsx +++ b/packages/website/ts/pages/documentation/doc_page.tsx @@ -6,11 +6,11 @@ import { SupportedDocJson, TypeDocUtils, } from '@0x/react-docs'; -import findVersions = require('find-versions'); +import findVersions from 'find-versions'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; -import semverSort = require('semver-sort'); +import semverSort from 'semver-sort'; import { SidebarHeader } from 'ts/components/documentation/sidebar_header'; import { NestedSidebarMenu } from 'ts/components/nested_sidebar_menu'; import { Container } from 'ts/components/ui/container'; diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index c56ed4ebe..e2af40c8d 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -1,7 +1,7 @@ import { colors, Link } from '@0x/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import DocumentTitle = require('react-document-title'); +import DocumentTitle from 'react-document-title'; import { Footer } from 'ts/components/footer'; import { SubscribeForm } from 'ts/components/forms/subscribe_form'; import { TopBar } from 'ts/components/top_bar/top_bar'; diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index e0b883ace..6be164e6e 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -2,7 +2,7 @@ import { DocAgnosticFormat, GeneratedDocJson } from '@0x/react-docs'; import { fetchAsync, logUtils } from '@0x/utils'; import * as _ from 'lodash'; import { S3FileObject, VersionToFilePath } from 'ts/types'; -import convert = require('xml-js'); +import convert from 'xml-js'; export const docUtils = { async getVersionToFilePathAsync(s3DocJsonRoot: string, folderName: string): Promise<VersionToFilePath> { diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts index 6fc1216c3..8e24060ac 100644 --- a/packages/website/ts/utils/error_reporter.ts +++ b/packages/website/ts/utils/error_reporter.ts @@ -1,5 +1,5 @@ import { logUtils } from '@0x/utils'; -import Rollbar = require('rollbar'); +import Rollbar from 'rollbar'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; diff --git a/packages/website/ts/utils/translate.ts b/packages/website/ts/utils/translate.ts index 5595e6e0f..af5c766a9 100644 --- a/packages/website/ts/utils/translate.ts +++ b/packages/website/ts/utils/translate.ts @@ -1,11 +1,11 @@ import * as _ from 'lodash'; import { Deco, Key, Language } from 'ts/types'; -import * as chinese from '../../translations/chinese.json'; -import * as english from '../../translations/english.json'; -import * as korean from '../../translations/korean.json'; -import * as russian from '../../translations/russian.json'; -import * as spanish from '../../translations/spanish.json'; +import chinese from '../../translations/chinese.json'; +import english from '../../translations/english.json'; +import korean from '../../translations/korean.json'; +import russian from '../../translations/russian.json'; +import spanish from '../../translations/spanish.json'; const languageToTranslations = { [Language.English]: english, diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 87aa48018..8cc0061de 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -5,7 +5,7 @@ import { ExchangeContractErrs } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as bowser from 'bowser'; -import deepEqual = require('deep-equal'); +import deepEqual from 'deep-equal'; import * as _ from 'lodash'; import * as moment from 'moment'; import * as numeral from 'numeral'; diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json index 6421cd459..7d5f31b7f 100644 --- a/packages/website/tsconfig.json +++ b/packages/website/tsconfig.json @@ -16,7 +16,9 @@ "composite": false, "paths": { "*": ["node_modules/@types/*", "*"] - } + }, + "module": "esnext", + "moduleResolution": "node" }, "include": ["./ts/**/*"] } |