UNPKG

@polkadot/types-codec

Version:
231 lines (230 loc) 8.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractInt = exports.DEFAULT_UINT_BITS = void 0; const util_1 = require("@polkadot/util"); exports.DEFAULT_UINT_BITS = 64; const MAX_NUMBER_BITS = 52; const MUL_P = new util_1.BN(1_00_00); const FORMATTERS = [ ['Perquintill', util_1.BN_QUINTILL], ['Perbill', util_1.BN_BILLION], ['Permill', util_1.BN_MILLION], ['Percent', util_1.BN_HUNDRED] ]; function isToBn(value) { return (0, util_1.isFunction)(value.toBn); } function toPercentage(value, divisor) { return `${(value.mul(MUL_P).div(divisor).toNumber() / 100).toFixed(2)}%`; } /** @internal */ function decodeAbstractInt(value, isNegative) { if ((0, util_1.isNumber)(value)) { if (!Number.isInteger(value) || value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) { throw new Error('Number needs to be an integer <= Number.MAX_SAFE_INTEGER, i.e. 2 ^ 53 - 1'); } return value; } else if ((0, util_1.isString)(value)) { if ((0, util_1.isHex)(value, -1, true)) { return (0, util_1.hexToBn)(value, { isLe: false, isNegative }).toString(); } if (value.includes('.') || value.includes(',') || value.includes('e')) { throw new Error('String should not contain decimal points or scientific notation'); } return value; } else if ((0, util_1.isBn)(value) || (0, util_1.isBigInt)(value)) { return value.toString(); } else if ((0, util_1.isObject)(value)) { if (isToBn(value)) { return value.toBn().toString(); } // Allow the construction from an object with a single top-level key. This means that // single key objects can be treated equivalently to numbers, assuming they meet the // specific requirements. (This is useful in Weights 1.5 where Objects are compact) const keys = Object.keys(value); if (keys.length !== 1) { throw new Error('Unable to construct number from multi-key object'); } return decodeAbstractInt(value[keys[0]], isNegative); } else if (!value) { return 0; } throw new Error(`Unable to create BN from unknown type ${typeof value}`); } /** * @name AbstractInt * @ignore * @noInheritDoc */ class AbstractInt extends util_1.BN { registry; encodedLength; isUnsigned; createdAtHash; initialU8aLength; isStorageFallback; __internal__bitLength; constructor(registry, value = 0, bitLength = exports.DEFAULT_UINT_BITS, isSigned = false) { // Construct via a string/number, which will be passed in the BN constructor. // It would be ideal to actually return a BN, but there is an issue: // https://github.com/indutny/bn.js/issues/206 super( // shortcut isU8a as used in SCALE decoding (0, util_1.isU8a)(value) ? bitLength <= 48 ? (0, util_1.u8aToNumber)(value.subarray(0, bitLength / 8), { isNegative: isSigned }) : (0, util_1.u8aToBn)(value.subarray(0, bitLength / 8), { isLe: true, isNegative: isSigned }).toString() : decodeAbstractInt(value, isSigned)); this.registry = registry; this.__internal__bitLength = bitLength; this.encodedLength = this.__internal__bitLength / 8; this.initialU8aLength = this.__internal__bitLength / 8; this.isUnsigned = !isSigned; const isNegative = this.isNeg(); const maxBits = bitLength - (isSigned && !isNegative ? 1 : 0); if (isNegative && !isSigned) { throw new Error(`${this.toRawType()}: Negative number passed to unsigned type`); } else if (super.bitLength() > maxBits) { throw new Error(`${this.toRawType()}: Input too large. Found input with ${super.bitLength()} bits, expected ${maxBits}`); } } /** * @description returns a hash of the contents */ get hash() { return this.registry.hash(this.toU8a()); } /** * @description Checks if the value is a zero value (align elsewhere) */ get isEmpty() { return this.isZero(); } /** * @description Returns the number of bits in the value */ bitLength() { return this.__internal__bitLength; } /** * @description Compares the value of the input to see if there is a match */ // eslint-disable-next-line @typescript-eslint/no-explicit-any eq(other) { // Here we are actually overriding the built-in .eq to take care of both // number and BN inputs (no `.eqn` needed) - numbers will be converted return super.eq((0, util_1.isHex)(other) ? (0, util_1.hexToBn)(other.toString(), { isLe: false, isNegative: !this.isUnsigned }) : (0, util_1.bnToBn)(other)); } /** * @description Returns a breakdown of the hex encoding for this Codec */ inspect() { return { outer: [this.toU8a()] }; } /** * @description True if this value is the max of the type */ isMax() { const u8a = this.toU8a().filter((b) => b === 0xff); return u8a.length === (this.__internal__bitLength / 8); } /** * @description Returns a BigInt representation of the number */ toBigInt() { return BigInt(this.toString()); } /** * @description Returns the BN representation of the number. (Compatibility) */ toBn() { return this; } /** * @description Returns a hex string representation of the value */ toHex(isLe = false) { // For display/JSON, this is BE, for compare, use isLe return (0, util_1.bnToHex)(this, { bitLength: this.bitLength(), isLe, isNegative: !this.isUnsigned }); } /** * @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information */ toHuman(_isExpanded) { const rawType = this.toRawType(); if (rawType === 'Balance') { return this.isMax() ? 'everything' // FIXME In the case of multiples we need some way of detecting which instance this belongs // to. as it stands we will always format (incorrectly) against the first token defined : (0, util_1.formatBalance)(this, { decimals: this.registry.chainDecimals[0], withSi: true, withUnit: this.registry.chainTokens[0] }); } const [, divisor] = FORMATTERS.find(([type]) => type === rawType) || []; return divisor ? toPercentage(this, divisor) : (0, util_1.formatNumber)(this); } /** * @description Converts the Object to JSON, typically used for RPC transfers */ toJSON(onlyHex = false) { // FIXME this return type should by string | number, however BN returns string // Options here are // - super.bitLength() - the actual used bits, use hex when close to MAX_SAFE_INTEGER // - this.__internal__bitLength - the max used bits, use hex when larger than native Rust type return onlyHex || (this.__internal__bitLength > 128) || (super.bitLength() > MAX_NUMBER_BITS) ? this.toHex() : this.toNumber(); } /** * @description Returns the value in a primitive form, either number when <= 52 bits, or string otherwise */ toPrimitive() { return super.bitLength() > MAX_NUMBER_BITS ? this.toString() : this.toNumber(); } /** * @description Returns the base runtime type name for this instance */ toRawType() { // NOTE In the case of balances, which have a special meaning on the UI // and can be interpreted differently, return a specific value for it so // underlying it always matches (no matter which length it actually is) return this instanceof this.registry.createClassUnsafe('Balance') ? 'Balance' : `${this.isUnsigned ? 'u' : 'i'}${this.bitLength()}`; } /** * @description Returns the string representation of the value * @param base The base to use for the conversion */ toString(base) { // only included here since we do not inherit docs return super.toString(base); } /** * @description Encodes the value as a Uint8Array as per the SCALE specifications */ toU8a(_isBare) { return (0, util_1.bnToU8a)(this, { bitLength: this.bitLength(), isLe: true, isNegative: !this.isUnsigned }); } } exports.AbstractInt = AbstractInt;