diff options
Diffstat (limited to 'packages/website/ts')
18 files changed, 272 insertions, 255 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index b13c48a65..fbe12ee75 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,59 +1,60 @@ -import * as _ from 'lodash'; -import * as React from 'react'; import { - ZeroEx, - ZeroExError, + BlockParam, + DecodedLogEvent, ExchangeContractErrs, ExchangeContractEventArgs, ExchangeEvents, - SubscriptionOpts, IndexedFilterValues, - DecodedLogEvent, - BlockParam, - LogFillContractEventArgs, LogCancelContractEventArgs, - Token as ZeroExToken, + LogFillContractEventArgs, LogWithDecodedArgs, - TransactionReceiptWithDecodedLogs, - SignedOrder, Order, + SignedOrder, + SubscriptionOpts, + Token as ZeroExToken, + TransactionReceiptWithDecodedLogs, + ZeroEx, + ZeroExError, } from '0x.js'; import BigNumber from 'bignumber.js'; -import Web3 = require('web3'); +import compareVersions = require('compare-versions'); import promisify = require('es6-promisify'); +import ethUtil = require('ethereumjs-util'); import findVersions = require('find-versions'); -import compareVersions = require('compare-versions'); +import * as _ from 'lodash'; +import * as React from 'react'; import contract = require('truffle-contract'); -import ethUtil = require('ethereumjs-util'); -import ProviderEngine = require('web3-provider-engine'); -import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); -import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed'; -import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; +import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; +import {Dispatcher} from 'ts/redux/dispatcher'; import {InjectedWeb3SubProvider} from 'ts/subproviders/injected_web3_subprovider'; import {ledgerWalletSubproviderFactory} from 'ts/subproviders/ledger_wallet_subprovider_factory'; -import {Dispatcher} from 'ts/redux/dispatcher'; -import {utils} from 'ts/utils/utils'; -import {constants} from 'ts/utils/constants'; -import {configs} from 'ts/utils/configs'; +import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; import { - BlockchainErrs, - Token, - SignatureData, - Side, - ContractResponse, BlockchainCallErrs, + BlockchainErrs, ContractInstance, - ProviderType, - LedgerWalletSubprovider, + ContractResponse, EtherscanLinkSuffixes, + LedgerWalletSubprovider, + ProviderType, + Side, + SignatureData, + Token, TokenByAddress, TokenStateByAddress, } from 'ts/types'; -import {Web3Wrapper} from 'ts/web3_wrapper'; +import {configs} from 'ts/utils/configs'; +import {constants} from 'ts/utils/constants'; import {errorReporter} from 'ts/utils/error_reporter'; -import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {utils} from 'ts/utils/utils'; +import {Web3Wrapper} from 'ts/web3_wrapper'; +import Web3 = require('web3'); +import ProviderEngine = require('web3-provider-engine'); +import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); + import * as MintableArtifacts from '../contracts/Mintable.json'; const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; @@ -72,9 +73,68 @@ export class Blockchain { private cachedProvider: Web3.Provider; private ledgerSubProvider: LedgerWalletSubprovider; private zrxPollIntervalId: number; + private static async onPageLoadAsync() { + if (document.readyState === 'complete') { + return; // Already loaded + } + return new Promise((resolve, reject) => { + window.onload = resolve; + }); + } + private static getNameGivenProvider(provider: Web3.Provider): string { + if (!_.isUndefined((provider as any).isMetaMask)) { + return constants.METAMASK_PROVIDER_NAME; + } + + // HACK: We use the fact that Parity Signer's provider is an instance of their + // internal `Web3FrameProvider` class. + const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); + if (isParitySigner) { + return constants.PARITY_SIGNER_PROVIDER_NAME; + } + + return constants.GENERIC_PROVIDER_NAME; + } + private static async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; + const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); + + let provider; + if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + // We catch all requests involving a users account and send it to the injectedWeb3 + // instance. All other requests go to the public hosted node. + provider = new ProviderEngine(); + provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); + provider.addProvider(new FilterSubprovider()); + provider.addProvider(new RedundantRPCSubprovider( + publicNodeUrlsIfExistsForNetworkId, + )); + provider.start(); + } else if (doesInjectedWeb3Exist) { + // Since no public node for this network, all requests go to injectedWeb3 instance + provider = injectedWeb3.currentProvider; + } else { + // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node + // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 + // injected into their browser. + provider = new ProviderEngine(); + provider.addProvider(new FilterSubprovider()); + const networkId = configs.isMainnetEnabled ? + constants.MAINNET_NETWORK_ID : + constants.TESTNET_NETWORK_ID; + provider.addProvider(new RedundantRPCSubprovider( + constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], + )); + provider.start(); + } + + return provider; + } constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { this.dispatcher = dispatcher; this.userAddress = ''; + // tslint:disable-next-line:no-floating-promises this.onPageLoadInitFireAndForgetAsync(); } public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) { @@ -216,7 +276,7 @@ export class Blockchain { signatureData: SignatureData, salt: BigNumber): SignedOrder { const ecSignature = signatureData; const exchangeContractAddress = this.getExchangeContractAddressIfExists(); - taker = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; + const takerOrNullAddress = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; const signedOrder = { ecSignature, exchangeContractAddress, @@ -227,7 +287,7 @@ export class Blockchain { makerTokenAddress, makerTokenAmount, salt, - taker, + taker: takerOrNullAddress, takerFee, takerTokenAddress, takerTokenAmount, @@ -273,51 +333,6 @@ export class Blockchain { public getExchangeContractAddressIfExists() { return this.exchangeAddress; } - public toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { - const ZeroExErrorToHumanReadableError: {[error: string]: string} = { - [ZeroExError.ContractDoesNotExist]: 'Contract does not exist', - [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', - [ZeroExError.UnhandledError]: ' Unhandled error occured', - [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', - [ZeroExError.InvalidSignature]: 'Order signature is not valid', - [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', - [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', - [ZeroExError.OutOfGas]: 'Transaction ran out of gas', - [ZeroExError.NoNetworkId]: 'No network id detected', - }; - const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { - [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', - [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', - [ExchangeContractErrs.OrderRemainingFillAmountZero]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', - [ExchangeContractErrs.InsufficientTakerBalance]: - 'Taker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientTakerAllowance]: - 'Taker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientMakerBalance]: - 'Maker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientMakerAllowance]: - 'Maker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientTakerFeeAllowance]: - 'Taker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeAllowance]: - 'Maker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: - `This order can only be filled by ${takerAddress}`, - [ExchangeContractErrs.InsufficientRemainingFillAmount]: - 'Insufficient remaining fill amount', - }; - const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || - ZeroExErrorToHumanReadableError[error]; - return humanReadableErrorMsg; - } public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, takerAddress: string): Promise<void> { @@ -446,6 +461,7 @@ export class Blockchain { public destroy() { clearInterval(this.zrxPollIntervalId); this.web3Wrapper.destroy(); + // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget } private async showEtherScanLinkAndAwaitTransactionMinedAsync( @@ -485,7 +501,7 @@ export class Blockchain { // Start a subscription for new logs const exchangeAddress = this.getExchangeContractAddressIfExists(); - const subscriptionId = await this.zeroEx.exchange.subscribeAsync( + const subscriptionId = this.zeroEx.exchange.subscribe( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => { const decodedLog = decodedLogEvent.log; @@ -493,7 +509,9 @@ export class Blockchain { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error // to rollbar and stop watching when one occurs + // tslint:disable-next-line:no-floating-promises errorReporter.reportAsync(err); // fire and forget + // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget return; } else { @@ -529,7 +547,7 @@ export class Blockchain { } } private async convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) { - const args = decodedLog.args as LogFillContractEventArgs; + const args = decodedLog.args; const blockTimestamp = await this.web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); const fill = { filledTakerTokenAmount: args.filledTakerTokenAmount, @@ -548,7 +566,7 @@ export class Blockchain { return fill; } private doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) { - const args = decodedLog.args as LogFillContractEventArgs; + const args = decodedLog.args; const isUserMakerOrTaker = args.maker === this.userAddress || args.taker === this.userAddress; return isUserMakerOrTaker; @@ -614,7 +632,7 @@ export class Blockchain { const provider = await this.getProviderAsync(injectedWeb3, networkId); this.zeroEx = new ZeroEx(provider); - await this.updateProviderName(injectedWeb3); + this.updateProviderName(injectedWeb3); const shouldPollUserAddress = true; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, networkId, shouldPollUserAddress); await this.postInstantiationOrUpdatingProviderZeroExAsync(); @@ -637,56 +655,6 @@ export class Blockchain { private getBlockchainNetworkId() { return this.networkId; } - private async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); - const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; - const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - - let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { - // We catch all requests involving a users account and send it to the injectedWeb3 - // instance. All other requests go to the public hosted node. - provider = new ProviderEngine(); - provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); - provider.addProvider(new FilterSubprovider()); - provider.addProvider(new RedundantRPCSubprovider( - publicNodeUrlsIfExistsForNetworkId, - )); - provider.start(); - } else if (doesInjectedWeb3Exist) { - // Since no public node for this network, all requests go to injectedWeb3 instance - provider = injectedWeb3.currentProvider; - } else { - // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node - // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 - // injected into their browser. - provider = new ProviderEngine(); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.isMainnetEnabled ? - constants.MAINNET_NETWORK_ID : - constants.TESTNET_NETWORK_ID; - provider.addProvider(new RedundantRPCSubprovider( - constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], - )); - provider.start(); - } - - return provider; - } - private getNameGivenProvider(provider: Web3.Provider): string { - if (!_.isUndefined((provider as any).isMetaMask)) { - return constants.METAMASK_PROVIDER_NAME; - } - - // HACK: We use the fact that Parity Signer's provider is an instance of their - // internal `Web3FrameProvider` class. - const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); - if (isParitySigner) { - return constants.PARITY_SIGNER_PROVIDER_NAME; - } - - return constants.GENERIC_PROVIDER_NAME; - } private async fetchTokenInformationAsync() { utils.assert(!_.isUndefined(this.networkId), 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node'); @@ -776,12 +744,4 @@ export class Blockchain { } } } - private async onPageLoadAsync() { - if (document.readyState === 'complete') { - return; // Already loaded - } - return new Promise((resolve, reject) => { - window.onload = resolve; - }); - } -} +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index dc965283e..0d652d33f 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -563,7 +563,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( signedOrder, takerFillAmount, this.props.userAddress); } catch (err) { - globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = this.props.blockchain.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); } } if (!_.isEmpty(globalErrMsg)) { @@ -652,7 +652,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync( signedOrder, availableTakerTokenAmount); } catch (err) { - globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = this.props.blockchain.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); } if (!_.isEmpty(globalErrMsg)) { this.setState({ diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts index ee449ecfd..c5b94dc45 100644 --- a/packages/website/ts/globals.d.ts +++ b/packages/website/ts/globals.d.ts @@ -132,6 +132,8 @@ declare class Subprovider {} declare module 'web3-provider-engine/subproviders/subprovider' { export = Subprovider; } + +// tslint:disable-next-line:max-classes-per-file declare class RpcSubprovider { constructor(options: {rpcUrl: string}); public handleRequest(payload: any, next: any, end: (err?: Error, data?: any) => void): void; @@ -139,6 +141,7 @@ declare class RpcSubprovider { declare module 'web3-provider-engine/subproviders/rpc' { export = RpcSubprovider; } +// tslint:disable-next-line:max-classes-per-file declare class HookedWalletSubprovider { constructor(wallet: any); } @@ -148,7 +151,9 @@ declare module 'web3-provider-engine/subproviders/hooked-wallet' { declare interface Artifact { abi: any; - networks: {[networkId: number]: { - address: string; - }}; + networks: { + [networkId: number]: { + address: string; + }; + }; } diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index 0b54a66e0..051a78ae1 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; -import {Token, TrackedTokensByNetworkId} from 'ts/types'; import {localStorage} from 'ts/local_storage/local_storage'; +import {Token, TrackedTokensByNetworkId} from 'ts/types'; const TRACKED_TOKENS_KEY = 'trackedTokens'; diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index 566ab8a01..a0a1da21b 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -1,20 +1,20 @@ +import BigNumber from 'bignumber.js'; import {Dispatch} from 'redux'; import {State} from 'ts/redux/reducer'; import { - Direction, - Side, + ActionTypes, AssetToken, BlockchainErrs, - Token, - SignatureData, + Direction, Fill, Order, - ActionTypes, - ScreenWidths, ProviderType, + ScreenWidths, + Side, + SignatureData, + Token, TokenStateByAddress, } from 'ts/types'; -import BigNumber from 'bignumber.js'; export class Dispatcher { private dispatch: Dispatch<State>; diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 7723597cd..da69a9d00 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -1,23 +1,23 @@ -import * as _ from 'lodash'; import {ZeroEx} from '0x.js'; import BigNumber from 'bignumber.js'; -import {utils} from 'ts/utils/utils'; +import * as _ from 'lodash'; import { + Action, + ActionTypes, + BlockchainErrs, + Direction, GenerateOrderSteps, + Order, + ProviderType, + ScreenWidths, Side, SideToAssetToken, - Direction, - BlockchainErrs, SignatureData, TokenByAddress, - TokenStateByAddress, - Order, - Action, - ActionTypes, - ScreenWidths, - ProviderType, TokenState, + TokenStateByAddress, } from 'ts/types'; +import {utils} from 'ts/utils/utils'; // Instead of defaulting the docs version to an empty string, we pre-populate it with // a valid version value. This does not need to be updated however, since onLoad, it @@ -54,7 +54,7 @@ export interface State { flashMessage: string|React.ReactNode; providerType: ProviderType; injectedProviderName: string; -}; +} const INITIAL_STATE: State = { // Portal diff --git a/packages/website/ts/schemas/validator.ts b/packages/website/ts/schemas/validator.ts index bf6ba4044..e8eb4aaf2 100644 --- a/packages/website/ts/schemas/validator.ts +++ b/packages/website/ts/schemas/validator.ts @@ -1,8 +1,8 @@ -import {Validator, Schema as JSONSchema} from 'jsonschema'; -import {signatureDataSchema} from 'ts/schemas/signature_data_schema'; +import {Schema as JSONSchema, Validator} from 'jsonschema'; import {orderSchema} from 'ts/schemas/order_schema'; -import {tokenSchema} from 'ts/schemas/token_schema'; import {orderTakerSchema} from 'ts/schemas/order_taker_schema'; +import {signatureDataSchema} from 'ts/schemas/signature_data_schema'; +import {tokenSchema} from 'ts/schemas/token_schema'; export class SchemaValidator { private validator: Validator; diff --git a/packages/website/ts/subproviders/injected_web3_subprovider.ts b/packages/website/ts/subproviders/injected_web3_subprovider.ts index b9e5af3ef..910fe3cdf 100644 --- a/packages/website/ts/subproviders/injected_web3_subprovider.ts +++ b/packages/website/ts/subproviders/injected_web3_subprovider.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; -import Web3 = require('web3'); import {constants} from 'ts/utils/constants'; +import Web3 = require('web3'); /* * This class implements the web3-provider-engine subprovider interface and forwards @@ -38,6 +38,7 @@ export class InjectedWeb3SubProvider { } } // Required to implement this method despite not needing it for this subprovider + // tslint:disable-next-line:prefer-function-over-method public setEngine(engine: any) { // noop } diff --git a/packages/website/ts/subproviders/ledger_wallet_subprovider_factory.ts b/packages/website/ts/subproviders/ledger_wallet_subprovider_factory.ts index df0c5a4db..bfabc90ae 100644 --- a/packages/website/ts/subproviders/ledger_wallet_subprovider_factory.ts +++ b/packages/website/ts/subproviders/ledger_wallet_subprovider_factory.ts @@ -1,11 +1,11 @@ -import * as _ from 'lodash'; -import Web3 = require('web3'); import * as EthereumTx from 'ethereumjs-tx'; import ethUtil = require('ethereumjs-util'); import * as ledger from 'ledgerco'; -import HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet'); -import {constants} from 'ts/utils/constants'; +import * as _ from 'lodash'; import {LedgerEthConnection, SignPersonalMessageParams, TxParams} from 'ts/types'; +import {constants} from 'ts/utils/constants'; +import Web3 = require('web3'); +import HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet'); const NUM_ADDRESSES_TO_FETCH = 10; const ASK_FOR_ON_DEVICE_CONFIRMATION = false; diff --git a/packages/website/ts/subproviders/redundant_rpc_subprovider.ts b/packages/website/ts/subproviders/redundant_rpc_subprovider.ts index a6c53ebd1..74675d910 100644 --- a/packages/website/ts/subproviders/redundant_rpc_subprovider.ts +++ b/packages/website/ts/subproviders/redundant_rpc_subprovider.ts @@ -1,11 +1,26 @@ +import promisify = require('es6-promisify'); import * as _ from 'lodash'; import {JSONRPCPayload} from 'ts/types'; -import promisify = require('es6-promisify'); -import Subprovider = require('web3-provider-engine/subproviders/subprovider'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); +import Subprovider = require('web3-provider-engine/subproviders/subprovider'); export class RedundantRPCSubprovider extends Subprovider { private rpcs: RpcSubprovider[]; + private static async firstSuccessAsync( + rpcs: RpcSubprovider[], payload: JSONRPCPayload, next: () => void, + ): Promise<any> { + let lastErr; + for (const rpc of rpcs) { + try { + const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next); + return data; + } catch (err) { + lastErr = err; + continue; + } + } + throw Error(lastErr); + } constructor(endpoints: string[]) { super(); this.rpcs = _.map(endpoints, endpoint => { @@ -25,17 +40,4 @@ export class RedundantRPCSubprovider extends Subprovider { } } - private async firstSuccessAsync(rpcs: RpcSubprovider[], payload: JSONRPCPayload, next: () => void): Promise<any> { - let lastErr; - for (const rpc of rpcs) { - try { - const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next); - return data; - } catch (err) { - lastErr = err; - continue; - } - } - throw Error(lastErr); - } } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2d0103499..403af9e78 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,5 +1,5 @@ -import * as _ from 'lodash'; import BigNumber from 'bignumber.js'; +import * as _ from 'lodash'; // Utility function to create a K:V from a list of strings // Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html @@ -16,7 +16,7 @@ export enum GenerateOrderSteps { RemainingConfigs, SignTransaction, CopyAndShare, -}; +} export const Side = strEnum([ 'receive', @@ -45,11 +45,11 @@ export interface Token { decimals: number; isTracked: boolean; isRegistered: boolean; -}; +} export interface TokenByAddress { [address: string]: Token; -}; +} export interface TokenState { allowance: BigNumber; @@ -58,7 +58,7 @@ export interface TokenState { export interface TokenStateByAddress { [address: string]: TokenState; -}; +} export interface AssetToken { address?: string; @@ -67,14 +67,14 @@ export interface AssetToken { export interface SideToAssetToken { [side: string]: AssetToken; -}; +} export interface SignatureData { hash: string; r: string; s: string; v: number; -}; +} export interface HashData { depositAmount: BigNumber; @@ -138,7 +138,7 @@ export enum BalanceErrs { wethConversionFailed, sendFailed, allowanceSettingFailed, -}; +} export const ActionTypes = strEnum([ // Portal @@ -535,7 +535,7 @@ interface LedgerSignResult { s: string; } interface LedgerCommunication { - close_async: () => void; + close_async: () => Promise<void>; } export interface LedgerEthConnection { getAddress_async: (derivationPath: string, askForDeviceConfirmation: boolean, @@ -566,7 +566,7 @@ export interface TxParams { export interface PublicNodeUrlsByNetworkId { [networkId: number]: string[]; -}; +} export interface JSONRPCPayload { params: any[]; @@ -689,4 +689,4 @@ export enum WebsitePaths { About = '/about', Whitepaper = '/pdfs/0x_white_paper.pdf', SmartContracts = '/docs/contracts', -} +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 7fc52b035..77a6e3e38 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -1,14 +1,14 @@ +import BigNumber from 'bignumber.js'; import { + ContractAddresses, + Docs, ExchangeContractErrs, + Networks, PublicNodeUrlsByNetworkId, - ZeroExJsDocSections, SmartContractsDocSections, - Docs, - ContractAddresses, - Networks, WebsitePaths, + ZeroExJsDocSections, } from 'ts/types'; -import BigNumber from 'bignumber.js'; const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs'; diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index ca6e0dc52..594e3bae6 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -1,9 +1,9 @@ -import * as _ from 'lodash'; import findVersions = require('find-versions'); -import convert = require('xml-js'); +import * as _ from 'lodash'; +import {DoxityDocObj, S3FileObject, TypeDocNode, VersionToFileName} from 'ts/types'; import {constants} from 'ts/utils/constants'; import {utils} from 'ts/utils/utils'; -import {VersionToFileName, S3FileObject, TypeDocNode, DoxityDocObj} from 'ts/types'; +import convert = require('xml-js'); export const docUtils = { async getVersionToFileNameAsync(s3DocJsonRoot: string): @@ -29,12 +29,10 @@ export const docUtils = { compact: true, }); const responseObj = JSON.parse(responseJSONString); - let fileObjs: S3FileObject[]; - if (_.isArray(responseObj.ListBucketResult.Contents)) { - fileObjs = responseObj.ListBucketResult.Contents as S3FileObject[]; - } else { - fileObjs = [responseObj.ListBucketResult.Contents]; - } + const fileObjs: S3FileObject[] = (_.isArray(responseObj.ListBucketResult.Contents)) ? + responseObj.ListBucketResult.Contents as S3FileObject[] : + [responseObj.ListBucketResult.Contents]; + const versionFileNames = _.map(fileObjs, fileObj => { return fileObj.Key._text; }); diff --git a/packages/website/ts/utils/doxity_utils.ts b/packages/website/ts/utils/doxity_utils.ts index 3bab0a69d..26e555b16 100644 --- a/packages/website/ts/utils/doxity_utils.ts +++ b/packages/website/ts/utils/doxity_utils.ts @@ -1,18 +1,18 @@ import * as _ from 'lodash'; import { - DoxityDocObj, - DoxityContractObj, - DoxityAbiDoc, - DoxityInput, + AbiTypes, DocAgnosticFormat, DocSection, + DoxityAbiDoc, + DoxityContractObj, + DoxityDocObj, + DoxityInput, + EventArg, Parameter, Property, + SolidityMethod, Type, TypeDocTypes, - EventArg, - AbiTypes, - SolidityMethod, } from 'ts/types'; export const doxityUtils = { diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts index a9731c4d4..40991afbf 100644 --- a/packages/website/ts/utils/error_reporter.ts +++ b/packages/website/ts/utils/error_reporter.ts @@ -1,7 +1,7 @@ -import {utils} from 'ts/utils/utils'; -import {constants} from 'ts/utils/constants'; -import {configs} from 'ts/utils/configs'; import {Environments} from 'ts/types'; +import {configs} from 'ts/utils/configs'; +import {constants} from 'ts/utils/constants'; +import {utils} from 'ts/utils/utils'; // Suggested way to include Rollbar with Webpack // https://github.com/rollbar/rollbar.js/tree/master/examples/webpack @@ -32,7 +32,7 @@ import Rollbar = require('../../public/js/rollbar.umd.nojson.min.js'); const rollbar = Rollbar.init(rollbarConfig); export const errorReporter = { - reportAsync(err: Error): Promise<any> { + async reportAsync(err: Error): Promise<any> { if (configs.ENVIRONMENT === Environments.DEVELOPMENT) { return; // Let's not log development errors to rollbar } diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts index b3d0f7d90..bb2f7745a 100644 --- a/packages/website/ts/utils/typedoc_utils.ts +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -1,25 +1,25 @@ -import * as _ from 'lodash'; import compareVersions = require('compare-versions'); -import {constants} from 'ts/utils/constants'; -import {utils} from 'ts/utils/utils'; +import * as _ from 'lodash'; import { - TypeDocNode, - KindString, - ZeroExJsDocSections, - MenuSubsectionsBySection, - TypeDocType, - Type, + CustomType, + CustomTypeChild, DocAgnosticFormat, DocSection, - TypescriptMethod, + IndexSignature, + KindString, + MenuSubsectionsBySection, Parameter, Property, - CustomType, - IndexSignature, - CustomTypeChild, - TypeParameter, + Type, + TypeDocNode, + TypeDocType, TypeDocTypes, + TypeParameter, + TypescriptMethod, + ZeroExJsDocSections, } from 'ts/types'; +import {constants} from 'ts/utils/constants'; +import {utils} from 'ts/utils/utils'; const TYPES_MODULE_PATH = '"src/types"'; diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index eb4c5be3a..8b23b6a40 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -1,23 +1,24 @@ +import {ExchangeContractErrs, ZeroExError} from '0x.js'; +import BigNumber from 'bignumber.js'; +import deepEqual = require('deep-equal'); +import ethUtil = require('ethereumjs-util'); +import isMobile = require('is-mobile'); import * as _ from 'lodash'; +import * as moment from 'moment'; import { - SideToAssetToken, - SignatureData, + EtherscanLinkSuffixes, + Networks, Order, - Side, - TokenByAddress, OrderParty, ScreenWidths, - EtherscanLinkSuffixes, + Side, + SideToAssetToken, + SignatureData, Token, - Networks, + TokenByAddress, } from 'ts/types'; -import * as moment from 'moment'; -import isMobile = require('is-mobile'); -import * as u2f from 'ts/vendor/u2f_api'; -import deepEqual = require('deep-equal'); -import ethUtil = require('ethereumjs-util'); -import BigNumber from 'bignumber.js'; import {constants} from 'ts/utils/constants'; +import * as u2f from 'ts/vendor/u2f_api'; const LG_MIN_EM = 64; const MD_MIN_EM = 52; @@ -101,7 +102,7 @@ export const utils = { console.log(message); /* tslint:enable */ }, - sleepAsync(ms: number) { + async sleepAsync(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); }, deepEqual(actual: any, expected: any, opts?: {strict: boolean}) { @@ -212,4 +213,53 @@ export const utils = { const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists); return isUniqueName && isUniqueSymbol; }, + zeroExErrToHumanReadableErrMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { + const ZeroExErrorToHumanReadableError: {[error: string]: string} = { + [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', + [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist', + [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist', + [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist', + [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist', + [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', + [ZeroExError.UnhandledError]: 'Unhandled error occured', + [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', + [ZeroExError.InvalidSignature]: 'Order signature is not valid', + [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', + [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', + [ZeroExError.OutOfGas]: 'Transaction ran out of gas', + [ZeroExError.NoNetworkId]: 'No network id detected', + }; + const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { + [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', + [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', + [ExchangeContractErrs.OrderRemainingFillAmountZero]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', + [ExchangeContractErrs.InsufficientTakerBalance]: + 'Taker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientTakerAllowance]: + 'Taker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientMakerBalance]: + 'Maker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientMakerAllowance]: + 'Maker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientTakerFeeAllowance]: + 'Taker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeAllowance]: + 'Maker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: + `This order can only be filled by ${takerAddress}`, + [ExchangeContractErrs.InsufficientRemainingFillAmount]: + 'Insufficient remaining fill amount', + }; + const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || + ZeroExErrorToHumanReadableError[error]; + return humanReadableErrorMsg; + }, }; diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 24279f5d2..c43436c7e 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -1,8 +1,8 @@ -import * as _ from 'lodash'; -import Web3 = require('web3'); import BigNumber from 'bignumber.js'; import promisify = require('es6-promisify'); +import * as _ from 'lodash'; import {Dispatcher} from 'ts/redux/dispatcher'; +import Web3 = require('web3'); export class Web3Wrapper { private dispatcher: Dispatcher; @@ -21,6 +21,7 @@ export class Web3Wrapper { this.web3 = new Web3(); this.web3.setProvider(provider); + // tslint:disable-next-line:no-floating-promises this.startEmittingNetworkConnectionAndUserBalanceStateAsync(); } public isAddress(address: string) { @@ -35,7 +36,7 @@ export class Web3Wrapper { if (_.isEmpty(addresses)) { return ''; } - return (addresses as string[])[0]; + return (addresses)[0]; } public async getNodeVersionAsync() { const nodeVersion = await promisify(this.web3.version.getNode)(); |