UNPKG

@colony/purser-core

Version:

A collection of helpers, utils, validators and normalizers to assist the individual purser modules

340 lines (297 loc) 11.2 kB
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/esm/inherits"; import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _typeof from "@babel/runtime/helpers/esm/typeof"; import BN from 'bn.js'; import { ENV, WEI_MINIFICATION, GWEI_MINIFICATION, DESCRIPTORS } from './defaults'; import { utils as messages } from './messages'; /** * Simple helper to determine if we should output messages to the console * based on the environment the modules have been built in * * @method verbose * * @return {boolean} Do we output to the console, or not? */ export var verbose = function verbose() { if (typeof ENV === 'undefined') { return true; } if (ENV === 'development') { return true; } return false; }; /** * If we're in `dev` mode, show an warning to the console * * This way you won't have to explicitly tell it which message from `messages.js` to show * Arguments will be split into three types: * First arg will be the message string * Rest of them will be template literals that will replace %s values in the previous messsage string (with one exception) * If the last argument is an object that has only one prop named `level`, it will be interpreted as an option object * (if level equals `low` it will only warn, if the level equals `high`, it will error) * * @method warning * * @param {any} args Arguments array that will be passed down to `console` methods (see above) */ export var warning = function warning() { var _console; /* * Stop everything if we're in production mode. * No point in doing all the computations and assignments if we don't have to. */ if (!verbose()) { return undefined; } var level = 'low'; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var lastArgIndex = args.length - 1; var options = args[lastArgIndex]; var message = args[0]; var literalTemplates = args.slice(1); /* * We're being very specific with object testing here, since we don't want to * highjack a legitimate object that comes in as a template part (althogh * this is very unlikely) */ if (_typeof(options) === 'object' && typeof options.level === 'string' && Object.keys(options).length === 1) { level = options.level; literalTemplates.pop(); } var warningType = 'warn'; if (level === 'high') { warningType = 'error'; } /* * This is actually correct since we're allowed to console warn/error by eslint, * it's just that it doesn't know which method we're calling (see above), so it warns by default */ /* eslint-disable-next-line no-console */ return (_console = console)[warningType].apply(_console, [message].concat(_toConsumableArray(literalTemplates.map(function (value) { if (_typeof(value) === 'object') { return JSON.stringify(value); } return value; })))); }; /** * A very basic polyfill method to generate randomness for use in wallet entropy. * This will fall back to nodejs's `crypto` library if the browser that's using this doesn't have the `webcrypto` API implemented yet. * * @method getRandomValues * * @param {Uint8Array} typedArray An initial unsigned 8-bit integer array to generate randomness from * * @return {Uint8Array} A new 8-bit unsigned integer array filled with random bytes */ export var getRandomValues = function getRandomValues() { var typedArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Uint8Array(10); /* * Check if `webCrypto` is available (Chrome and Firefox browsers) * * Also check if the `window` global variable is avaiable if this library * is being used in a `node` environment */ if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) { return window.crypto.getRandomValues(typedArray); } /* * Check if `webCrypto` is available (Microsoft based browsers, most likely Edge) * * Also check if the `window` global variable is avaiable if this library * is being used in a `node` environment */ if (typeof window !== 'undefined' && _typeof(window.msCrypto) === 'object' && typeof window.msCrypto.getRandomValues === 'function') { return window.msCrypto.getRandomValues(typedArray); } /* * We can't find any crypto method, we'll try to do our own. * * WARNING: This is really not all that secure as it relies on Javascripts' * internal random number generator, which isn't all that good. */ warning(messages.getRandomValues.noCryptoLib); return typedArray.map(function () { return Math.floor(Math.random() * 255); }); }; /** * Check if an expression is true and, if not, either throw an error or just log a message. * * Just as the `warning()` util above it uses two levels: `high` and `low`. If the set level is high (default), * it will throw an error, else it will just use the `warning()` method (set to `low`) to log the message * as an warning. * * @method assertTruth * * @param {boolean} expression The logic expression to assert * @param {string | Array<string>} message The message to display in case of an error * @param {string} level The log level: high (error) or low (warning) * * The above parameters are sent in as props of an object. * * @return {boolean} true if the expression is valid, false otherwise (and depending on the level, throw an error * or just log the warning) */ export var assertTruth = function assertTruth() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, expression = _ref.expression, message = _ref.message, _ref$level = _ref.level, level = _ref$level === void 0 ? 'high' : _ref$level; if (expression) { return true; } if (level === 'high') { throw new Error(Array.isArray(message) ? message.join(' ') : message); } if (Array.isArray(message)) { warning.apply(void 0, _toConsumableArray(message)); } else { warning(message); } return false; }; /** * Wrapper for the `bn.js` constructor to use as an utility for big numbers * * Make sure to inform the users that this is the preffered way of interacting with * big numbers inside this library, as even if the underlying Big Number library will change, * this API will (mostly) stay the same. * * See: BigInt * https://developers.google.com/web/updates/2018/05/bigint * * @TODO Add internal version of methods * Eg: `ifromWei()` and `itoWei`. See BN's docs about prefixes and postfixes * * @method bigNumber * * @param {number | string | BN} value the value to convert to a big number * * @return {BN} The new bignumber instance */ export var bigNumber = function bigNumber(value) { var GETTERS = DESCRIPTORS.GETTERS; var oneWei = new BN(WEI_MINIFICATION.toString()); var oneGwei = new BN(GWEI_MINIFICATION.toString()); var ExtendedBN = /*#__PURE__*/ function (_BN) { _inherits(ExtendedBN, _BN); function ExtendedBN() { var _getPrototypeOf2; var _this; _classCallCheck(this, ExtendedBN); for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(ExtendedBN)).call.apply(_getPrototypeOf2, [this].concat(args))); var ExtendedBNPrototype = Object.getPrototypeOf(_assertThisInitialized(_assertThisInitialized(_this))); Object.defineProperties(ExtendedBNPrototype, { /* * Convert the number to WEI (multiply by 1 to the power of 18) */ toWei: Object.assign({}, { value: function value() { return _this.imul(oneWei); } }, GETTERS), /* * Convert the number to WEI (divide by 1 to the power of 18) */ fromWei: Object.assign({}, { value: function value() { return _this.div(oneWei); } }, GETTERS), /* * Convert the number to GWEI (multiply by 1 to the power of 9) */ toGwei: Object.assign({}, { value: function value() { return _this.imul(oneGwei); } }, GETTERS), /* * Convert the number to GWEI (divide by 1 to the power of 9) */ fromGwei: Object.assign({}, { value: function value() { return _this.div(oneGwei); } }, GETTERS) }); return _this; } return ExtendedBN; }(BN); return new ExtendedBN(value); }; /** * Convert an object to a key (value) concatenated string. * This is useful to list values inside of error messages, where you can only pass in a string and * not the whole object. * * @method objectToErrorString * * @param {Object} object The object to convert * * @return {string} The string containing the object's key (value) pairs */ export var objectToErrorString = function objectToErrorString() { var object = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return Object.keys(object).reduce(function (allArgs, key) { return "".concat(allArgs).concat(key, " (").concat(String(JSON.stringify(object[key])), "), "); }, '').replace(/"/g, '').trim(); }; /** * Validate an (array) sequence of validation assertions (objects that are to be * directly passed into `assertTruth`) * * This is to reduce code duplication and boilerplate. * * @TODO Validate the validator * So we can have redundancy while being reduntant :) * * @method validatorGenerator * * @param {Array} validationSequenceArray An array containing objects which are in the same format as the one expect by `assertTruth` * @param {string} genericError A generic error message to be used for the catch all error (and if some of the other messages are missing) * * @return {boolean} It only returns true if all the validation assertions pass, * otherwise an Error will be thrown and this will not finish execution. */ export var validatorGenerator = function validatorGenerator(validationSequenceArray, genericError) { var validationTests = []; validationSequenceArray.map(function (validationSequence) { return validationTests.push(assertTruth( /* * If there's no message passed in, use the generic error */ Object.assign({}, { message: genericError, level: 'high' }, validationSequence))); }); /* * This is a fail-safe in case anything spills through. * If any of the values are `false` throw a general Error */ if (!validationTests.every(function (testResult) { return testResult === true; })) { throw new Error(genericError); } /* * Everything goes well here. (But most likely this value will be ignored) */ return true; };