text: /*
      This file is part of web3.js.
  
      web3.js is free software: you can redistribute it and/or modify
      it under the terms of the GNU Lesser General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      web3.js 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 Lesser General Public License for more details.
  
      You should have received a copy of the GNU Lesser General Public License
      along with web3.js.  If not, see <http://www.gnu.org/licenses/>.
  */
  /**
   * @file formatters.js
   * @author Fabian Vogelsteller <fabian@ethereum.org>
   * @author Marek Kotewicz <marek@parity.io>
   * @date 2017
   */
  
  "use strict";
  
  
  var _ = require('underscore');
  var utils = require('web3-utils');
  var Iban = require('web3-eth-iban');
  
  /**
   * Should the format output to a big number
   *
   * @method outputBigNumberFormatter
   * @param {String|Number|BigNumber} number
   * @returns {BigNumber} object
   */
  var outputBigNumberFormatter = function (number) {
      return utils.toBN(number).toString(10);
  };
  
  var isPredefinedBlockNumber = function (blockNumber) {
      return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest';
  };
  
  var inputDefaultBlockNumberFormatter = function (blockNumber) {
      if (this && (blockNumber === undefined || blockNumber === null)) {
          return this.defaultBlock;
      }
      if (blockNumber === 'genesis' || blockNumber === 'earliest') {
          return '0x0';
      }
      return inputBlockNumberFormatter(blockNumber);
  };
  
  var inputBlockNumberFormatter = function (blockNumber) {
      if (blockNumber === undefined) {
          return undefined;
      } else if (isPredefinedBlockNumber(blockNumber)) {
          return blockNumber;
      }
      return (utils.isHex(blockNumber)) ? ((_.isString(blockNumber)) ? blockNumber.toLowerCase() : blockNumber) : utils.numberToHex(blockNumber);
  };
  
  /**
   * Formats the input of a transaction and converts all values to HEX
   *
   * @method inputCallFormatter
   * @param {Object} transaction options
   * @returns object
  */
  var inputCallFormatter = function (options){
  
      var from = options.from || (this ? this.defaultAccount : null);
  
      if (from) {
          options.from = inputAddressFormatter(from);
      }
  
      if (options.to) { // it might be contract creation
          options.to = inputAddressFormatter(options.to);
      }
  
      // allow both
      if (options.gas || options.gasLimit) {
          options.gas = options.gas || options.gasLimit;
      }
  
      ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
          return options[key] !== undefined;
      }).forEach(function(key){
          options[key] = utils.numberToHex(options[key]);
      });
  
      return options;
  };
  
  /**
   * Formats the input of a transaction and converts all values to HEX
   *
   * @method inputTransactionFormatter
   * @param {Object} options
   * @returns object
  */
  var inputTransactionFormatter = function (options) {
  
      // check from, only if not number, or object
      if (!_.isNumber(options.from) && !_.isObject(options.from)) {
          options.from = options.from || (this ? this.defaultAccount : null);
  
          if (!options.from && !_.isNumber(options.from)) {
              throw new Error('The send transactions "from" field must be defined!');
          }
  
          options.from = inputAddressFormatter(options.from);
      }
  
      if (options.to) { // it might be contract creation
          options.to = inputAddressFormatter(options.to);
      }
  
      // allow both
      if (options.gas || options.gasLimit) {
          options.gas = options.gas || options.gasLimit;
      }
  
      ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
          return options[key] !== undefined;
      }).forEach(function(key){
          options[key] = utils.numberToHex(options[key]);
      });
  
      return options;
  };
  
  /**
   * Hex encodes the data passed to eth_sign and personal_sign
   *
   * @method inputSignFormatter
   * @param {String} data
   * @returns {String}
   */
  var inputSignFormatter = function (data) {
      return (utils.isHex(data)) ? data : utils.utf8ToHex(data);
  };
  
  /**
   * Formats the output of a transaction to its proper values
   *
   * @method outputTransactionFormatter
   * @param {Object} tx
   * @returns {Object}
  */
  var outputTransactionFormatter = function (tx){
      if(tx.blockNumber !== null)
          tx.blockNumber = utils.hexToNumber(tx.blockNumber);
      if(tx.transactionIndex !== null)
          tx.transactionIndex = utils.hexToNumber(tx.transactionIndex);
      tx.nonce = utils.hexToNumber(tx.nonce);
      tx.gas = utils.hexToNumber(tx.gas);
      tx.gasPrice = outputBigNumberFormatter(tx.gasPrice);
      tx.value = outputBigNumberFormatter(tx.value);
  
      if(tx.to) {
          tx.to = utils.toChecksumAddress(tx.to);
      }
      if(tx.from) {
          tx.from = utils.toChecksumAddress(tx.from);
      }
  
      return tx;
  };
  
  /**
   * Formats the output of a transaction receipt to its proper values
   *
   * @method outputTransactionReceiptFormatter
   * @param {Object} receipt
   * @returns {Object}
  */
  var outputTransactionReceiptFormatter = function (receipt){
      if(typeof receipt !== 'object') {
          throw new Error('Received receipt is invalid: '+ receipt);
      }
  
      if(receipt.blockNumber !== null)
          receipt.blockNumber = utils.hexToNumber(receipt.blockNumber);
      if(receipt.transactionIndex !== null)
          receipt.transactionIndex = utils.hexToNumber(receipt.transactionIndex);
      receipt.cumulativeGasUsed = utils.hexToNumber(receipt.cumulativeGasUsed);
      receipt.gasUsed = utils.hexToNumber(receipt.gasUsed);
  
      if(_.isArray(receipt.logs)) {
          receipt.logs = receipt.logs.map(outputLogFormatter);
      }
  
      if(receipt.contractAddress) {
          receipt.contractAddress = utils.toChecksumAddress(receipt.contractAddress);
      }
  
      return receipt;
  };
  
  /**
   * Formats the output of a block to its proper values
   *
   * @method outputBlockFormatter
   * @param {Object} block
   * @returns {Object}
  */
  var outputBlockFormatter = function(block) {
  
      // transform to number
      block.gasLimit = utils.hexToNumber(block.gasLimit);
      block.gasUsed = utils.hexToNumber(block.gasUsed);
      block.size = utils.hexToNumber(block.size);
      block.timestamp = utils.hexToNumber(block.timestamp);
      if (block.number !== null)
          block.number = utils.hexToNumber(block.number);
  
      if(block.difficulty)
          block.difficulty = outputBigNumberFormatter(block.difficulty);
      if(block.totalDifficulty)
          block.totalDifficulty = outputBigNumberFormatter(block.totalDifficulty);
  
      if (_.isArray(block.transactions)) {
          block.transactions.forEach(function(item){
              if(!_.isString(item))
                  return outputTransactionFormatter(item);
          });
      }
  
      if (block.miner)
          block.miner = utils.toChecksumAddress(block.miner);
  
      return block;
  };
  
  /**
   * Formats the input of a log
   *
   * @method inputLogFormatter
   * @param {Object} log object
   * @returns {Object} log
  */
  var inputLogFormatter = function(options) {
      var toTopic = function(value){
  
          if(value === null || typeof value === 'undefined')
              return null;
  
          value = String(value);
  
          if(value.indexOf('0x') === 0)
              return value;
          else
              return utils.fromUtf8(value);
      };
  
      // make sure topics, get converted to hex
      options.topics = options.topics || [];
      options.topics = options.topics.map(function(topic){
          return (_.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
      });
  
      toTopic = null;
  
      if(options.address)
          options.address = inputAddressFormatter(options.address);
  
      return options;
  };
  
  /**
   * Formats the output of a log
   *
   * @method outputLogFormatter
   * @param {Object} log object
   * @returns {Object} log
  */
  var outputLogFormatter = function(log) {
  
      // generate a custom log id
      if(typeof log.blockHash === 'string' &&
         typeof log.transactionHash === 'string' &&
         typeof log.logIndex === 'string') {
          var shaId = utils.sha3(log.blockHash.replace('0x','') + log.transactionHash.replace('0x','') + log.logIndex.replace('0x',''));
          log.id = 'log_'+ shaId.replace('0x','').substr(0,8);
      } else if(!log.id) {
          log.id = null;
      }
  
      if (log.blockNumber !== null)
          log.blockNumber = utils.hexToNumber(log.blockNumber);
      if (log.transactionIndex !== null)
          log.transactionIndex = utils.hexToNumber(log.transactionIndex);
      if (log.logIndex !== null)
          log.logIndex = utils.hexToNumber(log.logIndex);
  
      if (log.address)
          log.address = utils.toChecksumAddress(log.address);
  
      return log;
  };
  
  /**
   * Formats the input of a whisper post and converts all values to HEX
   *
   * @method inputPostFormatter
   * @param {Object} transaction object
   * @returns {Object}
  */
  var inputPostFormatter = function(post) {
  
      // post.payload = utils.toHex(post.payload);
  
      if (post.ttl)
          post.ttl = utils.numberToHex(post.ttl);
      if (post.workToProve)
          post.workToProve = utils.numberToHex(post.workToProve);
      if (post.priority)
          post.priority = utils.numberToHex(post.priority);
  
      // fallback
      if (!_.isArray(post.topics)) {
          post.topics = post.topics ? [post.topics] : [];
      }
  
      // format the following options
      post.topics = post.topics.map(function(topic){
          // convert only if not hex
          return (topic.indexOf('0x') === 0) ? topic : utils.fromUtf8(topic);
      });
  
      return post;
  };
  
  /**
   * Formats the output of a received post message
   *
   * @method outputPostFormatter
   * @param {Object}
   * @returns {Object}
   */
  var outputPostFormatter = function(post){
  
      post.expiry = utils.hexToNumber(post.expiry);
      post.sent = utils.hexToNumber(post.sent);
      post.ttl = utils.hexToNumber(post.ttl);
      post.workProved = utils.hexToNumber(post.workProved);
      // post.payloadRaw = post.payload;
      // post.payload = utils.hexToAscii(post.payload);
  
      // if (utils.isJson(post.payload)) {
      //     post.payload = JSON.parse(post.payload);
      // }
  
      // format the following options
      if (!post.topics) {
          post.topics = [];
      }
      post.topics = post.topics.map(function(topic){
          return utils.toUtf8(topic);
      });
  
      return post;
  };
  
  var inputAddressFormatter = function (address) {
      var iban = new Iban(address);
      if (iban.isValid() && iban.isDirect()) {
          return iban.toAddress().toLowerCase();
      } else if (utils.isAddress(address)) {
          return '0x' + address.toLowerCase().replace('0x','');
      }
      throw new Error('Provided address "'+ address +'" is invalid, the capitalization checksum test failed, or its an indrect IBAN address which can\'t be converted.');
  };
  
  
  var outputSyncingFormatter = function(result) {
  
      result.startingBlock = utils.hexToNumber(result.startingBlock);
      result.currentBlock = utils.hexToNumber(result.currentBlock);
      result.highestBlock = utils.hexToNumber(result.highestBlock);
      if (result.knownStates) {
          result.knownStates = utils.hexToNumber(result.knownStates);
          result.pulledStates = utils.hexToNumber(result.pulledStates);
      }
  
      return result;
  };
  
  module.exports = {
      inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter,
      inputBlockNumberFormatter: inputBlockNumberFormatter,
      inputCallFormatter: inputCallFormatter,
      inputTransactionFormatter: inputTransactionFormatter,
      inputAddressFormatter: inputAddressFormatter,
      inputPostFormatter: inputPostFormatter,
      inputLogFormatter: inputLogFormatter,
      inputSignFormatter: inputSignFormatter,
      outputBigNumberFormatter: outputBigNumberFormatter,
      outputTransactionFormatter: outputTransactionFormatter,
      outputTransactionReceiptFormatter: outputTransactionReceiptFormatter,
      outputBlockFormatter: outputBlockFormatter,
      outputLogFormatter: outputLogFormatter,
      outputPostFormatter: outputPostFormatter,
      outputSyncingFormatter: outputSyncingFormatter
  };
  
  