Merge pull request #444 from 0xProject/dedupWeb3Wrapper

Remove custom web3Wrapper from website
This commit is contained in:
Fabio Berger 2018-03-11 12:49:15 +01:00 committed by GitHub
commit 870ba445b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 308 additions and 278 deletions

View File

@ -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)

View File

@ -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);
}

View File

@ -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",

View File

@ -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];

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -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)}`);

View File

@ -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}
/>

View File

@ -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({

View File

@ -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;

View File

@ -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({

View File

@ -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) {

View File

@ -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}
/>

View File

@ -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({

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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,

View File

@ -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,

View File

@ -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,
};
}

View File

@ -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',

View File

@ -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);
}
}
}