From 63f2606863b9dc3b01fbdc52e232103d07f35c45 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sat, 10 Mar 2018 22:31:19 +0100 Subject: Refactor blockchain to store userAddress as address or undefined --- packages/website/ts/blockchain.ts | 97 +++++++++++++++++++++------------ packages/website/ts/redux/dispatcher.ts | 6 +- packages/website/ts/redux/reducer.ts | 4 +- 3 files changed, 68 insertions(+), 39 deletions(-) (limited to 'packages') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index e85c40e59..30adf680e 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -65,7 +65,7 @@ export class Blockchain { private _dispatcher: Dispatcher; private _web3Wrapper?: Web3Wrapper; private _blockchainWatcher?: BlockchainWatcher; - private _userAddress: string; + private _userAddressIfExists: string; private _cachedProvider: Web3.Provider; private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerWalletSubprovider; @@ -116,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 @@ -138,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(); } @@ -196,8 +195,8 @@ export class Blockchain { 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 = { @@ -244,7 +243,7 @@ export class Blockchain { shouldPollUserAddress, ); - this._userAddress = await this.getFirstAccountIfExistsAsync(); + this._userAddressIfExists = await this.getFirstAccountIfExistsAsync(); this._zeroEx.setProvider(provider, this.networkId); @@ -262,7 +261,7 @@ export class Blockchain { this._showFlashMessageIfLedger(); const txHash = await this._zeroEx.token.setProxyAllowanceAsync( token.address, - this._userAddress, + this._userAddressIfExists, amountInBaseUnits, { gasPrice: this._defaultGasPrice, @@ -271,10 +270,13 @@ export class Blockchain { await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { + 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, { @@ -316,6 +318,7 @@ export class Blockchain { return zeroExSignedOrder; } public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); const shouldThrowOnInsufficientBalanceOrAllowance = true; @@ -325,7 +328,7 @@ export class Blockchain { signedOrder, fillTakerTokenAmount, shouldThrowOnInsufficientBalanceOrAllowance, - this._userAddress, + this._userAddressIfExists, { gasPrice: this._defaultGasPrice, }, @@ -384,12 +387,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); @@ -408,7 +414,7 @@ export class Blockchain { } public async signOrderHashAsync(orderHash: string): Promise { 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)) { @@ -438,7 +444,7 @@ 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, }); } @@ -451,9 +457,14 @@ export class Blockchain { 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 { @@ -461,9 +472,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) { @@ -471,7 +487,12 @@ export class Blockchain { return doesContractExist; } public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise { - 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 { @@ -509,12 +530,16 @@ export class Blockchain { !_.isUndefined(this.networkId), 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node', ); + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); this._dispatcher.updateBlockchainIsLoaded(false); const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); - const trackedTokensByAddress = trackedTokenStorage.getTrackedTokensByAddress(this._userAddress, this.networkId); + const trackedTokensByAddress = trackedTokenStorage.getTrackedTokensByAddress( + this._userAddressIfExists, + this.networkId, + ); const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); if (_.isEmpty(trackedTokensByAddress)) { _.each(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { @@ -523,7 +548,7 @@ export class Blockchain { trackedTokensByAddress[token.address] = token; }); _.each(trackedTokensByAddress, (token: Token, address: string) => { - trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); + trackedTokenStorage.addTrackedTokenToUser(this._userAddressIfExists, this.networkId, token); }); } else { // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array @@ -550,14 +575,14 @@ 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); } public async getFirstAccountIfExistsAsync() { const addresses = await this._web3Wrapper.getAvailableAddressesAsync(); if (_.isEmpty(addresses)) { - return ''; + return undefined; } return addresses[0]; } @@ -578,7 +603,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 @@ -623,16 +648,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, @@ -648,7 +675,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) { @@ -672,10 +699,12 @@ export class Blockchain { } private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs) { 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 @@ -686,7 +715,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 { @@ -765,8 +794,8 @@ export class Blockchain { shouldPollUserAddress, ); - this._userAddress = await this.getFirstAccountIfExistsAsync(); - this._dispatcher.updateUserAddress(this._userAddress); + this._userAddressIfExists = await this.getFirstAccountIfExistsAsync(); + this._dispatcher.updateUserAddress(this._userAddressIfExists); await this.fetchTokenInformationAsync(); this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState(); await this._rehydrateStoreWithContractEvents(); diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index e40d435e0..13e9a10cc 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -86,7 +86,7 @@ export class Dispatcher { type: ActionTypes.UpdateOrderTakerAddress, }); } - public updateUserAddress(address: string) { + public updateUserAddress(address?: string) { this._dispatch({ data: address, type: ActionTypes.UpdateUserAddress, @@ -125,14 +125,14 @@ export class Dispatcher { public batchDispatch( tokenByAddress: TokenByAddress, networkId: number, - userAddress: string, + userAddressIfExists: string | undefined, sideToAssetToken: SideToAssetToken, ) { this._dispatch({ data: { tokenByAddress, networkId, - userAddress, + userAddressIfExists, sideToAssetToken, }, type: ActionTypes.BatchDispatch, diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 1d43e4ffb..2f3150f92 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -187,7 +187,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { return { ...state, networkId: action.data.networkId, - userAddress: action.data.userAddress, + userAddress: _.isUndefined(action.data.userAddress) ? '' : action.data.userAddress, sideToAssetToken: action.data.sideToAssetToken, tokenByAddress: action.data.tokenByAddress, }; @@ -286,7 +286,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action) { case ActionTypes.UpdateUserAddress: { return { ...state, - userAddress: action.data, + userAddress: _.isUndefined(action.data) ? '' : action.data, }; } -- cgit v1.2.3