aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts/blockchain.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts/blockchain.ts')
-rw-r--r--packages/website/ts/blockchain.ts321
1 files changed, 155 insertions, 166 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index 7851ea8ff..b13c48a65 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -1,60 +1,59 @@
+import * as _ from 'lodash';
+import * as React from 'react';
import {
- BlockParam,
- DecodedLogEvent,
+ ZeroEx,
+ ZeroExError,
ExchangeContractErrs,
ExchangeContractEventArgs,
ExchangeEvents,
+ SubscriptionOpts,
IndexedFilterValues,
- LogCancelContractEventArgs,
+ DecodedLogEvent,
+ BlockParam,
LogFillContractEventArgs,
- LogWithDecodedArgs,
- Order,
- SignedOrder,
- SubscriptionOpts,
+ LogCancelContractEventArgs,
Token as ZeroExToken,
+ LogWithDecodedArgs,
TransactionReceiptWithDecodedLogs,
- ZeroEx,
- ZeroExError,
+ SignedOrder,
+ Order,
} from '0x.js';
import BigNumber from 'bignumber.js';
-import compareVersions = require('compare-versions');
+import Web3 = require('web3');
import promisify = require('es6-promisify');
-import ethUtil = require('ethereumjs-util');
import findVersions = require('find-versions');
-import * as _ from 'lodash';
-import * as React from 'react';
+import compareVersions = require('compare-versions');
import contract = require('truffle-contract');
-import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed';
+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 {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage';
-import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage';
-import {Dispatcher} from 'ts/redux/dispatcher';
+import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed';
+import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider';
import {InjectedWeb3SubProvider} from 'ts/subproviders/injected_web3_subprovider';
import {ledgerWalletSubproviderFactory} from 'ts/subproviders/ledger_wallet_subprovider_factory';
-import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider';
+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 {
- BlockchainCallErrs,
BlockchainErrs,
- ContractInstance,
+ Token,
+ SignatureData,
+ Side,
ContractResponse,
- EtherscanLinkSuffixes,
- LedgerWalletSubprovider,
+ BlockchainCallErrs,
+ ContractInstance,
ProviderType,
- Side,
- SignatureData,
- Token,
+ LedgerWalletSubprovider,
+ EtherscanLinkSuffixes,
TokenByAddress,
TokenStateByAddress,
} from 'ts/types';
-import {configs} from 'ts/utils/configs';
-import {constants} from 'ts/utils/constants';
-import {errorReporter} from 'ts/utils/error_reporter';
-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 {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 * as MintableArtifacts from '../contracts/Mintable.json';
const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730;
@@ -73,116 +72,9 @@ export class Blockchain {
private cachedProvider: Web3.Provider;
private ledgerSubProvider: LedgerWalletSubprovider;
private zrxPollIntervalId: number;
- public static toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string {
- const ZeroExErrorToHumanReadableError: {[error: string]: string} = {
- [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'Token transfer proxy contract does not exist',
- [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist',
- [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist',
- [ZeroExError.EtherTokenContractDoesNotExist]: 'Ether token 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;
- }
- private static async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise<Web3.Provider> {
- 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 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 onPageLoadAsync() {
- if (document.readyState === 'complete') {
- return; // Already loaded
- }
- return new Promise((resolve, reject) => {
- window.onload = resolve;
- });
- }
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) {
@@ -265,8 +157,8 @@ export class Blockchain {
this.web3Wrapper.destroy();
const shouldPollUserAddress = false;
this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress);
- this.zeroEx.setProvider(provider, this.networkId);
- this.postInstantiationOrUpdatingProviderZeroEx();
+ await this.zeroEx.setProviderAsync(provider);
+ await this.postInstantiationOrUpdatingProviderZeroExAsync();
break;
}
@@ -277,8 +169,8 @@ export class Blockchain {
provider = this.cachedProvider;
const shouldPollUserAddress = true;
this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress);
- this.zeroEx.setProvider(provider, this.networkId);
- this.postInstantiationOrUpdatingProviderZeroEx();
+ await this.zeroEx.setProviderAsync(provider);
+ await this.postInstantiationOrUpdatingProviderZeroExAsync();
delete this.ledgerSubProvider;
delete this.cachedProvider;
break;
@@ -316,7 +208,7 @@ export class Blockchain {
amountInBaseUnits,
}));
}
- public portalOrderToSignedOrder(maker: string, takerIfExists: string, makerTokenAddress: string,
+ public portalOrderToSignedOrder(maker: string, taker: string, makerTokenAddress: string,
takerTokenAddress: string, makerTokenAmount: BigNumber,
takerTokenAmount: BigNumber, makerFee: BigNumber,
takerFee: BigNumber, expirationUnixTimestampSec: BigNumber,
@@ -324,7 +216,7 @@ export class Blockchain {
signatureData: SignatureData, salt: BigNumber): SignedOrder {
const ecSignature = signatureData;
const exchangeContractAddress = this.getExchangeContractAddressIfExists();
- const taker = _.isEmpty(takerIfExists) ? constants.NULL_ADDRESS : takerIfExists;
+ taker = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker;
const signedOrder = {
ecSignature,
exchangeContractAddress,
@@ -381,6 +273,51 @@ 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> {
@@ -509,7 +446,6 @@ 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(
@@ -549,7 +485,7 @@ export class Blockchain {
// Start a subscription for new logs
const exchangeAddress = this.getExchangeContractAddressIfExists();
- const subscriptionId = this.zeroEx.exchange.subscribe(
+ const subscriptionId = await this.zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, indexFilterValues,
async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
const decodedLog = decodedLogEvent.log;
@@ -557,9 +493,7 @@ 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 {
@@ -595,7 +529,7 @@ export class Blockchain {
}
}
private async convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
- const args = decodedLog.args;
+ const args = decodedLog.args as LogFillContractEventArgs;
const blockTimestamp = await this.web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash);
const fill = {
filledTakerTokenAmount: args.filledTakerTokenAmount,
@@ -614,7 +548,7 @@ export class Blockchain {
return fill;
}
private doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
- const args = decodedLog.args;
+ const args = decodedLog.args as LogFillContractEventArgs;
const isUserMakerOrTaker = args.maker === this.userAddress ||
args.taker === this.userAddress;
return isUserMakerOrTaker;
@@ -660,7 +594,7 @@ export class Blockchain {
return tokenByAddress;
}
private async onPageLoadInitFireAndForgetAsync() {
- await Blockchain.onPageLoadAsync(); // wait for page to load
+ await this.onPageLoadAsync(); // wait for page to load
// Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
// order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot
@@ -678,34 +612,81 @@ export class Blockchain {
}
}
- const provider = await Blockchain.getProviderAsync(injectedWeb3, networkId);
- const zeroExConfig = {
- networkId: this.networkId,
- };
- this.zeroEx = new ZeroEx(provider, zeroExConfig);
- this.updateProviderName(injectedWeb3);
+ const provider = await this.getProviderAsync(injectedWeb3, networkId);
+ this.zeroEx = new ZeroEx(provider);
+ await this.updateProviderName(injectedWeb3);
const shouldPollUserAddress = true;
this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, networkId, shouldPollUserAddress);
- this.postInstantiationOrUpdatingProviderZeroEx();
+ await this.postInstantiationOrUpdatingProviderZeroExAsync();
}
// This method should always be run after instantiating or updating the provider
// of the ZeroEx instance.
- private postInstantiationOrUpdatingProviderZeroEx(): void {
+ private async postInstantiationOrUpdatingProviderZeroExAsync() {
utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.');
- this.exchangeAddress = this.zeroEx.exchange.getContractAddress();
+ this.exchangeAddress = await this.zeroEx.exchange.getContractAddressAsync();
}
- private updateProviderName(injectedWeb3: Web3): void {
+ private updateProviderName(injectedWeb3: Web3) {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
const providerName = doesInjectedWeb3Exist ?
- Blockchain.getNameGivenProvider(injectedWeb3.currentProvider) :
+ this.getNameGivenProvider(injectedWeb3.currentProvider) :
constants.PUBLIC_PROVIDER_NAME;
this.dispatcher.updateInjectedProviderName(providerName);
}
// This is only ever called by the LedgerWallet subprovider in order to retrieve
// the current networkId without this value going stale.
- private getBlockchainNetworkId(): number {
+ 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');
@@ -795,4 +776,12 @@ export class Blockchain {
}
}
}
-} // tslint:disable:max-file-line-count
+ private async onPageLoadAsync() {
+ if (document.readyState === 'complete') {
+ return; // Already loaded
+ }
+ return new Promise((resolve, reject) => {
+ window.onload = resolve;
+ });
+ }
+}