/*
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 .
*/
/**
* @file utils.js
* @author Marek Kotewicz
* @author Fabian Vogelsteller
* @date 2017
*/
var ethjsUnit = require('ethjs-unit');
var utils = require('./utils.js');
var soliditySha3 = require('./soliditySha3.js');
var randombytes = require('randombytes');
var BN = require('bn.js');
/**
* Fires an error in an event emitter and callback and returns the eventemitter
*
* @method _fireError
* @param {Object} error a string, a error, or an object with {message, data}
* @param {Object} emitter
* @param {Function} reject
* @param {Function} callback
* @param {any} optionalData
* @return {Object} the emitter
*/
var _fireError = function (error, emitter, reject, callback, optionalData) {
/*jshint maxcomplexity: 10 */
// add data if given
if (!!error && typeof error === 'object' && !(error instanceof Error) && error.data) {
if (!!error.data && typeof error.data === 'object' || Array.isArray(error.data)) {
error.data = JSON.stringify(error.data, null, 2);
}
error = error.message + "\n" + error.data;
}
if (typeof error === 'string') {
error = new Error(error);
}
if (typeof callback === 'function') {
callback(error, optionalData);
}
if (typeof reject === 'function') {
// suppress uncatched error if an error listener is present
// OR suppress uncatched error if an callback listener is present
if (emitter &&
(typeof emitter.listeners === 'function' &&
emitter.listeners('error').length) || typeof callback === 'function') {
emitter.catch(function () { });
}
// reject later, to be able to return emitter
setTimeout(function () {
reject(error);
}, 1);
}
if (emitter && typeof emitter.emit === 'function') {
// emit later, to be able to return emitter
setTimeout(function () {
emitter.emit('error', error, optionalData);
emitter.removeAllListeners();
}, 1);
}
return emitter;
};
/**
* Should be used to create full function/event name from json abi
*
* @method _jsonInterfaceMethodToString
* @param {Object} json
* @return {String} full function/event name
*/
var _jsonInterfaceMethodToString = function (json) {
if (!!json && typeof json === 'object' && json.name && json.name.indexOf('(') !== -1) {
return json.name;
}
return json.name + '(' + _flattenTypes(false, json.inputs).join(',') + ')';
};
/**
* Should be used to flatten json abi inputs/outputs into an array of type-representing-strings
*
* @method _flattenTypes
* @param {bool} includeTuple
* @param {Object} puts
* @return {Array} parameters as strings
*/
var _flattenTypes = function (includeTuple, puts) {
// console.log("entered _flattenTypes. inputs/outputs: " + puts)
var types = [];
puts.forEach(function (param) {
if (typeof param.components === 'object') {
if (param.type.substring(0, 5) !== 'tuple') {
throw new Error('components found but type is not tuple; report on GitHub');
}
var suffix = '';
var arrayBracket = param.type.indexOf('[');
if (arrayBracket >= 0) {
suffix = param.type.substring(arrayBracket);
}
var result = _flattenTypes(includeTuple, param.components);
// console.log("result should have things: " + result)
if (Array.isArray(result) && includeTuple) {
// console.log("include tuple word, and its an array. joining...: " + result.types)
types.push('tuple(' + result.join(',') + ')' + suffix);
}
else if (!includeTuple) {
// console.log("don't include tuple, but its an array. joining...: " + result)
types.push('(' + result.join(',') + ')' + suffix);
}
else {
// console.log("its a single type within a tuple: " + result.types)
types.push('(' + result + ')');
}
}
else {
// console.log("its a type and not directly in a tuple: " + param.type)
types.push(param.type);
}
});
return types;
};
/**
* Returns a random hex string by the given bytes size
*
* @param {Number} size
* @returns {string}
*/
var randomHex = function (size) {
return '0x' + randombytes(size).toString('hex');
};
/**
* Should be called to get ascii from it's hex representation
*
* @method hexToAscii
* @param {String} hex
* @returns {String} ascii string representation of hex value
*/
var hexToAscii = function (hex) {
if (!utils.isHexStrict(hex))
throw new Error('The parameter must be a valid HEX string.');
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x') {
i = 2;
}
for (; i < l; i += 2) {
var code = parseInt(hex.substr(i, 2), 16);
str += String.fromCharCode(code);
}
return str;
};
/**
* Should be called to get hex representation (prefixed by 0x) of ascii string
*
* @method asciiToHex
* @param {String} str
* @returns {String} hex representation of input string
*/
var asciiToHex = function (str) {
if (!str)
return "0x00";
var hex = "";
for (var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
var n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return "0x" + hex;
};
/**
* Returns value of unit in Wei
*
* @method getUnitValue
* @param {String} unit the unit to convert to, default ether
* @returns {BN} value of the unit (in Wei)
* @throws error if the unit is not correct:w
*/
var getUnitValue = function (unit) {
unit = unit ? unit.toLowerCase() : 'ether';
if (!ethjsUnit.unitMap[unit]) {
throw new Error('This unit "' + unit + '" doesn\'t exist, please use the one of the following units' + JSON.stringify(ethjsUnit.unitMap, null, 2));
}
return unit;
};
/**
* Takes a number of wei and converts it to any other ether unit.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method fromWei
* @param {Number|String} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert to, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var fromWei = function (number, unit) {
unit = getUnitValue(unit);
if (!utils.isBN(number) && !(typeof number === 'string')) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.fromWei(number, unit) : ethjsUnit.fromWei(number, unit).toString(10);
};
/**
* Takes a number of a unit and converts it to wei.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method toWei
* @param {Number|String|BN} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert from, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var toWei = function (number, unit) {
unit = getUnitValue(unit);
if (!utils.isBN(number) && !(typeof number === 'string')) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.toWei(number, unit) : ethjsUnit.toWei(number, unit).toString(10);
};
/**
* Converts to a checksum address
*
* @method toChecksumAddress
* @param {String} address the given HEX address
* @return {String}
*/
var toChecksumAddress = function (address) {
if (typeof address === 'undefined')
return '';
if (!/^(0x)?[0-9a-f]{40}$/i.test(address))
throw new Error('Given address "' + address + '" is not a valid Ethereum address.');
address = address.toLowerCase().replace(/^0x/i, '');
var addressHash = utils.sha3(address).replace(/^0x/i, '');
var checksumAddress = '0x';
for (var i = 0; i < address.length; i++) {
// If ith character is 8 to f then make it uppercase
if (parseInt(addressHash[i], 16) > 7) {
checksumAddress += address[i].toUpperCase();
}
else {
checksumAddress += address[i];
}
}
return checksumAddress;
};
/**
* Returns -1 if ab; 0 if a == b.
* For more details on this type of function, see
* developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
*
* @method compareBlockNumbers
*
* @param {String|Number|BN} a
*
* @param {String|Number|BN} b
*
* @returns {Number} -1, 0, or 1
*/
var compareBlockNumbers = function (a, b) {
if (a == b) {
return 0;
}
else if (("genesis" == a || "earliest" == a || 0 == a) && ("genesis" == b || "earliest" == b || 0 == b)) {
return 0;
}
else if ("genesis" == a || "earliest" == a) {
// b !== a, thus a < b
return -1;
}
else if ("genesis" == b || "earliest" == b) {
// b !== a, thus a > b
return 1;
}
else if (a == "latest") {
if (b == "pending") {
return -1;
}
else {
// b !== ("pending" OR "latest"), thus a > b
return 1;
}
}
else if (b === "latest") {
if (a == "pending") {
return 1;
}
else {
// b !== ("pending" OR "latest"), thus a > b
return -1;
}
}
else if (a == "pending") {
// b (== OR <) "latest", thus a > b
return 1;
}
else if (b == "pending") {
return -1;
}
else {
let bnA = new BN(a);
let bnB = new BN(b);
if (bnA.lt(bnB)) {
return -1;
}
else if (bnA.eq(bnB)) {
return 0;
}
else {
return 1;
}
}
};
module.exports = {
_fireError: _fireError,
_jsonInterfaceMethodToString: _jsonInterfaceMethodToString,
_flattenTypes: _flattenTypes,
// extractDisplayName: extractDisplayName,
// extractTypeName: extractTypeName,
randomHex: randomHex,
BN: utils.BN,
isBN: utils.isBN,
isBigNumber: utils.isBigNumber,
isHex: utils.isHex,
isHexStrict: utils.isHexStrict,
sha3: utils.sha3,
sha3Raw: utils.sha3Raw,
keccak256: utils.sha3,
soliditySha3: soliditySha3.soliditySha3,
soliditySha3Raw: soliditySha3.soliditySha3Raw,
encodePacked: soliditySha3.encodePacked,
isAddress: utils.isAddress,
checkAddressChecksum: utils.checkAddressChecksum,
toChecksumAddress: toChecksumAddress,
toHex: utils.toHex,
toBN: utils.toBN,
bytesToHex: utils.bytesToHex,
hexToBytes: utils.hexToBytes,
hexToNumberString: utils.hexToNumberString,
hexToNumber: utils.hexToNumber,
toDecimal: utils.hexToNumber,
numberToHex: utils.numberToHex,
fromDecimal: utils.numberToHex,
hexToUtf8: utils.hexToUtf8,
hexToString: utils.hexToUtf8,
toUtf8: utils.hexToUtf8,
stripHexPrefix: utils.stripHexPrefix,
utf8ToHex: utils.utf8ToHex,
stringToHex: utils.utf8ToHex,
fromUtf8: utils.utf8ToHex,
hexToAscii: hexToAscii,
toAscii: hexToAscii,
asciiToHex: asciiToHex,
fromAscii: asciiToHex,
unitMap: ethjsUnit.unitMap,
toWei: toWei,
fromWei: fromWei,
padLeft: utils.leftPad,
leftPad: utils.leftPad,
padRight: utils.rightPad,
rightPad: utils.rightPad,
toTwosComplement: utils.toTwosComplement,
isBloom: utils.isBloom,
isUserEthereumAddressInBloom: utils.isUserEthereumAddressInBloom,
isContractAddressInBloom: utils.isContractAddressInBloom,
isTopic: utils.isTopic,
isTopicInBloom: utils.isTopicInBloom,
isInBloom: utils.isInBloom,
compareBlockNumbers: compareBlockNumbers,
toNumber: utils.toNumber
};