UNPKG

webu

Version:

IrChain JavaScript API, middleware to talk to a irchain node over RPC

600 lines (542 loc) 15.7 kB
/* This file is part of webu.js. webu.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. webu.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 webu.js. If not, see <http://www.gnu.org/licenses/>. */ /** * @file utils.js * @author Marek Kotewicz <marek@ethdev.com> * @date 2015 */ /** * Utils * * @module utils */ /** * Utility functions * * @class [utils] utils * @constructor */ var BigNumber = require('bignumber.js'); var sha3 = require('./sha3.js'); var utf8 = require('utf8'); var unitMap = { 'wei' : '1', 'kwei' : '1000', 'mwei' : '1000000', 'gwei' : '1000000000', 'twei' : '1000000000000', 'pwei' : '1000000000000000', 'irc' : '1000000000000000000', 'kirc' : '1000000000000000000000', 'mirc' : '1000000000000000000000000', 'girc' : '1000000000000000000000000000', 'tirc' : '1000000000000000000000000000000', 'pirc' : '1000000000000000000000000000000000', }; /** * Should be called to pad string to expected length * * @method padLeft * @param {String} string to be padded * @param chars * @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; }; /** * Should be called to pad string to expected length * * @method padRight * @param {String} string to be padded * @param chars * @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')); }; /** * Should be called to get utf8 from it's hex representation * * @method toUtf8 * @returns {String} ascii string representation of hex value * @param hex */ var toUtf8 = 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); if (code === 0) break; str += String.fromCharCode(code); } return utf8.decode(str); }; /** * Should be called to get ascii from it's hex representation * * @method toAscii * @returns {String} ascii string representation of hex value * @param hex */ 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; }; /** * Should be called to get hex representation (prefixed by 0x) of utf8 string * * @method fromUtf8 * @param str * @param {String | Boolean} allowZero to convert code point zero to 00 instead of end of string * @returns {String} hex representation of input string */ var fromUtf8 = function(str, allowZero) { str = utf8.encode(str); var hex = ''; for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); if (code === 0) { if (allowZero) { hex += '00'; } else { break; } } else { var n = code.toString(16); hex += n.length < 2 ? '0' + n : n; } } return '0x' + hex; }; /** * Should be called to get hex representation (prefixed by 0x) of ascii string * * @method fromAscii * @returns {String} hex representation of input string * @param str */ 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; }; /** * Should be used to create full function/event name from json abi * * @method transformToFullName * @return {String} full fnction/event name * @param json */ 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 + ')'; }; /** * Should be called to get display name of contract function * * @method extractDisplayName * @param {String} name of function/event * @returns {String} display name for function/event eg. multiply(uint256) -> multiply */ var extractDisplayName = function(name) { var stBracket = name.indexOf('('); var endBracket = name.indexOf(')'); return (stBracket !== -1 && endBracket !== -1) ? name.substr(0, stBracket) : name; }; /** * Should be called to get type name of contract function * * @method extractTypeName * @param {String} name of function/event * @returns {String} type name for function/event eg. multiply(uint256) -> uint256 */ var extractTypeName = function(name) { var stBracket = name.indexOf('('); var endBracket = name.indexOf(')'); return (stBracket !== -1 && endBracket !== -1) ? name.substr(stBracket + 1, endBracket - stBracket - 1).replace(' ', '') : ''; }; /** * Converts value to it's decimal representation in string * * @method toDecimal * @return {String} * @param value */ var toDecimal = function(value) { return toBigNumber(value).toNumber(); }; /** * Converts value to it's hex representation * * @method fromDecimal * @return {String} * @param value */ var fromDecimal = function(value) { var number = toBigNumber(value); var result = number.toString(16); return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result; }; /** * Auto converts any given value into it's hex representation. * * And even stringifys objects before. * * @method toHex * @return {String} * @param val */ var toHex = function(val) { /*jshint maxcomplexity: 8 */ if (isBoolean(val)) return fromDecimal(+val); if (isBigNumber(val)) return fromDecimal(val); if (typeof val === 'object') return fromUtf8(JSON.stringify(val)); // if its a negative number, pass it through fromDecimal if (isString(val)) { if (val.indexOf('-0x') === 0) { return fromDecimal(val); } else if (val.indexOf('0x') === 0) { return val; } else if (!isFinite(val)) { return fromUtf8(val, 1); } } return fromDecimal(val); }; /** * Returns value of unit in Wei * * @method getValueOfUnit * @param {String} unit the unit to convert to, default irc * @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() : 'irc'; 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); }; /** * Takes a number of wei and converts it to any other irc unit. * @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 irc * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number */ var fromWei = function(number, unit) { var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); return isBigNumber(number) ? returnValue : returnValue.toString(10); }; /** * Takes a number of a unit and converts it to wei. * @method toWei * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal * @param {String} unit the unit to convert from, default irc * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number */ var toWei = function(number, unit) { var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); return isBigNumber(number) ? returnValue : returnValue.toString(10); }; /** * Takes an input and transforms it into an bignumber * * @method toBigNumber * @return {BigNumber} BigNumber * @param number */ var toBigNumber = function(number) { /*jshint maxcomplexity:5 */ number = number || 0; if (isBigNumber(number)) return number; if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { return new BigNumber(number.replace('0x', ''), 16); } return new BigNumber(number.toString(10), 10); }; /** * Takes and input transforms it into bignumber and if it is negative value, into two's complement * * @method toTwosComplement * @return {BigNumber} * @param number */ var toTwosComplement = function(number) { var bigNumber = toBigNumber(number).round(); if (bigNumber.lessThan(0)) { return new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16).plus(bigNumber).plus(1); } return bigNumber; }; /** * Checks if the given string is strictly an address * * @method isStrictAddress * @param {String} address the given HEX adress * @return {Boolean} */ var isStrictAddress = function(address) { 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} */ var isAddress = function(address) { 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 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) { // Check each case address = address.replace('0x', ''); var addressHash = sha3(address.toLowerCase()); for (var i = 0; i < 40; i++) { // the nth letter should be uppercase if the nth digit of casemap is 1 if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) { return false; } } return true; }; /** * Makes a checksum address * * @method toChecksumAddress * @param {String} address the given HEX adress * @return {String} */ var toChecksumAddress = function(address) { if (typeof address === 'undefined') return ''; address = address.toLowerCase().replace('0x', ''); var addressHash = sha3(address); var checksumAddress = '0x'; for (var i = 0; i < address.length; i++) { // If ith character is 9 to f then make it uppercase if (parseInt(addressHash[i], 16) > 7) { checksumAddress += address[i].toUpperCase(); } else { checksumAddress += address[i]; } } return checksumAddress; }; /** * 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)) { return address; } if (/^[0-9a-f]{40}$/.test(address)) { return '0x' + address; } return '0x' + padLeft(toHex(address).substr(2), 40); }; /** * Returns true if object is BigNumber, otherwise false * * @method isBigNumber * @return {Boolean} * @param object */ var isBigNumber = function(object) { return object instanceof BigNumber || (object && object.constructor && object.constructor.name === 'BigNumber'); }; /** * Returns true if object is string, otherwise false * * @method isString * @return {Boolean} * @param object */ var isString = function(object) { return typeof object === 'string' || (object && object.constructor && object.constructor.name === 'String'); }; /** * Returns true if object is function, otherwise false * * @method isFunction * @return {Boolean} * @param object */ var isFunction = function(object) { return typeof object === 'function'; }; /** * Returns true if object is Objet, otherwise false * * @method isObject * @return {Boolean} * @param object */ var isObject = function(object) { return object !== null && !(Array.isArray(object)) && typeof object === 'object'; }; /** * Returns true if object is boolean, otherwise false * * @method isBoolean * @return {Boolean} * @param object */ var isBoolean = function(object) { return typeof object === 'boolean'; }; /** * Returns true if object is array, otherwise false * * @method isArray * @return {Boolean} * @param object */ var isArray = function(object) { return Array.isArray(object); }; /** * Returns true if given string is valid json object * * @method isJson * @return {Boolean} * @param str */ var isJson = function(str) { try { return !!JSON.parse(str); } catch (e) { return false; } }; /** * Returns true if given string is a valid irchain block header bloom. * * @method isBloom * @return {Boolean} * @param bloom */ 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; } return false; }; /** * Returns true if given string is a valid log topic. * * @method isTopic * @return {Boolean} * @param topic */ 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; } return false; }; module.exports = { padLeft : padLeft, padRight : padRight, toHex : toHex, toDecimal : toDecimal, fromDecimal : fromDecimal, toUtf8 : toUtf8, toAscii : toAscii, fromUtf8 : fromUtf8, fromAscii : fromAscii, transformToFullName: transformToFullName, extractDisplayName : extractDisplayName, extractTypeName : extractTypeName, toWei : toWei, fromWei : fromWei, toBigNumber : toBigNumber, toTwosComplement : toTwosComplement, toAddress : toAddress, isBigNumber : isBigNumber, isStrictAddress : isStrictAddress, isAddress : isAddress, isChecksumAddress : isChecksumAddress, toChecksumAddress : toChecksumAddress, isFunction : isFunction, isString : isString, isObject : isObject, isBoolean : isBoolean, isArray : isArray, isJson : isJson, isBloom : isBloom, isTopic : isTopic, };