'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _slicedToArray = function () {function sliceIterator(arr, i) {var _arr = [];var _n = true;var _d = false;var _e = undefined;try {for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {_arr.push(_s.value);if (i && _arr.length === i) break;}} catch (err) {_d = true;_e = err;} finally {try {if (!_n && _i["return"]) _i["return"]();} finally {if (_d) throw _e;}}return _arr;}return function (arr, i) {if (Array.isArray(arr)) {return arr;} else if (Symbol.iterator in Object(arr)) {return sliceIterator(arr, i);} else {throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;}; // Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
exports.























































































fetchBalances = fetchBalances;exports.















updateTokensFilter = updateTokensFilter;exports.






























































































queryTokensFilter = queryTokensFilter;exports.

































































fetchTokensBalances = fetchTokensBalances;var _lodash = require('lodash');var _reactRouterRedux = require('react-router-redux');var _sha = require('@parity/api/lib/util/sha3');var _config = require('../../config');var _notifications = require('../../util/notifications');var _tokens = require('../../util/tokens');var _tokensActions = require('./tokensActions');function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}var TRANSFER_SIGNATURE = (0, _sha.sha3)('Transfer(address,address,uint256)');var log = (0, _config.getLogger)(_config.LOG_KEYS.Balances);var tokensFilter = { tokenAddresses: [], addresses: [] };function _setBalances(balances) {return { type: 'setBalances', balances: balances };} /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             * @param {Object}  _balances         - In the shape:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             *   {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             *     [ who ]: { [ tokenId ]: BigNumber } // The balances of `who`
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             *   }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             * @param {Boolean} skipNotifications [description]
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             */function setBalances(updates) {var skipNotifications = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;return function (dispatch, getState) {var _getState = getState(),tokens = _getState.tokens,balances = _getState.balances;var prevBalances = balances;var nextBalances = _extends({}, prevBalances);Object.keys(updates).forEach(function (who) {var accountUpdates = updates[who];Object.keys(accountUpdates).forEach(function (tokenId) {var token = tokens[tokenId];var prevTokenValue = (prevBalances[who] || {})[tokenId];var nextTokenValue = accountUpdates[tokenId];if (prevTokenValue && prevTokenValue.lt(nextTokenValue)) {dispatch(notifyBalanceChange(who, prevTokenValue, nextTokenValue, token));}nextBalances[who] = _extends({}, nextBalances[who] || {}, _defineProperty({}, tokenId, nextTokenValue));});});return dispatch(_setBalances(nextBalances));};}function notifyBalanceChange(who, fromValue, toValue, token) {return function (dispatch, getState) {var account = getState().personal.accounts[who];if (account) {var txValue = toValue.minus(fromValue);var redirectToAccount = function redirectToAccount() {var basePath = account.wallet ? 'wallet' : 'accounts';var route = '/' + basePath + '/' + account.address;dispatch((0, _reactRouterRedux.push)(route));};(0, _notifications.notifyTransaction)(account, token, txValue, redirectToAccount);}};} // TODO: fetch txCount when needed
function fetchBalances(addresses) {var skipNotifications = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;return function (dispatch, getState) {var _getState2 = getState(),personal = _getState2.personal;var visibleAccounts = personal.visibleAccounts,accounts = personal.accounts;var addressesToFetch = addresses || (0, _lodash.uniq)(visibleAccounts.concat(Object.keys(accounts)));var updates = addressesToFetch.reduce(function (updates, who) {updates[who] = [_tokens.ETH_TOKEN.id];return updates;}, {});return fetchTokensBalances(updates, skipNotifications)(dispatch, getState);};}function updateTokensFilter() {var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};return function (dispatch, getState) {var _getState3 = getState(),api = _getState3.api,personal = _getState3.personal,tokens = _getState3.tokens;var visibleAccounts = personal.visibleAccounts,accounts = personal.accounts;var addresses = (0, _lodash.uniq)(visibleAccounts.concat(Object.keys(accounts)));var tokensToUpdate = Object.values(tokens);var tokensAddressMap = Object.values(tokens).reduce(function (map, token) {map[token.address] = token;return map;}, {});var tokenAddresses = tokensToUpdate.map(function (t) {return t.address;}).filter(function (address) {return address && !/^(0x)?0*$/.test(address);}); // Token Addresses that are not in the current filter
    var newTokenAddresses = (0, _lodash.difference)(tokenAddresses, tokensFilter.tokenAddresses); // Addresses that are not in the current filter (omit those
    // that the filter includes)
    var newAddresses = (0, _lodash.difference)(addresses, tokensFilter.addresses);if (tokensFilter.filterFromId || tokensFilter.filterToId) {// If no new addresses and the same tokens, don't change the filter
      if (newTokenAddresses.length === 0 && newAddresses.length === 0) {log.debug('no need to update token filter', addresses, tokenAddresses, tokensFilter);return;}}var promises = [];var updates = {};var allTokenIds = tokensToUpdate.map(function (token) {return token.id;});var newTokenIds = newTokenAddresses.map(function (address) {return tokensAddressMap[address].id;});newAddresses.forEach(function (newAddress) {updates[newAddress] = allTokenIds;});(0, _lodash.difference)(addresses, newAddresses).forEach(function (oldAddress) {updates[oldAddress] = newTokenIds;});log.debug('updating the token filter', addresses, tokenAddresses);var topicsFrom = [TRANSFER_SIGNATURE, addresses, null];var topicsTo = [TRANSFER_SIGNATURE, null, addresses];var filterOptions = { fromBlock: 'latest', toBlock: 'latest', address: tokenAddresses };var optionsFrom = _extends({}, filterOptions, { topics: topicsFrom });var optionsTo = _extends({}, filterOptions, { topics: topicsTo });promises.push(api.eth.newFilter(optionsFrom), api.eth.newFilter(optionsTo));if (tokensFilter.filterFromId) {promises.push(api.eth.uninstallFilter(tokensFilter.filterFromId));}if (tokensFilter.filterToId) {promises.push(api.eth.uninstallFilter(tokensFilter.filterToId));}return Promise.all(promises).then(function (_ref) {var _ref2 = _slicedToArray(_ref, 2),filterFromId = _ref2[0],filterToId = _ref2[1];var nextTokensFilter = { filterFromId: filterFromId, filterToId: filterToId, addresses: addresses, tokenAddresses: tokenAddresses };tokensFilter = nextTokensFilter;}).then(function () {return fetchTokensBalances(updates)(dispatch, getState);}).catch(function (error) {console.warn('balances::updateTokensFilter', error);});};}function queryTokensFilter() {return function (dispatch, getState) {var _getState4 = getState(),api = _getState4.api;Promise.all([api.eth.getFilterChanges(tokensFilter.filterFromId), api.eth.getFilterChanges(tokensFilter.filterToId)]).then(function (_ref3) {var _ref4 = _slicedToArray(_ref3, 2),logsFrom = _ref4[0],logsTo = _ref4[1];var logs = [].concat(logsFrom, logsTo);if (logs.length === 0) {return;} else {log.debug('got tokens filter logs', logs);}var _getState5 = getState(),personal = _getState5.personal,tokens = _getState5.tokens;var visibleAccounts = personal.visibleAccounts,accounts = personal.accounts;var addressesToFetch = (0, _lodash.uniq)(visibleAccounts.concat(Object.keys(accounts)));var lcAddresses = addressesToFetch.map(function (a) {return a.toLowerCase();});var lcTokensMap = Object.values(tokens).reduce(function (map, token) {map[token.address.toLowerCase()] = token;return map;}); // The keys are the account addresses,
      // and the value is an Array of the tokens addresses
      // to update
      var updates = {};logs.forEach(function (log, index) {var tokenAddress = log.address.toLowerCase();var token = lcTokensMap[tokenAddress]; // logs = [ ...logsFrom, ...logsTo ]
        var topicIdx = index < logsFrom.length ? 1 : 2;var address = ('0x' + log.topics[topicIdx].slice(-40)).toLowerCase();var addressIndex = lcAddresses.indexOf(address);if (addressIndex > -1) {var who = addressesToFetch[addressIndex];updates[who] = [].concat(updates[who] || [], token.id);}}); // No accounts to update
      if (Object.keys(updates).length === 0) {return;}Object.keys(updates).forEach(function (who) {// Keep non-empty token addresses
        updates[who] = (0, _lodash.uniq)(updates[who]);});fetchTokensBalances(updates)(dispatch, getState);});};}function fetchTokensBalances(updates) {var skipNotifications = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;return function (dispatch, getState) {var _getState6 = getState(),api = _getState6.api,personal = _getState6.personal,tokens = _getState6.tokens;var allTokens = Object.values(tokens);if (!updates) {var visibleAccounts = personal.visibleAccounts,accounts = personal.accounts;var addressesToFetch = (0, _lodash.uniq)(visibleAccounts.concat(Object.keys(accounts)));updates = addressesToFetch.reduce(function (updates, who) {updates[who] = allTokens.map(function (token) {return token.id;});return updates;}, {});}var start = Date.now();
    return (0, _tokens.fetchAccountsBalances)(api, allTokens, updates).
    then(function (balances) {
      log.debug('got tokens balances', balances, updates, '(took ' + (Date.now() - start) + 'ms)');

      // Tokens info might not be fetched yet (to not load
      // tokens we don't care about)
      var tokenIdsToFetch = Object.values(balances).
      reduce(function (tokenIds, balance) {
        var nextTokenIds = Object.keys(balance).
        filter(function (tokenId) {return balance[tokenId].gt(0);});

        return tokenIds.concat(nextTokenIds);
      }, []);

      var tokenIndexesToFetch = (0, _lodash.uniq)(tokenIdsToFetch).
      filter(function (tokenId) {return tokens[tokenId] && tokens[tokenId].index && !tokens[tokenId].fetched;}).
      map(function (tokenId) {return tokens[tokenId].index;});

      if (tokenIndexesToFetch.length === 0) {
        return balances;
      }

      start = Date.now();
      return (0, _tokensActions.fetchTokens)(tokenIndexesToFetch)(dispatch, getState).
      then(function () {return log.debug('token indexes fetched', tokenIndexesToFetch, '(took ' + (Date.now() - start) + 'ms)');}).
      then(function () {return balances;});
    }).
    then(function (balances) {
      dispatch(setBalances(balances, skipNotifications));
    }).
    catch(function (error) {
      console.warn('balances::fetchTokensBalances', error);
    });
  };
}