@colony/purser-core
Version:
A collection of helpers, utils, validators and normalizers to assist the individual purser modules
378 lines (310 loc) • 12.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.validatorGenerator = exports.objectToErrorString = exports.bigNumber = exports.assertTruth = exports.getRandomValues = exports.warning = exports.verbose = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _bn = _interopRequireDefault(require("bn.js"));
var _defaults = require("./defaults");
var _messages = require("./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?
*/
var verbose = function verbose() {
if (typeof _defaults.ENV === 'undefined') {
return true;
}
if (_defaults.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)
*/
exports.verbose = verbose;
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 ((0, _typeof2.default)(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((0, _toConsumableArray2.default)(literalTemplates.map(function (value) {
if ((0, _typeof2.default)(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
*/
exports.warning = warning;
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' && (0, _typeof2.default)(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.utils.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)
*/
exports.getRandomValues = getRandomValues;
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, (0, _toConsumableArray2.default)(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
*/
exports.assertTruth = assertTruth;
var bigNumber = function bigNumber(value) {
var GETTERS = _defaults.DESCRIPTORS.GETTERS;
var oneWei = new _bn.default(_defaults.WEI_MINIFICATION.toString());
var oneGwei = new _bn.default(_defaults.GWEI_MINIFICATION.toString());
var ExtendedBN =
/*#__PURE__*/
function (_BN) {
(0, _inherits2.default)(ExtendedBN, _BN);
function ExtendedBN() {
var _getPrototypeOf2;
var _this;
(0, _classCallCheck2.default)(this, ExtendedBN);
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
_this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(ExtendedBN)).call.apply(_getPrototypeOf2, [this].concat(args)));
var ExtendedBNPrototype = Object.getPrototypeOf((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_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.default);
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
*/
exports.bigNumber = bigNumber;
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.
*/
exports.objectToErrorString = objectToErrorString;
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;
};
exports.validatorGenerator = validatorGenerator;