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.ts177
1 files changed, 111 insertions, 66 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index fca9504d7..14582eae9 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -24,9 +24,11 @@ import {
RedundantRPCSubprovider,
} from '@0xproject/subproviders';
import { BigNumber, intervalUtils, promisify } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import * as React from 'react';
import contract = require('truffle-contract');
+import { BlockchainWatcher } from 'ts/blockchain_watcher';
import { TokenSendCompleted } from 'ts/components/flash_messages/token_send_completed';
import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted';
import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
@@ -47,7 +49,6 @@ 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');
@@ -63,8 +64,8 @@ export class Blockchain {
private _zeroEx: ZeroEx;
private _dispatcher: Dispatcher;
private _web3Wrapper?: Web3Wrapper;
- private _exchangeAddress: string;
- private _userAddress: string;
+ private _blockchainWatcher?: BlockchainWatcher;
+ private _userAddressIfExists: string;
private _cachedProvider: Web3.Provider;
private _cachedProviderNetworkId: number;
private _ledgerSubprovider: LedgerWalletSubprovider;
@@ -115,7 +116,6 @@ export class Blockchain {
}
constructor(dispatcher: Dispatcher, isSalePage: boolean = false) {
this._dispatcher = dispatcher;
- this._userAddress = '';
const defaultGasPrice = GWEI_IN_WEI * 30;
this._defaultGasPrice = new BigNumber(defaultGasPrice);
// tslint:disable-next-line:no-floating-promises
@@ -137,8 +137,8 @@ export class Blockchain {
}
}
public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) {
- if (this._userAddress !== newUserAddress) {
- this._userAddress = newUserAddress;
+ if (this._userAddressIfExists !== newUserAddress) {
+ this._userAddressIfExists = newUserAddress;
await this.fetchTokenInformationAsync();
await this._rehydrateStoreWithContractEvents();
}
@@ -189,14 +189,14 @@ export class Blockchain {
// Cache injected provider so that we can switch the user back to it easily
if (_.isUndefined(this._cachedProvider)) {
- this._cachedProvider = this._web3Wrapper.getProviderObj();
+ this._cachedProvider = this._web3Wrapper.getProvider();
this._cachedProviderNetworkId = this.networkId;
}
- this._web3Wrapper.destroy();
+ this._blockchainWatcher.destroy();
- this._userAddress = '';
- this._dispatcher.updateUserAddress(''); // Clear old userAddress
+ delete this._userAddressIfExists;
+ this._dispatcher.updateUserAddress(undefined); // Clear old userAddress
const provider = new ProviderEngine();
const ledgerWalletConfigs = {
@@ -211,10 +211,15 @@ export class Blockchain {
this.networkId = networkId;
this._dispatcher.updateNetworkId(this.networkId);
const shouldPollUserAddress = false;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._blockchainWatcher = new BlockchainWatcher(
+ this._dispatcher,
+ this._web3Wrapper,
+ this.networkId,
+ shouldPollUserAddress,
+ );
this._zeroEx.setProvider(provider, this.networkId);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
- this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState();
+ this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState();
this._dispatcher.updateProviderType(ProviderType.Ledger);
}
public async updateProviderToInjectedAsync() {
@@ -224,21 +229,27 @@ export class Blockchain {
return; // Going from injected to injected, so we noop
}
- this._web3Wrapper.destroy();
+ this._blockchainWatcher.destroy();
const provider = this._cachedProvider;
this.networkId = this._cachedProviderNetworkId;
const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._blockchainWatcher = new BlockchainWatcher(
+ this._dispatcher,
+ this._web3Wrapper,
+ this.networkId,
+ shouldPollUserAddress,
+ );
- this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync();
+ const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ this._userAddressIfExists = userAddresses[0];
this._zeroEx.setProvider(provider, this.networkId);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
await this.fetchTokenInformationAsync();
- this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState();
+ this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState();
this._dispatcher.updateProviderType(ProviderType.Injected);
delete this._ledgerSubprovider;
delete this._cachedProvider;
@@ -251,7 +262,7 @@ export class Blockchain {
this._showFlashMessageIfLedger();
const txHash = await this._zeroEx.token.setProxyAllowanceAsync(
token.address,
- this._userAddress,
+ this._userAddressIfExists,
amountInBaseUnits,
{
gasPrice: this._defaultGasPrice,
@@ -260,10 +271,13 @@ export class Blockchain {
await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
}
public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise<void> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
this._showFlashMessageIfLedger();
const txHash = await this._zeroEx.token.transferAsync(
token.address,
- this._userAddress,
+ this._userAddressIfExists,
toAddress,
amountInBaseUnits,
{
@@ -305,6 +319,7 @@ export class Blockchain {
return zeroExSignedOrder;
}
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise<BigNumber> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
const shouldThrowOnInsufficientBalanceOrAllowance = true;
@@ -314,7 +329,7 @@ export class Blockchain {
signedOrder,
fillTakerTokenAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
- this._userAddress,
+ this._userAddressIfExists,
{
gasPrice: this._defaultGasPrice,
},
@@ -347,7 +362,7 @@ export class Blockchain {
return unavailableTakerAmount;
}
public getExchangeContractAddressIfExists() {
- return this._exchangeAddress;
+ return this._zeroEx.exchange.getContractAddress();
}
public async validateFillOrderThrowIfInvalidAsync(
signedOrder: SignedOrder,
@@ -373,12 +388,15 @@ export class Blockchain {
public async pollTokenBalanceAsync(token: Token) {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
- const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+ const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddressIfExists, token.address);
const newTokenBalancePromise = new Promise((resolve: (balance: BigNumber) => void, reject) => {
const tokenPollInterval = intervalUtils.setAsyncExcludingInterval(
async () => {
- const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+ const [balance] = await this.getTokenBalanceAndAllowanceAsync(
+ this._userAddressIfExists,
+ token.address,
+ );
if (!balance.eq(currBalance)) {
intervalUtils.clearAsyncExcludingInterval(tokenPollInterval);
resolve(balance);
@@ -397,7 +415,7 @@ export class Blockchain {
}
public async signOrderHashAsync(orderHash: string): Promise<ECSignature> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- const makerAddress = this._userAddress;
+ const makerAddress = this._userAddressIfExists;
// If makerAddress is undefined, this means they have a web3 instance injected into their browser
// but no account addresses associated with it.
if (_.isUndefined(makerAddress)) {
@@ -427,22 +445,27 @@ export class Blockchain {
const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address);
this._showFlashMessageIfLedger();
await mintableContract.mint(constants.MINT_AMOUNT, {
- from: this._userAddress,
+ from: this._userAddressIfExists,
gasPrice: this._defaultGasPrice,
});
}
- public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
- const balance = await this._web3Wrapper.getBalanceInEthAsync(owner);
- return balance;
+ public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> {
+ const balanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(owner);
+ return balanceInWei;
}
public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
this._showFlashMessageIfLedger();
- const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress, {
- gasPrice: this._defaultGasPrice,
- });
+ const txHash = await this._zeroEx.etherToken.depositAsync(
+ etherTokenAddress,
+ amount,
+ this._userAddressIfExists,
+ {
+ gasPrice: this._defaultGasPrice,
+ },
+ );
await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
}
public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
@@ -450,9 +473,14 @@ export class Blockchain {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
this._showFlashMessageIfLedger();
- const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress, {
- gasPrice: this._defaultGasPrice,
- });
+ const txHash = await this._zeroEx.etherToken.withdrawAsync(
+ etherTokenAddress,
+ amount,
+ this._userAddressIfExists,
+ {
+ gasPrice: this._defaultGasPrice,
+ },
+ );
await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
}
public async doesContractExistAtAddressAsync(address: string) {
@@ -460,21 +488,29 @@ export class Blockchain {
return doesContractExist;
}
public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise<BigNumber[]> {
- const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, tokenAddress);
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(
+ this._userAddressIfExists,
+ tokenAddress,
+ );
return tokenBalanceAndAllowance;
}
- public async getTokenBalanceAndAllowanceAsync(ownerAddress: string, tokenAddress: string): Promise<BigNumber[]> {
+ public async getTokenBalanceAndAllowanceAsync(
+ ownerAddressIfExists: string,
+ tokenAddress: string,
+ ): Promise<BigNumber[]> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- if (_.isEmpty(ownerAddress)) {
+ if (_.isUndefined(ownerAddressIfExists)) {
const zero = new BigNumber(0);
return [zero, zero];
}
let balance = new BigNumber(0);
let allowance = new BigNumber(0);
if (this._doesUserAddressExist()) {
- balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddress);
- allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddress);
+ balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddressIfExists);
+ allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddressIfExists);
}
return [balance, allowance];
}
@@ -487,10 +523,10 @@ export class Blockchain {
// by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to
// manually update it. This should only be called by the LedgerConfigDialog.
public updateWeb3WrapperPrevUserAddress(newUserAddress: string) {
- this._web3Wrapper.updatePrevUserAddress(newUserAddress);
+ this._blockchainWatcher.updatePrevUserAddress(newUserAddress);
}
public destroy() {
- this._web3Wrapper.destroy();
+ this._blockchainWatcher.destroy();
this._stopWatchingExchangeLogFillEvents();
}
public async fetchTokenInformationAsync() {
@@ -503,7 +539,9 @@ export class Blockchain {
const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync();
- const trackedTokensByAddress = trackedTokenStorage.getTrackedTokensByAddress(this._userAddress, this.networkId);
+ const trackedTokensByAddress = _.isUndefined(this._userAddressIfExists)
+ ? {}
+ : trackedTokenStorage.getTrackedTokensByAddress(this._userAddressIfExists, this.networkId);
const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress);
if (_.isEmpty(trackedTokensByAddress)) {
_.each(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => {
@@ -511,9 +549,11 @@ export class Blockchain {
token.isTracked = true;
trackedTokensByAddress[token.address] = token;
});
- _.each(trackedTokensByAddress, (token: Token, address: string) => {
- trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token);
- });
+ if (!_.isUndefined(this._userAddressIfExists)) {
+ _.each(trackedTokensByAddress, (token: Token, address: string) => {
+ trackedTokenStorage.addTrackedTokenToUser(this._userAddressIfExists, this.networkId, token);
+ });
+ }
} else {
// Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array
_.each(trackedTokensByAddress, (trackedToken: Token, address: string) => {
@@ -539,7 +579,7 @@ export class Blockchain {
address: mostPopularTradingPairTokens[1].address,
},
};
- this._dispatcher.batchDispatch(allTokensByAddress, this.networkId, this._userAddress, sideToAssetToken);
+ this._dispatcher.batchDispatch(allTokensByAddress, this.networkId, this._userAddressIfExists, sideToAssetToken);
this._dispatcher.updateBlockchainIsLoaded(true);
}
@@ -560,7 +600,7 @@ export class Blockchain {
return receipt;
}
private _doesUserAddressExist(): boolean {
- return this._userAddress !== '';
+ return !_.isUndefined(this._userAddressIfExists);
}
private async _rehydrateStoreWithContractEvents() {
// Ensure we are only ever listening to one set of events
@@ -605,16 +645,18 @@ export class Blockchain {
this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
const fill = await this._convertDecodedLogToFillAsync(decodedLog);
if (decodedLogEvent.isRemoved) {
- tradeHistoryStorage.removeFillFromUser(this._userAddress, this.networkId, fill);
+ tradeHistoryStorage.removeFillFromUser(this._userAddressIfExists, this.networkId, fill);
} else {
- tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
+ tradeHistoryStorage.addFillToUser(this._userAddressIfExists, this.networkId, fill);
}
}
},
);
}
private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) {
- const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddress, this.networkId);
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddressIfExists, this.networkId);
const blockRange: BlockRange = {
fromBlock,
toBlock: 'latest' as BlockParam,
@@ -630,7 +672,7 @@ export class Blockchain {
}
this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
const fill = await this._convertDecodedLogToFillAsync(decodedLog);
- tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
+ tradeHistoryStorage.addFillToUser(this._userAddressIfExists, this.networkId, fill);
}
}
private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
@@ -654,10 +696,12 @@ export class Blockchain {
}
private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
const args = decodedLog.args;
- const isUserMakerOrTaker = args.maker === this._userAddress || args.taker === this._userAddress;
+ const isUserMakerOrTaker = args.maker === this._userAddressIfExists || args.taker === this._userAddressIfExists;
return isUserMakerOrTaker;
}
private _updateLatestFillsBlockIfNeeded(blockNumber: number) {
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
const isBlockPending = _.isNull(blockNumber);
if (!isBlockPending) {
// Hack: I've observed the behavior where a client won't register certain fill events
@@ -668,7 +712,7 @@ export class Blockchain {
// TODO: Debug if this is a race condition, and apply a more precise fix
const blockNumberToSet =
blockNumber - BLOCK_NUMBER_BACK_TRACK < 0 ? 0 : blockNumber - BLOCK_NUMBER_BACK_TRACK;
- tradeHistoryStorage.setFillsLatestBlock(this._userAddress, this.networkId, blockNumberToSet);
+ tradeHistoryStorage.setFillsLatestBlock(this._userAddressIfExists, this.networkId, blockNumberToSet);
}
}
private _stopWatchingExchangeLogFillEvents(): void {
@@ -739,20 +783,21 @@ export class Blockchain {
this._zeroEx = new ZeroEx(provider, zeroExConfigs);
this._updateProviderName(injectedWeb3);
const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
- this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync();
- this._dispatcher.updateUserAddress(this._userAddress);
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._blockchainWatcher = new BlockchainWatcher(
+ this._dispatcher,
+ this._web3Wrapper,
+ this.networkId,
+ shouldPollUserAddress,
+ );
+
+ const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ this._userAddressIfExists = userAddresses[0];
+ this._dispatcher.updateUserAddress(this._userAddressIfExists);
await this.fetchTokenInformationAsync();
- this._web3Wrapper.startEmittingNetworkConnectionAndUserBalanceState();
+ this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState();
await this._rehydrateStoreWithContractEvents();
}
- // This method should always be run after instantiating or updating the provider
- // of the ZeroEx instance.
- private async _postInstantiationOrUpdatingProviderZeroExAsync() {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- this._exchangeAddress = this._zeroEx.exchange.getContractAddress();
- }
private _updateProviderName(injectedWeb3: Web3) {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
const providerName = doesInjectedWeb3Exist
@@ -762,7 +807,7 @@ export class Blockchain {
}
private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise<ContractInstance> {
const c = await contract(artifact);
- const providerObj = this._web3Wrapper.getProviderObj();
+ const providerObj = this._web3Wrapper.getProvider();
c.setProvider(providerObj);
const artifactNetworkConfigs = artifact.networks[this.networkId];