Merge pull request #444 from 0xProject/dedupWeb3Wrapper
Remove custom web3Wrapper from website
This commit is contained in:
commit
870ba445b8
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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];
|
||||
|
107
packages/website/ts/blockchain_watcher.ts
Normal file
107
packages/website/ts/blockchain_watcher.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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)}`);
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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({
|
||||
|
@ -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;
|
||||
|
@ -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({
|
||||
|
@ -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) {
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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({
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user