diff options
Diffstat (limited to 'packages/website/ts/components/token_balances.tsx')
-rw-r--r-- | packages/website/ts/components/token_balances.tsx | 658 |
1 files changed, 0 insertions, 658 deletions
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx deleted file mode 100644 index e8f2a6461..000000000 --- a/packages/website/ts/components/token_balances.tsx +++ /dev/null @@ -1,658 +0,0 @@ -import { - constants as sharedConstants, - EtherscanLinkSuffixes, - Networks, - Styles, - utils as sharedUtils, -} from '@0x/react-shared'; -import { BigNumber, errorUtils, fetchAsync, logUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; -import Dialog from 'material-ui/Dialog'; -import Divider from 'material-ui/Divider'; -import FlatButton from 'material-ui/FlatButton'; -import FloatingActionButton from 'material-ui/FloatingActionButton'; -import ContentAdd from 'material-ui/svg-icons/content/add'; -import ContentRemove from 'material-ui/svg-icons/content/remove'; -import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; -import * as React from 'react'; -import ReactTooltip from 'react-tooltip'; -import firstBy from 'thenby'; -import { Blockchain } from 'ts/blockchain'; -import { AssetPicker } from 'ts/components/generate_order/asset_picker'; -import { SendButton } from 'ts/components/send_button'; -import { HelpTooltip } from 'ts/components/ui/help_tooltip'; -import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; -import { TokenIcon } from 'ts/components/ui/token_icon'; -import { AllowanceStateToggle } from 'ts/containers/inputs/allowance_state_toggle'; -import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { - BalanceErrs, - BlockchainCallErrs, - BlockchainErrs, - ScreenWidths, - Token, - TokenByAddress, - TokenStateByAddress, - TokenVisibility, -} from 'ts/types'; -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'; - -const ETHER_ICON_PATH = '/images/ether.png'; -const ETHER_TOKEN_SYMBOL = 'WETH'; -const ZRX_TOKEN_SYMBOL = 'ZRX'; - -const ICON_DIMENSION = 40; -const ARTIFICIAL_FAUCET_REQUEST_DELAY = 1000; -const TOKEN_TABLE_ROW_HEIGHT = 60; -const MAX_TOKEN_TABLE_HEIGHT = 420; -const TOKEN_COL_SPAN_LG = 2; -const TOKEN_COL_SPAN_SM = 1; - -const styles: Styles = { - bgColor: { - backgroundColor: 'transparent', - }, -}; - -interface TokenBalancesProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - screenWidth: ScreenWidths; - tokenByAddress: TokenByAddress; - trackedTokens: Token[]; - userAddress: string; - userEtherBalanceInWei: BigNumber; - networkId: number; - lastForceTokenStateRefetch: number; - isFullWidth?: boolean; -} - -interface TokenBalancesState { - errorType: BalanceErrs; - trackedTokenStateByAddress: TokenStateByAddress; - isBalanceSpinnerVisible: boolean; - isZRXSpinnerVisible: boolean; - isTokenPickerOpen: boolean; - isAddingToken: boolean; -} - -export class TokenBalances extends React.Component<TokenBalancesProps, TokenBalancesState> { - public static defaultProps: Partial<TokenBalancesProps> = { - userEtherBalanceInWei: new BigNumber(0), - isFullWidth: false, - }; - private _isUnmounted: boolean; - public constructor(props: TokenBalancesProps) { - super(props); - this._isUnmounted = false; - const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); - this.state = { - errorType: undefined, - isBalanceSpinnerVisible: false, - isZRXSpinnerVisible: false, - isTokenPickerOpen: false, - isAddingToken: false, - trackedTokenStateByAddress: initialTrackedTokenStateByAddress, - }; - } - public componentWillMount(): void { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - public componentWillUnmount(): void { - this._isUnmounted = true; - } - public componentWillReceiveProps(nextProps: TokenBalancesProps): void { - if (nextProps.userEtherBalanceInWei !== this.props.userEtherBalanceInWei) { - if (this.state.isBalanceSpinnerVisible) { - const receivedAmountInWei = nextProps.userEtherBalanceInWei.minus(this.props.userEtherBalanceInWei); - const receivedAmountInEth = Web3Wrapper.toUnitAmount(receivedAmountInWei, constants.DECIMAL_PLACES_ETH); - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - this.props.dispatcher.showFlashMessage( - `Received ${receivedAmountInEth.toString(10)} ${networkName} Ether`, - ); - } - this.setState({ - isBalanceSpinnerVisible: false, - }); - } - - if ( - nextProps.userAddress !== this.props.userAddress || - nextProps.networkId !== this.props.networkId || - nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch - ) { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - - if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { - const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); - const newTokenAddresses = _.map(newTokens, token => token.address); - // Add placeholder entry for this token to the state, since fetching the - // balance/allowance is asynchronous - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - for (const tokenAddress of newTokenAddresses) { - trackedTokenStateByAddress[tokenAddress] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - } - this.setState({ - trackedTokenStateByAddress, - }); - // Fetch the actual balance/allowance. - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(newTokenAddresses); - } - } - public componentDidMount(): void { - window.scrollTo(0, 0); - } - public render(): React.ReactNode { - const errorDialogActions = [ - <FlatButton - key="errorOkBtn" - label="Ok" - primary={true} - onClick={this._onErrorDialogToggle.bind(this, false)} - />, - ]; - const isTestNetwork = utils.isTestNetwork(this.props.networkId); - const stubColumnStyle = { - display: isTestNetwork ? 'none' : 'table-cell', - }; - const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT; - const tokenTableHeight = - allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT; - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const allowanceExplanation = - '0x smart contracts require access to your<br> \ - 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 = Web3Wrapper.toUnitAmount( - this.props.userEtherBalanceInWei, - constants.DECIMAL_PLACES_ETH, - ); - const rootClassName = this.props.isFullWidth ? 'pb2' : 'lg-px4 md-px4 sm-px1 pb2'; - return ( - <div className={rootClassName}> - <h3>{isTestNetwork ? 'Test ether' : 'Ether'}</h3> - <Divider /> - <div className="pt2 pb2"> - {isTestNetwork - ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \ - gas costs. It might take a bit of time for the test ether to show up.' - : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \ - You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'} - </div> - <Table selectable={false} style={styles.bgColor}> - <TableHeader displaySelectAll={false} adjustForCheckbox={false}> - <TableRow> - <TableHeaderColumn>Currency</TableHeaderColumn> - <TableHeaderColumn>Balance</TableHeaderColumn> - <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} /> - {isTestNetwork && <TableHeaderColumn style={{ paddingLeft: 3 }}>Action</TableHeaderColumn>} - <TableHeaderColumn>Send</TableHeaderColumn> - </TableRow> - </TableHeader> - <TableBody displayRowCheckbox={false}> - <TableRow key="ETH"> - <TableRowColumn className="py1"> - <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} /> - </TableRowColumn> - <TableRowColumn> - {userEtherBalanceInEth.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} ETH - {this.state.isBalanceSpinnerVisible && ( - <span className="pl1"> - <i className="zmdi zmdi-spinner zmdi-hc-spin" /> - </span> - )} - </TableRowColumn> - <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} /> - {isTestNetwork && ( - <TableRowColumn style={{ paddingLeft: 3 }}> - <LifeCycleRaisedButton - labelReady="Request" - labelLoading="Sending..." - labelComplete="Sent!" - onClickAsyncFn={this._faucetRequestAsync.bind(this, true)} - /> - </TableRowColumn> - )} - <TableRowColumn> - <SendButton - userAddress={this.props.userAddress} - networkId={this.props.networkId} - blockchain={this.props.blockchain} - dispatcher={this.props.dispatcher} - asset="ETH" - onError={this._onSendFailed.bind(this)} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - // This is not necessary for ETH. - // tslint:disable:jsx-no-lambda - refetchTokenStateAsync={() => undefined} - /> - </TableRowColumn> - </TableRow> - </TableBody> - </Table> - <div className="clearfix" style={{ paddingBottom: 1 }}> - <div className="col col-10"> - <h3 className="pt2">{isTestNetwork ? 'Test tokens' : 'Tokens'}</h3> - </div> - <div className="col col-1 pt3 align-right"> - <FloatingActionButton mini={true} zDepth={0} onClick={this._onAddTokenClicked.bind(this)}> - <ContentAdd /> - </FloatingActionButton> - </div> - <div className="col col-1 pt3 align-right"> - <FloatingActionButton mini={true} zDepth={0} onClick={this._onRemoveTokenClicked.bind(this)}> - <ContentRemove /> - </FloatingActionButton> - </div> - </div> - <Divider /> - <div className="pt2 pb2"> - {isTestNetwork - ? "Mint some test tokens you'd like to use to generate or fill an order using 0x." - : "Set trading permissions for a token you'd like to start trading."} - </div> - <Table selectable={false} bodyStyle={{ height: tokenTableHeight }} style={styles.bgColor}> - <TableHeader displaySelectAll={false} adjustForCheckbox={false}> - <TableRow> - <TableHeaderColumn colSpan={tokenColSpan}>Token</TableHeaderColumn> - <TableHeaderColumn style={{ paddingLeft: 3 }}>Balance</TableHeaderColumn> - <TableHeaderColumn> - <div className="inline-block">Allowance</div> - <HelpTooltip style={{ paddingLeft: 4 }} explanation={allowanceExplanation} /> - </TableHeaderColumn> - {isTestNetwork && <TableHeaderColumn>Action</TableHeaderColumn>} - {this.props.screenWidth !== ScreenWidths.Sm && <TableHeaderColumn>Send</TableHeaderColumn>} - </TableRow> - </TableHeader> - <TableBody displayRowCheckbox={false}>{this._renderTokenTableRows()}</TableBody> - </Table> - <Dialog - title="Oh oh" - titleStyle={{ fontWeight: 100 }} - actions={errorDialogActions} - open={!_.isUndefined(this.state.errorType)} - onRequestClose={this._onErrorDialogToggle.bind(this, false)} - > - {this._renderErrorDialogBody()} - </Dialog> - <AssetPicker - userAddress={this.props.userAddress} - networkId={this.props.networkId} - blockchain={this.props.blockchain} - dispatcher={this.props.dispatcher} - isOpen={this.state.isTokenPickerOpen} - currentTokenAddress={''} - onTokenChosen={this._onAssetTokenPicked.bind(this)} - tokenByAddress={this.props.tokenByAddress} - tokenVisibility={this.state.isAddingToken ? TokenVisibility.Untracked : TokenVisibility.Tracked} - /> - </div> - ); - } - private _renderTokenTableRows(): React.ReactNode { - if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) { - return ''; - } - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const actionPaddingX = isSmallScreen ? 2 : 24; - const trackedTokens = this.props.trackedTokens; - const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) - .thenBy('trackedTimestamp'), - ); - const tableRows = _.map( - trackedTokensStartingWithEtherToken, - this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX), - ); - return tableRows; - } - private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token): React.ReactNode { - const tokenState = this.state.trackedTokenStateByAddress[token.address]; - const tokenLink = sharedUtils.getEtherScanLinkIfExists( - token.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const isMintable = - (_.includes(configs.SYMBOLS_OF_MINTABLE_KOVAN_TOKENS, token.symbol) && - this.props.networkId === sharedConstants.NETWORK_ID_BY_NAME[Networks.Kovan]) || - (_.includes(configs.SYMBOLS_OF_MINTABLE_ROPSTEN_TOKENS, token.symbol) && - this.props.networkId === sharedConstants.NETWORK_ID_BY_NAME[Networks.Ropsten]); - return ( - <TableRow key={token.address} style={{ height: TOKEN_TABLE_ROW_HEIGHT }}> - <TableRowColumn colSpan={tokenColSpan}> - {_.isUndefined(tokenLink) ? ( - this._renderTokenName(token) - ) : ( - <a href={tokenLink} target="_blank" style={{ textDecoration: 'none' }}> - {this._renderTokenName(token)} - </a> - )} - </TableRowColumn> - <TableRowColumn style={{ paddingRight: 3, paddingLeft: 3 }}> - {tokenState.isLoaded ? ( - <span> - {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} - {this.state.isZRXSpinnerVisible && token.symbol === ZRX_TOKEN_SYMBOL && ( - <span className="pl1"> - <i className="zmdi zmdi-spinner zmdi-hc-spin" /> - </span> - )} - </span> - ) : ( - <i className="zmdi zmdi-spinner zmdi-hc-spin" /> - )} - </TableRowColumn> - <TableRowColumn> - <div className="flex justify-center"> - <AllowanceStateToggle - blockchain={this.props.blockchain} - token={token} - tokenState={tokenState} - onErrorOccurred={this._onErrorOccurred.bind(this)} - refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this, token.address)} - /> - </div> - </TableRowColumn> - {utils.isTestNetwork(this.props.networkId) && ( - <TableRowColumn style={{ paddingLeft: actionPaddingX, paddingRight: actionPaddingX }}> - {isMintable && ( - <LifeCycleRaisedButton - labelReady="Mint" - labelLoading={<span style={{ fontSize: 12 }}>Minting...</span>} - labelComplete="Minted!" - onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)} - /> - )} - </TableRowColumn> - )} - {this.props.screenWidth !== ScreenWidths.Sm && ( - <TableRowColumn - style={{ - paddingLeft: actionPaddingX, - paddingRight: actionPaddingX, - }} - > - <SendButton - userAddress={this.props.userAddress} - networkId={this.props.networkId} - blockchain={this.props.blockchain} - dispatcher={this.props.dispatcher} - asset={token} - onError={this._onSendFailed.bind(this)} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this, token.address)} - /> - </TableRowColumn> - )} - </TableRow> - ); - } - private _onAssetTokenPicked(tokenAddress: string): void { - if (_.isEmpty(tokenAddress)) { - this.setState({ - isTokenPickerOpen: false, - }); - return; - } - const token = this.props.tokenByAddress[tokenAddress]; - const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol); - if (!this.state.isAddingToken && !isDefaultTrackedToken) { - if (token.isRegistered) { - // Remove the token from tracked tokens - const newToken: Token = { - ...token, - trackedTimestamp: undefined, - }; - this.props.dispatcher.updateTokenByAddress([newToken]); - } else { - this.props.dispatcher.removeTokenToTokenByAddress(token); - } - trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress); - } else if (isDefaultTrackedToken) { - this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`); - } - this.setState({ - isTokenPickerOpen: false, - }); - } - private _onSendFailed(): void { - this.setState({ - errorType: BalanceErrs.SendFailed, - }); - } - private _renderAmount(amount: BigNumber, decimals: number): React.ReactNode { - const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); - return unitAmount.toNumber().toFixed(configs.AMOUNT_DISPLAY_PRECSION); - } - private _renderTokenName(token: Token): React.ReactNode { - const tooltipId = `tooltip-${token.address}`; - return ( - <div className="flex"> - <TokenIcon token={token} diameter={ICON_DIMENSION} /> - <div data-tip={true} data-for={tooltipId} className="mt2 ml2 sm-hide xs-hide"> - {token.name} - </div> - <ReactTooltip id={tooltipId}>{token.address}</ReactTooltip> - </div> - ); - } - private _renderErrorDialogBody(): React.ReactNode { - switch (this.state.errorType) { - case BalanceErrs.IncorrectNetworkForFaucet: - return ( - <div> - Our faucet can only send test Ether to addresses on testnets. Please make sure you are connected - to a testnet and try requesting again. - </div> - ); - - case BalanceErrs.FaucetRequestFailed: - return ( - <div> - An unexpected error occurred while trying to request test Ether from our faucet. Please refresh - the page and try again. - </div> - ); - - case BalanceErrs.FaucetQueueIsFull: - return <div>Our test Ether faucet queue is full. Please try requesting test Ether again later.</div>; - - case BalanceErrs.MintingFailed: - return <div>Minting your test tokens failed unexpectedly. Please refresh the page and try again.</div>; - - case BalanceErrs.AllowanceSettingFailed: - return ( - <div> - An unexpected error occurred while trying to set your test token allowance. Please refresh the - page and try again. - </div> - ); - - case undefined: - return null; // No error to show - - default: - throw errorUtils.spawnSwitchErr('errorType', this.state.errorType); - } - } - private _onErrorOccurred(errorType: BalanceErrs): void { - this.setState({ - errorType, - }); - } - private async _onMintTestTokensAsync(token: Token): Promise<boolean> { - try { - await this.props.blockchain.mintTestTokensAsync(token); - await this._refetchTokenStateAsync(token.address); - const amount = Web3Wrapper.toUnitAmount(constants.MINT_AMOUNT, token.decimals); - this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); - return true; - } catch (err) { - const errMsg = `${err}`; - if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - if (utils.didUserDenyWeb3Request(errMsg)) { - return false; - } - logUtils.log(`Unexpected error encountered: ${err}`); - logUtils.log(err.stack); - this.setState({ - errorType: BalanceErrs.MintingFailed, - }); - errorReporter.report(err); - return false; - } - } - private async _faucetRequestAsync(isEtherRequest: boolean): Promise<boolean> { - if (this.props.userAddress === '') { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - - // If on another network other then the testnet our faucet serves test ether - // from, we must show user an error message - if (!utils.isTestNetwork(this.props.blockchain.networkId)) { - this.setState({ - errorType: BalanceErrs.IncorrectNetworkForFaucet, - }); - return false; - } - - await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY); - - const segment = isEtherRequest ? 'ether' : 'zrx'; - const response = await fetchAsync( - `${constants.URL_TESTNET_FAUCET}/${segment}/${this.props.userAddress}?networkId=${this.props.networkId}`, - ); - const responseBody = await response.text(); - if (response.status !== constants.SUCCESS_STATUS) { - logUtils.log(`Unexpected status code: ${response.status} -> ${responseBody}`); - const errorType = - response.status === constants.UNAVAILABLE_STATUS - ? BalanceErrs.FaucetQueueIsFull - : BalanceErrs.FaucetRequestFailed; - this.setState({ - errorType, - }); - errorReporter.report(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); - return false; - } - - if (isEtherRequest) { - this.setState({ - isBalanceSpinnerVisible: true, - }); - } else { - this.setState({ - isZRXSpinnerVisible: true, - }); - // tslint:disable-next-line:no-floating-promises - this._startPollingZrxBalanceAsync(); - } - return true; - } - private _onErrorDialogToggle(_isOpen: boolean): void { - this.setState({ - errorType: undefined, - }); - } - private _onAddTokenClicked(): void { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: true, - }); - } - private _onRemoveTokenClicked(): void { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: false, - }); - } - private async _startPollingZrxBalanceAsync(): Promise<void> { - const tokens = _.values(this.props.tokenByAddress); - const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL); - - // tslint:disable-next-line:no-floating-promises - const balance = await this.props.blockchain.pollTokenBalanceAsync(zrxToken); - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - trackedTokenStateByAddress[zrxToken.address] = { - ...trackedTokenStateByAddress[zrxToken.address], - balance, - }; - this.setState({ - isZRXSpinnerVisible: false, - }); - } - private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]): Promise<void> { - 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( - userAddressIfExists, - tokenAddress, - ); - trackedTokenStateByAddress[tokenAddress] = { - balance, - allowance, - isLoaded: true, - }; - } - if (!this._isUnmounted) { - this.setState({ - trackedTokenStateByAddress, - }); - } - } - private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]): TokenStateByAddress { - const trackedTokenStateByAddress: TokenStateByAddress = {}; - _.each(trackedTokens, token => { - trackedTokenStateByAddress[token.address] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - }); - return trackedTokenStateByAddress; - } - private async _refetchTokenStateAsync(tokenAddress: string): Promise<void> { - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - tokenAddress, - ); - this.setState({ - trackedTokenStateByAddress: { - ...this.state.trackedTokenStateByAddress, - [tokenAddress]: { - balance, - allowance, - isLoaded: true, - }, - }, - }); - } -} // tslint:disable:max-file-line-count |