aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-03-11 19:49:15 +0800
committerGitHub <noreply@github.com>2018-03-11 19:49:15 +0800
commit870ba445b85aec8331059ebdc80e8d37d73bf9cf (patch)
tree6b29441ee2831f3bc22bf289b9e2bad667199b5b
parent0aad2ee005c0d6d530dd870ec1dd38399359a3c4 (diff)
parentcaaa70f6302b0d84d43ca93ab014e0cf37886c0a (diff)
downloaddexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar.gz
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar.bz2
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar.lz
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar.xz
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.tar.zst
dexon-sol-tools-870ba445b85aec8331059ebdc80e8d37d73bf9cf.zip
Merge pull request #444 from 0xProject/dedupWeb3Wrapper
Remove custom web3Wrapper from website
-rw-r--r--packages/web3-wrapper/CHANGELOG.md4
-rw-r--r--packages/web3-wrapper/src/index.ts5
-rw-r--r--packages/website/package.json1
-rw-r--r--packages/website/ts/blockchain.ts177
-rw-r--r--packages/website/ts/blockchain_watcher.ts107
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx11
-rw-r--r--packages/website/ts/components/dialogs/ledger_config_dialog.tsx12
-rw-r--r--packages/website/ts/components/eth_weth_conversion_button.tsx4
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx30
-rw-r--r--packages/website/ts/components/generate_order/generate_order_form.tsx3
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx1
-rw-r--r--packages/website/ts/components/inputs/token_amount_input.tsx3
-rw-r--r--packages/website/ts/components/portal.tsx9
-rw-r--r--packages/website/ts/components/token_balances.tsx26
-rw-r--r--packages/website/ts/components/trade_history/trade_history_item.tsx6
-rw-r--r--packages/website/ts/components/visual_order.tsx5
-rw-r--r--packages/website/ts/containers/portal.ts4
-rw-r--r--packages/website/ts/redux/dispatcher.ts8
-rw-r--r--packages/website/ts/redux/reducer.ts12
-rw-r--r--packages/website/ts/utils/configs.ts1
-rw-r--r--packages/website/ts/web3_wrapper.ts157
21 files changed, 308 insertions, 278 deletions
diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md
index ac0f5ff0b..6dabc7eb4 100644
--- a/packages/web3-wrapper/CHANGELOG.md
+++ b/packages/web3-wrapper/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## v0.2.1 _TBD_
+
+ * Add a `getProvider` method (#444)
+
## v0.2.0 _March 4, 2018_
* Ensure all returned user addresses are lowercase (#373)
diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts
index 64a19f531..e14eb2914 100644
--- a/packages/web3-wrapper/src/index.ts
+++ b/packages/web3-wrapper/src/index.ts
@@ -11,7 +11,7 @@ export class Web3Wrapper {
if (_.isUndefined((provider as any).sendAsync)) {
// Web3@1.0 provider doesn't support synchronous http requests,
// so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
- // We re-assign the send method so that Web3@1.0 providers work with 0x.js
+ // We re-assign the send method so that Web3@1.0 providers work with @0xproject/web3-wrapper
(provider as any).sendAsync = (provider as any).send;
}
this._web3 = new Web3();
@@ -22,6 +22,9 @@ export class Web3Wrapper {
public getContractDefaults(): Partial<TxData> {
return this._defaults;
}
+ public getProvider(): Web3.Provider {
+ return this._web3.currentProvider;
+ }
public setProvider(provider: Web3.Provider) {
this._web3.setProvider(provider);
}
diff --git a/packages/website/package.json b/packages/website/package.json
index db3035642..a7fc1fe26 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -22,6 +22,7 @@
"@0xproject/react-docs": "^0.0.1",
"@0xproject/react-shared": "^0.0.1",
"@0xproject/subproviders": "^0.7.0",
+ "@0xproject/web3-wrapper": "^0.2.1",
"@0xproject/utils": "^0.4.1",
"accounting": "^0.4.1",
"basscss": "^8.0.3",
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];
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
new file mode 100644
index 000000000..d3801cef4
--- /dev/null
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -0,0 +1,107 @@
+import { BigNumber, intervalUtils, promisify } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import * as _ from 'lodash';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { utils } from 'ts/utils/utils';
+
+export class BlockchainWatcher {
+ private _dispatcher: Dispatcher;
+ private _web3Wrapper: Web3Wrapper;
+ private _prevNetworkId: number;
+ private _shouldPollUserAddress: boolean;
+ private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
+ private _prevUserEtherBalanceInWei: BigNumber;
+ private _prevUserAddressIfExists: string;
+ constructor(
+ dispatcher: Dispatcher,
+ web3Wrapper: Web3Wrapper,
+ networkIdIfExists: number,
+ shouldPollUserAddress: boolean,
+ ) {
+ this._dispatcher = dispatcher;
+ this._prevNetworkId = networkIdIfExists;
+ this._shouldPollUserAddress = shouldPollUserAddress;
+ this._web3Wrapper = web3Wrapper;
+ }
+ public destroy() {
+ this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ // HACK: stop() is only available on providerEngine instances
+ const provider = this._web3Wrapper.getProvider();
+ if (!_.isUndefined((provider as any).stop)) {
+ (provider as any).stop();
+ }
+ }
+ // This should only be called from the LedgerConfigDialog
+ public updatePrevUserAddress(userAddress: string) {
+ this._prevUserAddressIfExists = userAddress;
+ }
+ public startEmittingNetworkConnectionAndUserBalanceState() {
+ if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
+ return; // we are already emitting the state
+ }
+
+ let prevNodeVersion: string;
+ this._prevUserEtherBalanceInWei = new BigNumber(0);
+ this._dispatcher.updateNetworkId(this._prevNetworkId);
+ this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ // Check for network state changes
+ let currentNetworkId;
+ try {
+ currentNetworkId = await this._web3Wrapper.getNetworkIdAsync();
+ } catch (err) {
+ // Noop
+ }
+ if (currentNetworkId !== this._prevNetworkId) {
+ this._prevNetworkId = currentNetworkId;
+ this._dispatcher.updateNetworkId(currentNetworkId);
+ }
+
+ // Check for node version changes
+ const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync();
+ if (currentNodeVersion !== prevNodeVersion) {
+ prevNodeVersion = currentNodeVersion;
+ this._dispatcher.updateNodeVersion(currentNodeVersion);
+ }
+
+ if (this._shouldPollUserAddress) {
+ const addresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ const userAddressIfExists = addresses[0];
+ // Update makerAddress on network change
+ if (this._prevUserAddressIfExists !== userAddressIfExists) {
+ this._prevUserAddressIfExists = userAddressIfExists;
+ this._dispatcher.updateUserAddress(userAddressIfExists);
+ }
+
+ // Check for user ether balance changes
+ if (!_.isUndefined(userAddressIfExists)) {
+ await this._updateUserWeiBalanceAsync(userAddressIfExists);
+ }
+ } else {
+ // This logic is primarily for the Ledger, since we don't regularly poll for the address
+ // we simply update the balance for the last fetched address.
+ if (!_.isUndefined(this._prevUserAddressIfExists)) {
+ await this._updateUserWeiBalanceAsync(this._prevUserAddressIfExists);
+ }
+ }
+ },
+ 5000,
+ (err: Error) => {
+ utils.consoleLog(`Watching network and balances failed: ${err.stack}`);
+ this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ },
+ );
+ }
+ private async _updateUserWeiBalanceAsync(userAddress: string) {
+ const balanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(userAddress);
+ if (!balanceInWei.eq(this._prevUserEtherBalanceInWei)) {
+ this._prevUserEtherBalanceInWei = balanceInWei;
+ this._dispatcher.updateUserWeiBalance(balanceInWei);
+ }
+ }
+ private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
+ if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
+ intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
+ }
+ }
+}
diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
index 5c61f0d57..d1bdb447f 100644
--- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
+++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
@@ -1,5 +1,7 @@
+import { ZeroEx } from '0x.js';
import { colors } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
+import * as _ from 'lodash';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import * as React from 'react';
@@ -7,6 +9,7 @@ import { Blockchain } from 'ts/blockchain';
import { EthAmountInput } from 'ts/components/inputs/eth_amount_input';
import { TokenAmountInput } from 'ts/components/inputs/token_amount_input';
import { Side, Token } from 'ts/types';
+import { constants } from 'ts/utils/constants';
interface EthWethConversionDialogProps {
blockchain: Blockchain;
@@ -17,7 +20,7 @@ interface EthWethConversionDialogProps {
onCancelled: () => void;
isOpen: boolean;
token: Token;
- etherBalance: BigNumber;
+ etherBalanceInWei: BigNumber;
lastForceTokenStateRefetch: number;
}
@@ -75,6 +78,7 @@ export class EthWethConversionDialog extends React.Component<
? 'Convert your Ether into a tokenized, tradable form.'
: "Convert your Wrapped Ether back into it's native form.";
const isWrappedVersion = this.props.direction === Side.Receive;
+ const etherBalanceInEth = ZeroEx.toUnitAmount(this.props.etherBalanceInWei, constants.DECIMAL_PLACES_ETH);
return (
<div>
<div className="pb2">{explanation}</div>
@@ -103,7 +107,7 @@ export class EthWethConversionDialog extends React.Component<
/>
) : (
<EthAmountInput
- balance={this.props.etherBalance}
+ balance={etherBalanceInEth}
amount={this.state.value}
onChange={this._onValueChange.bind(this)}
shouldCheckBalance={true}
@@ -182,8 +186,9 @@ export class EthWethConversionDialog extends React.Component<
this.props.onCancelled();
}
private async _fetchEthTokenBalanceAsync() {
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
this.props.token.address,
);
if (!this._isUnmounted) {
diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
index 8a242cd33..87b75ed95 100644
--- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
+++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
@@ -1,3 +1,4 @@
+import { ZeroEx } from '0x.js';
import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -160,14 +161,15 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
}
private _renderAddressTableRows() {
const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => {
- const balance = this.state.addressBalances[i];
+ const balanceInWei = this.state.addressBalances[i];
const addressTooltipId = `address-${userAddress}`;
const balanceTooltipId = `balance-${userAddress}`;
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
// We specifically prefix kovan ETH.
// TODO: We should probably add prefixes for all networks
const isKovanNetwork = networkName === 'Kovan';
- const balanceString = `${balance.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`;
+ const balanceInEth = ZeroEx.toUnitAmount(balanceInWei, constants.DECIMAL_PLACES_ETH);
+ const balanceString = `${balanceInEth.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`;
return (
<TableRow key={userAddress} style={{ height: 40 }}>
<TableRowColumn colSpan={2}>
@@ -204,7 +206,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
this.props.blockchain.updateWeb3WrapperPrevUserAddress(selectedAddress);
// tslint:disable-next-line:no-floating-promises
this.props.blockchain.fetchTokenInformationAsync();
- this.props.dispatcher.updateUserEtherBalance(selectAddressBalance);
+ this.props.dispatcher.updateUserWeiBalance(selectAddressBalance);
this.setState({
stepIndex: LedgerSteps.CONNECT,
});
@@ -233,8 +235,8 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
try {
userAddresses = await this._getUserAddressesAsync();
for (const address of userAddresses) {
- const balance = await this.props.blockchain.getBalanceInEthAsync(address);
- addressBalances.push(balance);
+ const balanceInWei = await this.props.blockchain.getBalanceInWeiAsync(address);
+ addressBalances.push(balanceInWei);
}
} catch (err) {
utils.consoleLog(`Ledger error: ${JSON.stringify(err)}`);
diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx
index 62bafdba7..8e13d0aae 100644
--- a/packages/website/ts/components/eth_weth_conversion_button.tsx
+++ b/packages/website/ts/components/eth_weth_conversion_button.tsx
@@ -18,7 +18,7 @@ interface EthWethConversionButtonProps {
ethToken: Token;
dispatcher: Dispatcher;
blockchain: Blockchain;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
isOutdatedWrappedEther: boolean;
onConversionSuccessful?: () => void;
isDisabled?: boolean;
@@ -74,7 +74,7 @@ export class EthWethConversionButton extends React.Component<
isOpen={this.state.isEthConversionDialogVisible}
onComplete={this._onConversionAmountSelectedAsync.bind(this)}
onCancelled={this._toggleConversionDialog.bind(this)}
- etherBalance={this.props.userEtherBalance}
+ etherBalanceInWei={this.props.userEtherBalanceInWei}
token={this.props.ethToken}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
/>
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx
index 7ac5d5c9c..b12c637e5 100644
--- a/packages/website/ts/components/eth_wrappers.tsx
+++ b/packages/website/ts/components/eth_wrappers.tsx
@@ -15,7 +15,6 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
-const PRECISION = 5;
const DATE_FORMAT = 'D/M/YY';
const ICON_DIMENSION = 40;
const ETHER_ICON_PATH = '/images/ether.png';
@@ -34,7 +33,7 @@ interface EthWrappersProps {
dispatcher: Dispatcher;
tokenByAddress: TokenByAddress;
userAddress: string;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
lastForceTokenStateRefetch: number;
}
@@ -98,6 +97,10 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
EtherscanLinkSuffixes.Address,
);
const tokenLabel = this._renderToken('Wrapped Ether', etherToken.address, configs.ICON_URL_BY_SYMBOL.WETH);
+ const userEtherBalanceInEth = ZeroEx.toUnitAmount(
+ this.props.userEtherBalanceInWei,
+ constants.DECIMAL_PLACES_ETH,
+ );
return (
<div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
<div className="relative">
@@ -144,7 +147,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</div>
</TableRowColumn>
<TableRowColumn>
- {this.props.userEtherBalance.toFixed(PRECISION)} ETH
+ {userEtherBalanceInEth.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} ETH
</TableRowColumn>
<TableRowColumn>
<EthWethConversionButton
@@ -157,7 +160,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
ethToken={etherToken}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
/>
</TableRowColumn>
</TableRow>
@@ -167,7 +170,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</TableRowColumn>
<TableRowColumn>
{this.state.isWethStateLoaded ? (
- `${wethBalance.toFixed(PRECISION)} WETH`
+ `${wethBalance.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} WETH`
) : (
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
)}
@@ -184,7 +187,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
ethToken={etherToken}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
/>
</TableRowColumn>
</TableRow>
@@ -267,7 +270,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address];
const balanceInEthIfExists = isStateLoaded
? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed(
- PRECISION,
+ configs.AMOUNT_DISPLAY_PRECSION,
)
: undefined;
const onConversionSuccessful = this._onOutdatedConversionSuccessfulAsync.bind(
@@ -304,7 +307,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
ethToken={outdatedEtherToken}
dispatcher={this.props.dispatcher}
blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
onConversionSuccessful={onConversionSuccessful}
/>
</TableRowColumn>
@@ -348,8 +351,9 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
[outdatedWETHAddress]: false,
},
});
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
outdatedWETHAddress,
);
this.setState({
@@ -369,8 +373,9 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
private async _fetchWETHStateAsync() {
const tokens = _.values(this.props.tokenByAddress);
const wethToken = _.find(tokens, token => token.symbol === 'WETH');
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [wethBalance, wethAllowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
wethToken.address,
);
@@ -379,7 +384,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
for (const address of outdatedWETHAddresses) {
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
address,
);
outdatedWETHStateByAddress[address] = {
@@ -420,8 +425,9 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
}
private async _refetchEthTokenStateAsync() {
const etherToken = this._getEthToken();
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
etherToken.address,
);
this.setState({
diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx
index 26fa904fe..ea1d57a7b 100644
--- a/packages/website/ts/components/generate_order/generate_order_form.tsx
+++ b/packages/website/ts/components/generate_order/generate_order_form.tsx
@@ -237,8 +237,9 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
// Check if all required inputs were supplied
const debitToken = this.props.sideToAssetToken[Side.Deposit];
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [debitBalance, debitAllowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
debitToken.address,
);
const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount;
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index 7fe303cf4..3d353d87c 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -67,6 +67,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
private async _onToggleAllowanceAsync(): Promise<void> {
if (this.props.userAddress === '') {
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return;
}
this.setState({
diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx
index 53248c065..b55840fc4 100644
--- a/packages/website/ts/components/inputs/token_amount_input.tsx
+++ b/packages/website/ts/components/inputs/token_amount_input.tsx
@@ -109,8 +109,9 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
this.setState({
isBalanceAndAllowanceLoaded: false,
});
+ const userAddressIfExists = _.isEmpty(userAddress) ? undefined : userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- userAddress,
+ userAddressIfExists,
tokenAddress,
);
if (!this._isUnmounted) {
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx
index d71e821c6..7df340f45 100644
--- a/packages/website/ts/components/portal.tsx
+++ b/packages/website/ts/components/portal.tsx
@@ -46,7 +46,7 @@ export interface PortalAllProps {
providerType: ProviderType;
screenWidth: ScreenWidths;
tokenByAddress: TokenByAddress;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
userAddress: string;
shouldBlockchainErrDialogBeOpen: boolean;
userSuppliedOrderCache: Order;
@@ -121,8 +121,9 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
});
}
if (nextProps.userAddress !== this.state.prevUserAddress) {
+ const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress;
// tslint:disable-next-line:no-floating-promises
- this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress);
+ this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress);
this.setState({
prevUserAddress: nextProps.userAddress,
});
@@ -279,7 +280,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
dispatcher={this.props.dispatcher}
tokenByAddress={this.props.tokenByAddress}
userAddress={this.props.userAddress}
- userEtherBalance={this.props.userEtherBalance}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
/>
);
@@ -306,7 +307,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
tokenByAddress={this.props.tokenByAddress}
trackedTokens={trackedTokens}
userAddress={this.props.userAddress}
- userEtherBalance={this.props.userEtherBalance}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
networkId={this.props.networkId}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
/>
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 7e7596fd7..894e0721f 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -48,7 +48,6 @@ const ETHER_ICON_PATH = '/images/ether.png';
const ETHER_TOKEN_SYMBOL = 'WETH';
const ZRX_TOKEN_SYMBOL = 'ZRX';
-const PRECISION = 5;
const ICON_DIMENSION = 40;
const ARTIFICIAL_FAUCET_REQUEST_DELAY = 1000;
const TOKEN_TABLE_ROW_HEIGHT = 60;
@@ -79,7 +78,7 @@ interface TokenBalancesProps {
tokenByAddress: TokenByAddress;
trackedTokens: Token[];
userAddress: string;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
networkId: number;
lastForceTokenStateRefetch: number;
}
@@ -119,11 +118,14 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
this._isUnmounted = true;
}
public componentWillReceiveProps(nextProps: TokenBalancesProps) {
- if (nextProps.userEtherBalance !== this.props.userEtherBalance) {
+ if (nextProps.userEtherBalanceInWei !== this.props.userEtherBalanceInWei) {
if (this.state.isBalanceSpinnerVisible) {
- const receivedAmount = nextProps.userEtherBalance.minus(this.props.userEtherBalance);
+ const receivedAmountInWei = nextProps.userEtherBalanceInWei.minus(this.props.userEtherBalanceInWei);
+ const receivedAmountInEth = ZeroEx.toUnitAmount(receivedAmountInWei, constants.DECIMAL_PLACES_ETH);
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- this.props.dispatcher.showFlashMessage(`Received ${receivedAmount.toString(10)} ${networkName} Ether`);
+ this.props.dispatcher.showFlashMessage(
+ `Received ${receivedAmountInEth.toString(10)} ${networkName} Ether`,
+ );
}
this.setState({
isBalanceSpinnerVisible: false,
@@ -205,6 +207,10 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
token balances in order to execute trades.<br> \
Toggling sets an allowance for the<br> \
smart contract so you can start trading that token.';
+ const userEtherBalanceInEth = ZeroEx.toUnitAmount(
+ this.props.userEtherBalanceInWei,
+ constants.DECIMAL_PLACES_ETH,
+ );
return (
<div className="lg-px4 md-px4 sm-px1 pb2">
<h3>{isTestNetwork ? 'Test ether' : 'Ether'}</h3>
@@ -241,7 +247,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />
</TableRowColumn>
<TableRowColumn>
- {this.props.userEtherBalance.toFixed(PRECISION)} ETH
+ {userEtherBalanceInEth.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} ETH
{this.state.isBalanceSpinnerVisible && (
<span className="pl1">
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
@@ -493,7 +499,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
}
private _renderAmount(amount: BigNumber, decimals: number) {
const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
- return unitAmount.toNumber().toFixed(PRECISION);
+ return unitAmount.toNumber().toFixed(configs.AMOUNT_DISPLAY_PRECSION);
}
private _renderTokenName(token: Token) {
const tooltipId = `tooltip-${token.address}`;
@@ -681,9 +687,10 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
}
private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) {
const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress;
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
for (const tokenAddress of tokenAddresses) {
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
tokenAddress,
);
trackedTokenStateByAddress[tokenAddress] = {
@@ -710,8 +717,9 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
return trackedTokenStateByAddress;
}
private async _refetchTokenStateAsync(tokenAddress: string) {
+ const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
+ userAddressIfExists,
tokenAddress,
);
this.setState({
diff --git a/packages/website/ts/components/trade_history/trade_history_item.tsx b/packages/website/ts/components/trade_history/trade_history_item.tsx
index 6b8d7c7b5..dbe72259b 100644
--- a/packages/website/ts/components/trade_history/trade_history_item.tsx
+++ b/packages/website/ts/components/trade_history/trade_history_item.tsx
@@ -9,8 +9,8 @@ import * as ReactTooltip from 'react-tooltip';
import { EtherScanIcon } from 'ts/components/ui/etherscan_icon';
import { Party } from 'ts/components/ui/party';
import { Fill, Token, TokenByAddress } from 'ts/types';
+import { configs } from 'ts/utils/configs';
-const PRECISION = 5;
const IDENTICON_DIAMETER = 40;
interface TradeHistoryItemProps {
@@ -131,7 +131,7 @@ export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, Tra
{this._renderAmount(givenAmount, givenToken.symbol, givenToken.decimals)}
</div>
<div style={{ color: colors.grey400, fontSize: 14 }}>
- {exchangeRate.toFixed(PRECISION)} {givenToken.symbol}/{receiveToken.symbol}
+ {exchangeRate.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} {givenToken.symbol}/{receiveToken.symbol}
</div>
</div>
);
@@ -163,7 +163,7 @@ export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, Tra
const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
return (
<span>
- {unitAmount.toFixed(PRECISION)} {symbol}
+ {unitAmount.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} {symbol}
</span>
);
}
diff --git a/packages/website/ts/components/visual_order.tsx b/packages/website/ts/components/visual_order.tsx
index ec2d47f39..3bf464e92 100644
--- a/packages/website/ts/components/visual_order.tsx
+++ b/packages/website/ts/components/visual_order.tsx
@@ -3,10 +3,9 @@ import * as _ from 'lodash';
import * as React from 'react';
import { Party } from 'ts/components/ui/party';
import { AssetToken, Token, TokenByAddress } from 'ts/types';
+import { configs } from 'ts/utils/configs';
import { utils } from 'ts/utils/utils';
-const PRECISION = 5;
-
interface VisualOrderProps {
makerAssetToken: AssetToken;
takerAssetToken: AssetToken;
@@ -67,7 +66,7 @@ export class VisualOrder extends React.Component<VisualOrderProps, VisualOrderSt
const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals);
return (
<div style={{ fontSize: 13 }}>
- {unitAmount.toNumber().toFixed(PRECISION)} {token.symbol}
+ {unitAmount.toNumber().toFixed(configs.AMOUNT_DISPLAY_PRECSION)} {token.symbol}
</div>
);
}
diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts
index befa16bdb..725564ead 100644
--- a/packages/website/ts/containers/portal.ts
+++ b/packages/website/ts/containers/portal.ts
@@ -21,7 +21,7 @@ interface ConnectedState {
providerType: ProviderType;
tokenByAddress: TokenByAddress;
lastForceTokenStateRefetch: number;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
screenWidth: ScreenWidths;
shouldBlockchainErrDialogBeOpen: boolean;
userAddress: string;
@@ -72,7 +72,7 @@ const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): Conne
tokenByAddress: state.tokenByAddress,
lastForceTokenStateRefetch: state.lastForceTokenStateRefetch,
userAddress: state.userAddress,
- userEtherBalance: state.userEtherBalance,
+ userEtherBalanceInWei: state.userEtherBalanceInWei,
userSuppliedOrderCache: state.userSuppliedOrderCache,
flashMessage: state.flashMessage,
translate: state.translate,
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index 5c40ded2c..13e9a10cc 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -86,7 +86,7 @@ export class Dispatcher {
type: ActionTypes.UpdateOrderTakerAddress,
});
}
- public updateUserAddress(address: string) {
+ public updateUserAddress(address?: string) {
this._dispatch({
data: address,
type: ActionTypes.UpdateUserAddress,
@@ -125,14 +125,14 @@ export class Dispatcher {
public batchDispatch(
tokenByAddress: TokenByAddress,
networkId: number,
- userAddress: string,
+ userAddressIfExists: string | undefined,
sideToAssetToken: SideToAssetToken,
) {
this._dispatch({
data: {
tokenByAddress,
networkId,
- userAddress,
+ userAddressIfExists,
sideToAssetToken,
},
type: ActionTypes.BatchDispatch,
@@ -155,7 +155,7 @@ export class Dispatcher {
type: ActionTypes.UpdateOrderECSignature,
});
}
- public updateUserEtherBalance(balance: BigNumber) {
+ public updateUserWeiBalance(balance: BigNumber) {
this._dispatch({
data: balance,
type: ActionTypes.UpdateUserEtherBalance,
diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts
index 1f489db85..a628f65c2 100644
--- a/packages/website/ts/redux/reducer.ts
+++ b/packages/website/ts/redux/reducer.ts
@@ -38,7 +38,7 @@ export interface State {
tokenByAddress: TokenByAddress;
lastForceTokenStateRefetch: number;
userAddress: string;
- userEtherBalance: BigNumber;
+ userEtherBalanceInWei: BigNumber;
// Note: cache of supplied orderJSON in fill order step. Do not use for anything else.
userSuppliedOrderCache: Order;
@@ -77,7 +77,7 @@ const INITIAL_STATE: State = {
tokenByAddress: {},
lastForceTokenStateRefetch: moment().unix(),
userAddress: '',
- userEtherBalance: new BigNumber(0),
+ userEtherBalanceInWei: new BigNumber(0),
userSuppliedOrderCache: undefined,
// Docs
@@ -138,7 +138,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action) {
case ActionTypes.UpdateUserEtherBalance: {
return {
...state,
- userEtherBalance: action.data,
+ userEtherBalanceInWei: action.data,
};
}
@@ -184,10 +184,11 @@ export function reducer(state: State = INITIAL_STATE, action: Action) {
}
case ActionTypes.BatchDispatch: {
+ const userAddress = _.isUndefined(action.data.userAddressIfExists) ? '' : action.data.userAddressIfExists;
return {
...state,
networkId: action.data.networkId,
- userAddress: action.data.userAddress,
+ userAddress,
sideToAssetToken: action.data.sideToAssetToken,
tokenByAddress: action.data.tokenByAddress,
};
@@ -284,9 +285,10 @@ export function reducer(state: State = INITIAL_STATE, action: Action) {
}
case ActionTypes.UpdateUserAddress: {
+ const userAddress = _.isUndefined(action.data) ? '' : action.data;
return {
...state,
- userAddress: action.data,
+ userAddress,
};
}
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index f33b06c0a..f966cedbe 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -9,6 +9,7 @@ const isDevelopment = _.includes(
const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
export const configs = {
+ AMOUNT_DISPLAY_PRECSION: 5,
BACKEND_BASE_URL: 'https://website-api.0xproject.com',
BASE_URL,
BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208',
diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts
deleted file mode 100644
index 9d8d771af..000000000
--- a/packages/website/ts/web3_wrapper.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-import { BigNumber, intervalUtils, promisify } from '@0xproject/utils';
-import * as _ from 'lodash';
-import { Dispatcher } from 'ts/redux/dispatcher';
-import { utils } from 'ts/utils/utils';
-import * as Web3 from 'web3';
-
-export class Web3Wrapper {
- private _dispatcher: Dispatcher;
- private _web3: Web3;
- private _prevNetworkId: number;
- private _shouldPollUserAddress: boolean;
- private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
- private _prevUserEtherBalanceInEth: BigNumber;
- private _prevUserAddress: string;
- constructor(
- dispatcher: Dispatcher,
- provider: Web3.Provider,
- networkIdIfExists: number,
- shouldPollUserAddress: boolean,
- ) {
- this._dispatcher = dispatcher;
- this._prevNetworkId = networkIdIfExists;
- this._shouldPollUserAddress = shouldPollUserAddress;
-
- this._web3 = new Web3();
- this._web3.setProvider(provider);
- }
- public isAddress(address: string) {
- return this._web3.isAddress(address);
- }
- public async getAccountsAsync(): Promise<string[]> {
- const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
- return addresses;
- }
- public async getFirstAccountIfExistsAsync() {
- const addresses = await this.getAccountsAsync();
- if (_.isEmpty(addresses)) {
- return '';
- }
- return addresses[0];
- }
- public async getNodeVersionAsync(): Promise<string> {
- const nodeVersion = await promisify<string>(this._web3.version.getNode)();
- return nodeVersion;
- }
- public getProviderObj() {
- return this._web3.currentProvider;
- }
- public async getNetworkIdIfExists() {
- try {
- const networkId = await this._getNetworkAsync();
- return Number(networkId);
- } catch (err) {
- return undefined;
- }
- }
- public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
- const balanceInWei: BigNumber = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
- const balanceEthOldBigNumber = this._web3.fromWei(balanceInWei, 'ether');
- const balanceEth = new BigNumber(balanceEthOldBigNumber);
- return balanceEth;
- }
- public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
- const code = await promisify<string>(this._web3.eth.getCode)(address);
- // Regex matches 0x0, 0x00, 0x in order to accomodate poorly implemented clients
- const zeroHexAddressRegex = /^0[xX][0]*$/;
- const didFindCode = _.isNull(code.match(zeroHexAddressRegex));
- return didFindCode;
- }
- public async signTransactionAsync(address: string, message: string): Promise<string> {
- const signData = await promisify<string>(this._web3.eth.sign)(address, message);
- return signData;
- }
- public async getBlockTimestampAsync(blockHash: string): Promise<number> {
- const { timestamp } = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockHash);
- return timestamp;
- }
- public destroy() {
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
- // HACK: stop() is only available on providerEngine instances
- const provider = this._web3.currentProvider;
- if (!_.isUndefined((provider as any).stop)) {
- (provider as any).stop();
- }
- }
- // This should only be called from the LedgerConfigDialog
- public updatePrevUserAddress(userAddress: string) {
- this._prevUserAddress = userAddress;
- }
- public startEmittingNetworkConnectionAndUserBalanceState() {
- if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
- return; // we are already emitting the state
- }
-
- let prevNodeVersion: string;
- this._prevUserEtherBalanceInEth = new BigNumber(0);
- this._dispatcher.updateNetworkId(this._prevNetworkId);
- this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
- async () => {
- // Check for network state changes
- const currentNetworkId = await this.getNetworkIdIfExists();
- if (currentNetworkId !== this._prevNetworkId) {
- this._prevNetworkId = currentNetworkId;
- this._dispatcher.updateNetworkId(currentNetworkId);
- }
-
- // Check for node version changes
- const currentNodeVersion = await this.getNodeVersionAsync();
- if (currentNodeVersion !== prevNodeVersion) {
- prevNodeVersion = currentNodeVersion;
- this._dispatcher.updateNodeVersion(currentNodeVersion);
- }
-
- if (this._shouldPollUserAddress) {
- const userAddressIfExists = await this.getFirstAccountIfExistsAsync();
- // Update makerAddress on network change
- if (this._prevUserAddress !== userAddressIfExists) {
- this._prevUserAddress = userAddressIfExists;
- this._dispatcher.updateUserAddress(userAddressIfExists);
- }
-
- // Check for user ether balance changes
- if (!_.isEmpty(userAddressIfExists)) {
- await this._updateUserEtherBalanceAsync(userAddressIfExists);
- }
- } else {
- // This logic is primarily for the Ledger, since we don't regularly poll for the address
- // we simply update the balance for the last fetched address.
- if (!_.isEmpty(this._prevUserAddress)) {
- await this._updateUserEtherBalanceAsync(this._prevUserAddress);
- }
- }
- },
- 5000,
- (err: Error) => {
- utils.consoleLog(`Watching network and balances failed: ${err.stack}`);
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
- },
- );
- }
- private async _getNetworkAsync() {
- const networkId = await promisify(this._web3.version.getNetwork)();
- return networkId;
- }
- private async _updateUserEtherBalanceAsync(userAddress: string) {
- const balance = await this.getBalanceInEthAsync(userAddress);
- if (!balance.eq(this._prevUserEtherBalanceInEth)) {
- this._prevUserEtherBalanceInEth = balance;
- this._dispatcher.updateUserEtherBalance(balance);
- }
- }
- private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
- if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
- intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
- }
- }
-}