web3.js/lib/utils/utils.js

630 lines
16 KiB
JavaScript
Raw Normal View History

/*
2015-10-08 15:34:07 +08:00
This file is part of web3.js.
2015-10-08 15:34:07 +08:00
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.
2015-10-08 15:34:07 +08:00
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
2015-10-08 15:34:07 +08:00
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
2015-04-20 23:01:21 +02:00
* @file utils.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
2015-03-09 15:09:21 +01:00
/**
* Utils
*
2015-03-09 15:09:21 +01:00
* @module utils
*/
/**
* Utility functions
*
2015-03-09 15:09:21 +01:00
* @class [utils] utils
* @constructor
*/
2015-07-30 15:44:10 +02:00
var BigNumber = require('bignumber.js');
2016-02-25 15:01:07 -03:00
var sha3 = require('./sha3.js');
var utf8 = require('utf8');
2015-02-20 09:49:42 +01:00
2015-02-26 10:56:04 +01:00
var unitMap = {
'noether': '0',
'wei': '1',
'kwei': '1000',
'Kwei': '1000',
'babbage': '1000',
'femtoether': '1000',
'mwei': '1000000',
'Mwei': '1000000',
'lovelace': '1000000',
'picoether': '1000000',
'gwei': '1000000000',
'Gwei': '1000000000',
'shannon': '1000000000',
'nanoether': '1000000000',
'nano': '1000000000',
'szabo': '1000000000000',
'microether': '1000000000000',
'micro': '1000000000000',
'finney': '1000000000000000',
2015-05-22 12:04:06 -03:00
'milliether': '1000000000000000',
'milli': '1000000000000000',
'ether': '1000000000000000000',
'kether': '1000000000000000000000',
'grand': '1000000000000000000000',
'mether': '1000000000000000000000000',
'gether': '1000000000000000000000000000',
'tether': '1000000000000000000000000000000'
2015-02-26 10:56:04 +01:00
};
2015-04-02 13:08:40 +02:00
/**
* Should be called to pad string to expected length
*
* @method padLeft
* @param {String} string to be padded
* @param {Number} characters that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
2015-02-26 10:56:04 +01:00
/**
* Should be called to pad string to expected length
*
* @method padRight
* @param {String} string to be padded
* @param {Number} characters that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var padRight = function (string, chars, sign) {
return string + (new Array(chars - string.length + 1).join(sign ? sign : "0"));
};
/**
2015-08-10 22:20:41 +02:00
* Should be called to get utf8 from it's hex representation
2015-03-08 10:36:16 +01:00
*
2015-08-10 22:11:37 +02:00
* @method toUtf8
2015-03-08 10:36:16 +01:00
* @param {String} string in hex
* @returns {String} ascii string representation of hex value
*/
2015-08-10 22:11:37 +02:00
var toUtf8 = function(hex) {
2015-01-31 13:54:39 +01:00
// Find termination
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);
if (code === 0)
break;
2015-01-31 13:54:39 +01:00
str += String.fromCharCode(code);
}
return utf8.decode(str);
2015-01-31 13:54:39 +01:00
};
/**
2015-08-10 22:20:41 +02:00
* Should be called to get ascii from it's hex representation
*
* @method toAscii
* @param {String} string in hex
* @returns {String} ascii string representation of hex value
*/
var toAscii = function(hex) {
// Find termination
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;
};
2015-03-08 10:36:16 +01:00
/**
2016-02-19 11:05:42 -02:00
* Should be called to get hex representation (prefixed by 0x) of utf8 string
2015-03-08 10:36:16 +01:00
*
2015-08-10 22:11:37 +02:00
* @method fromUtf8
2015-03-08 10:36:16 +01:00
* @param {String} string
* @param {Number} optional padding
2015-03-08 10:36:16 +01:00
* @returns {String} hex representation of input string
*/
2015-08-10 22:11:37 +02:00
var fromUtf8 = function(str) {
str = utf8.encode(str);
2015-01-31 13:54:39 +01:00
var hex = "";
for(var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
if (code === 0)
break;
var n = code.toString(16);
2015-01-31 13:54:39 +01:00
hex += n.length < 2 ? '0' + n : n;
}
return "0x" + hex;
};
2015-08-10 22:20:41 +02:00
/**
* Should be called to get hex representation (prefixed by 0x) of ascii string
2015-08-10 22:20:41 +02:00
*
* @method fromAscii
* @param {String} string
* @param {Number} optional padding
* @returns {String} hex representation of input string
*/
var fromAscii = function(str) {
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;
};
2015-04-20 23:01:21 +02:00
/**
* Should be used to create full function/event name from json abi
*
* @method transformToFullName
* @param {Object} json-abi
* @return {String} full fnction/event name
*/
var transformToFullName = function (json) {
if (json.name.indexOf('(') !== -1) {
return json.name;
}
var typeName = json.inputs.map(function(i){return i.type; }).join();
return json.name + '(' + typeName + ')';
};
2015-03-08 10:36:16 +01:00
/**
* Should be called to get display name of contract function
*
2015-03-08 10:36:16 +01:00
* @method extractDisplayName
* @param {String} name of function/event
* @returns {String} display name for function/event eg. multiply(uint256) -> multiply
*/
2015-01-31 15:22:05 +01:00
var extractDisplayName = function (name) {
var length = name.indexOf('(');
2015-01-31 15:22:05 +01:00
return length !== -1 ? name.substr(0, length) : name;
};
/// @returns overloaded part of function/event name
var extractTypeName = function (name) {
/// TODO: make it invulnerable
var length = name.indexOf('(');
2015-02-03 20:12:56 +01:00
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : "";
2015-01-31 15:22:05 +01:00
};
2015-03-07 19:55:20 +01:00
/**
* Converts value to it's decimal representation in string
*
* @method toDecimal
* @param {String|Number|BigNumber}
* @return {String}
*/
var toDecimal = function (value) {
2015-03-09 11:27:19 +01:00
return toBigNumber(value).toNumber();
2015-02-20 09:49:42 +01:00
};
2015-02-20 09:34:26 +01:00
2015-03-07 19:55:20 +01:00
/**
* Converts value to it's hex representation
*
* @method fromDecimal
* @param {String|Number|BigNumber}
* @return {String}
*/
var fromDecimal = function (value) {
2015-03-09 15:09:21 +01:00
var number = toBigNumber(value);
var result = number.toString(16);
2015-03-11 10:24:03 +01:00
return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result;
2015-02-24 13:00:24 +01:00
};
2015-03-07 19:55:20 +01:00
/**
2015-03-09 15:09:21 +01:00
* Auto converts any given value into it's hex representation.
*
* And even stringifys objects before.
2015-03-07 19:55:20 +01:00
*
* @method toHex
2015-03-09 15:09:21 +01:00
* @param {String|Number|BigNumber|Object}
2015-03-07 19:55:20 +01:00
* @return {String}
*/
2015-03-09 15:09:21 +01:00
var toHex = function (val) {
2015-06-19 13:28:59 +02:00
/*jshint maxcomplexity: 8 */
2015-03-09 15:09:21 +01:00
2015-03-23 15:19:42 +01:00
if (isBoolean(val))
return fromDecimal(+val);
2015-03-10 11:03:03 +01:00
2015-03-23 15:19:42 +01:00
if (isBigNumber(val))
2015-03-09 15:09:21 +01:00
return fromDecimal(val);
if (typeof val === 'object')
2015-08-10 22:11:37 +02:00
return fromUtf8(JSON.stringify(val));
2015-03-09 15:09:21 +01:00
// if its a negative number, pass it through fromDecimal
2015-03-10 16:55:57 +01:00
if (isString(val)) {
if (val.indexOf('-0x') === 0)
2015-06-03 16:55:45 +02:00
return fromDecimal(val);
else if(val.indexOf('0x') === 0)
return val;
else if (!isFinite(val))
return fromAscii(val);
2015-03-10 16:55:57 +01:00
}
2015-03-09 15:09:21 +01:00
2015-03-10 16:55:57 +01:00
return fromDecimal(val);
2015-02-27 12:41:07 +01:00
};
2015-03-07 19:18:22 +01:00
/**
* Returns value of unit in Wei
*
* @method getValueOfUnit
* @param {String} unit the unit to convert to, default ether
* @returns {BigNumber} value of the unit (in Wei)
* @throws error if the unit is not correct:w
*/
var getValueOfUnit = function (unit) {
unit = unit ? unit.toLowerCase() : 'ether';
var unitValue = unitMap[unit];
if (unitValue === undefined) {
throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2));
}
return new BigNumber(unitValue, 10);
};
2015-02-27 12:41:07 +01:00
2015-02-20 09:34:26 +01:00
/**
* 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
2015-05-22 12:04:06 -03:00
* - -- 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
2015-03-07 19:18:22 +01:00
* @param {String} unit the unit to convert to, default ether
* @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number
2015-02-20 09:34:26 +01:00
*/
var fromWei = function(number, unit) {
2015-03-09 18:21:11 +01:00
var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit));
return isBigNumber(number) ? returnValue : returnValue.toString(10);
2015-02-20 09:34:26 +01:00
};
/**
* Takes a number of a unit and converts it to wei.
*
* Possible units are:
* SI Short SI Full Effigy Other
2016-02-25 11:13:34 -03:00
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- microether szabo micro
2015-05-22 12:04:06 -03:00
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method toWei
* @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal
2015-03-07 19:18:22 +01:00
* @param {String} unit the unit to convert from, default ether
* @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number
2015-02-20 09:34:26 +01:00
*/
var toWei = function(number, unit) {
2015-03-09 18:21:11 +01:00
var returnValue = toBigNumber(number).times(getValueOfUnit(unit));
return isBigNumber(number) ? returnValue : returnValue.toString(10);
2015-02-20 09:34:26 +01:00
};
/**
* Takes an input and transforms it into an bignumber
*
* @method toBigNumber
* @param {Number|String|BigNumber} a number, string, HEX string or BigNumber
2015-03-08 17:28:32 +01:00
* @return {BigNumber} BigNumber
2015-02-20 09:34:26 +01:00
*/
var toBigNumber = function(number) {
2015-03-11 10:24:03 +01:00
/*jshint maxcomplexity:5 */
number = number || 0;
2015-03-09 11:27:19 +01:00
if (isBigNumber(number))
return number;
2015-02-20 09:34:26 +01:00
2015-03-11 10:24:03 +01:00
if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) {
return new BigNumber(number.replace('0x',''), 16);
}
2015-03-11 10:24:03 +01:00
return new BigNumber(number.toString(10), 10);
};
2015-03-08 17:28:32 +01:00
/**
* Takes and input transforms it into bignumber and if it is negative value, into two's complement
*
* @method toTwosComplement
* @param {Number|String|BigNumber}
* @return {BigNumber}
*/
var toTwosComplement = function (number) {
var bigNumber = toBigNumber(number).round();
2015-03-08 17:28:32 +01:00
if (bigNumber.lessThan(0)) {
return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1);
}
return bigNumber;
};
2015-02-26 15:54:49 +01:00
/**
* Checks if the given string is strictly an address
*
* @method isStrictAddress
* @param {String} address the given HEX adress
* @return {Boolean}
*/
var isStrictAddress = function (address) {
2015-09-10 14:44:34 +02:00
return /^0x[0-9a-f]{40}$/i.test(address);
};
/**
* Checks if the given string is an address
*
* @method isAddress
* @param {String} address the given HEX adress
* @return {Boolean}
2015-02-26 15:54:49 +01:00
*/
2015-04-02 13:08:40 +02:00
var isAddress = function (address) {
2016-02-19 11:05:42 -02:00
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
// check if it has the basic requirements of an address
return false;
} else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) {
// If it's all small caps or all all caps, return true
return true;
} else {
// Otherwise check each case
2016-02-22 15:17:32 -03:00
return isChecksumAddress(address);
}
};
/**
* Checks if the given string is a checksummed address
*
* @method isChecksumAddress
* @param {String} address the given HEX adress
* @return {Boolean}
*/
var isChecksumAddress = function (address) {
2016-02-22 15:17:32 -03:00
// Check each case
address = address.replace('0x','');
2016-02-25 11:10:43 -03:00
var addressHash = sha3(address.toLowerCase());
2016-02-22 15:17:32 -03:00
for (var i = 0; i < 40; i++ ) {
2016-02-22 15:17:32 -03:00
// the nth letter should be uppercase if the nth digit of casemap is 1
2016-02-25 11:10:43 -03:00
if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) {
2016-02-22 15:17:32 -03:00
return false;
2016-02-19 11:05:42 -02:00
}
}
return true;
2016-02-19 11:05:42 -02:00
};
2016-02-22 15:17:32 -03:00
2016-02-19 11:05:42 -02:00
/**
* Makes a checksum address
*
* @method toChecksumAddress
* @param {String} address the given HEX adress
* @return {String}
*/
var toChecksumAddress = function (address) {
2016-02-25 11:10:43 -03:00
if (typeof address === 'undefined') return '';
2016-02-23 16:54:44 -03:00
2016-02-19 11:05:42 -02:00
address = address.toLowerCase().replace('0x','');
2016-02-25 11:10:43 -03:00
var addressHash = sha3(address);
2016-02-22 12:19:50 -03:00
var checksumAddress = '0x';
2016-02-19 11:05:42 -02:00
for (var i = 0; i < address.length; i++ ) {
// If ith character is 9 to f then make it uppercase
2016-02-23 08:40:27 -03:00
if (parseInt(addressHash[i], 16) > 7) {
2016-02-19 11:05:42 -02:00
checksumAddress += address[i].toUpperCase();
} else {
checksumAddress += address[i];
}
}
return checksumAddress;
2015-04-02 13:08:40 +02:00
};
/**
* Transforms given string to valid 20 bytes-length addres with 0x prefix
*
* @method toAddress
* @param {String} address
* @return {String} formatted address
*/
var toAddress = function (address) {
if (isStrictAddress(address)) {
2015-04-02 13:08:40 +02:00
return address;
}
2015-04-02 13:08:40 +02:00
if (/^[0-9a-f]{40}$/.test(address)) {
return '0x' + address;
2015-03-08 16:56:21 +01:00
}
2015-04-02 13:08:40 +02:00
return '0x' + padLeft(toHex(address).substr(2), 40);
};
2015-02-26 15:54:49 +01:00
/**
* Returns true if object is BigNumber, otherwise false
*
* @method isBigNumber
* @param {Object}
* @return {Boolean}
*/
var isBigNumber = function (object) {
return object instanceof BigNumber ||
(object && object.constructor && object.constructor.name === 'BigNumber');
2015-02-26 15:54:49 +01:00
};
/**
* Returns true if object is string, otherwise false
*
* @method isString
* @param {Object}
* @return {Boolean}
*/
var isString = function (object) {
2015-03-08 10:07:32 +01:00
return typeof object === 'string' ||
(object && object.constructor && object.constructor.name === 'String');
};
2015-02-26 15:54:49 +01:00
2015-03-07 19:55:20 +01:00
/**
* Returns true if object is function, otherwise false
*
* @method isFunction
* @param {Object}
* @return {Boolean}
*/
var isFunction = function (object) {
return typeof object === 'function';
};
2015-03-10 16:55:57 +01:00
/**
* Returns true if object is Objet, otherwise false
*
* @method isObject
* @param {Object}
* @return {Boolean}
*/
var isObject = function (object) {
return object !== null && !(object instanceof Array) && typeof object === 'object';
2015-03-10 16:55:57 +01:00
};
/**
* Returns true if object is boolean, otherwise false
*
* @method isBoolean
* @param {Object}
* @return {Boolean}
*/
var isBoolean = function (object) {
return typeof object === 'boolean';
};
2015-03-11 10:24:03 +01:00
/**
* Returns true if object is array, otherwise false
*
* @method isArray
* @param {Object}
* @return {Boolean}
*/
var isArray = function (object) {
return object instanceof Array;
2015-03-11 10:24:03 +01:00
};
2015-03-21 22:57:44 +01:00
/**
* Returns true if given string is valid json object
*
2015-03-21 22:57:44 +01:00
* @method isJson
* @param {String}
* @return {Boolean}
*/
var isJson = function (str) {
try {
2015-03-25 22:17:35 +01:00
return !!JSON.parse(str);
2015-03-21 22:57:44 +01:00
} catch (e) {
return false;
}
};
2017-02-17 11:10:34 +01:00
/**
* Returns true if given string is a valid Ethereum block header bloom.
*
* @method isBloom
* @param {String} hex encoded bloom filter
* @return {Boolean}
*/
var isBloom = function (bloom) {
if (!/^(0x)?[0-9a-f]{512}$/i.test(bloom)) {
return false;
} else if (/^(0x)?[0-9a-f]{512}$/.test(bloom) || /^(0x)?[0-9A-F]{512}$/.test(bloom)) {
return true;
}
2017-02-17 11:10:34 +01:00
return false;
2017-02-17 12:13:26 +01:00
};
2017-02-17 11:10:34 +01:00
/**
* Returns true if given string is a valid log topic.
*
* @method isTopic
* @param {String} hex encoded topic
* @return {Boolean}
*/
var isTopic = function (topic) {
if (!/^(0x)?[0-9a-f]{64}$/i.test(topic)) {
return false;
} else if (/^(0x)?[0-9a-f]{64}$/.test(topic) || /^(0x)?[0-9A-F]{64}$/.test(topic)) {
return true;
}
2017-02-17 11:10:34 +01:00
return false;
2017-02-17 12:13:26 +01:00
};
2017-02-17 11:10:34 +01:00
module.exports = {
2015-04-02 13:08:40 +02:00
padLeft: padLeft,
padRight: padRight,
2015-02-27 12:41:07 +01:00
toHex: toHex,
2015-02-20 09:49:42 +01:00
toDecimal: toDecimal,
2015-02-24 13:00:24 +01:00
fromDecimal: fromDecimal,
2015-08-10 22:11:37 +02:00
toUtf8: toUtf8,
2015-08-10 22:20:41 +02:00
toAscii: toAscii,
2015-08-10 22:11:37 +02:00
fromUtf8: fromUtf8,
2015-08-10 22:20:41 +02:00
fromAscii: fromAscii,
2015-04-20 23:01:21 +02:00
transformToFullName: transformToFullName,
2015-01-31 15:22:05 +01:00
extractDisplayName: extractDisplayName,
extractTypeName: extractTypeName,
2015-02-20 09:34:26 +01:00
toWei: toWei,
fromWei: fromWei,
2015-02-26 15:54:49 +01:00
toBigNumber: toBigNumber,
2015-03-08 17:28:32 +01:00
toTwosComplement: toTwosComplement,
2015-04-02 13:08:40 +02:00
toAddress: toAddress,
isBigNumber: isBigNumber,
isStrictAddress: isStrictAddress,
2015-03-07 19:55:20 +01:00
isAddress: isAddress,
2016-02-22 15:17:32 -03:00
isChecksumAddress: isChecksumAddress,
2016-02-19 11:05:42 -02:00
toChecksumAddress: toChecksumAddress,
2015-03-08 08:33:22 +01:00
isFunction: isFunction,
2015-03-10 16:55:57 +01:00
isString: isString,
isObject: isObject,
2015-03-11 10:24:03 +01:00
isBoolean: isBoolean,
2015-03-21 22:57:44 +01:00
isArray: isArray,
2017-02-17 11:10:34 +01:00
isJson: isJson,
isBloom: isBloom,
isTopic: isTopic,
};