UNPKG

iso-filecoin

Version:

Isomorphic filecoin abstractions for RPC, signatures, address, token and wallet

290 lines (252 loc) 5.7 kB
'use strict'; var bignumber = require('../node_modules/.pnpm/bignumber.js@9.3.1/node_modules/bignumber.js/bignumber.cjs'); var rfc4648 = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/rfc4648.cjs'); var utils = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/utils.cjs'); const ATTO_DECIMALS = 18; const FEMTO_DECIMALS = 15; const PICO_DECIMALS = 12; const NANO_DECIMALS = 9; const MICRO_DECIMALS = 6; const MILLI_DECIMALS = 3; const WHOLE_DECIMALS = 0; const FEMTO_MUL = 10n ** BigInt(MILLI_DECIMALS); const PICO_MUL = 10n ** BigInt(MICRO_DECIMALS); const NANO_MUL = 10n ** BigInt(NANO_DECIMALS); const MICRO_MUL = 10n ** BigInt(PICO_DECIMALS); const MILLI_MUL = 10n ** BigInt(FEMTO_DECIMALS); const WHOLE_MUL = 10n ** BigInt(ATTO_DECIMALS); const symbol = Symbol.for('filecoin-token'); bignumber.BigNumber.config({ EXPONENTIAL_AT: 1e9, DECIMAL_PLACES: 40, ALPHABET: '0123456789abcdef', ROUNDING_MODE: bignumber.BigNumber.ROUND_HALF_DOWN, }); /** * @noImplicitAny: false * @typedef {number | string | BigNumber.Instance | bigint | Token} Value */ /** * Check if object is a {@link Token} instance * * @param {any} val * @returns {val is Token} */ function isToken(val) { return Boolean(val?.[symbol]) } /** * @param {unknown} val * @returns {val is bigint} */ function isBigInt(val) { return typeof val === 'bigint' } /** * @param {Value} val */ function bn(val) { if (isBigInt(val)) { return new bignumber.BigNumber(val.toString()) } if (isToken(val)) { return val.val } return new bignumber.BigNumber(val) } /** * Class to work with different Filecoin denominations. * * @see https://docs.filecoin.io/basics/assets/the-fil-token/#denomonations */ class Token { /** @type {boolean} */ [symbol] = true /** * @param {Value} val */ constructor(val) { /** @type {BigNumber} */ this.val = bn(val); } /** * @param {Value} val */ static fromAttoFIL(val) { return new Token(val) } /** * @param {Value} val */ static fromFemtoFIL(val) { return new Token(val).mul(FEMTO_MUL) } /** * @param {Value} val */ static fromPicoFIL(val) { return new Token(val).mul(PICO_MUL) } /** * @param {Value} val */ static fromNanoFIL(val) { return new Token(val).mul(NANO_MUL) } /** * @param {Value} val */ static fromMicroFIL(val) { return new Token(val).mul(MICRO_MUL) } /** * @param {Value} val */ static fromMilliFIL(val) { return new Token(val).mul(MILLI_MUL) } /** * @param {Value} val */ static fromFIL(val) { return new Token(val).mul(WHOLE_MUL) } /** * @param {Value} val */ mul(val) { return new Token(this.val.times(bn(val))) } /** * @param {Value} val */ div(val) { return new Token(this.val.div(bn(val))) } abs() { return new Token(this.val.abs()) } /** * @param {Value} val */ add(val) { return new Token(this.val.plus(bn(val))) } /** * @param {Value} val */ sub(val) { return new Token(this.val.minus(bn(val))) } /** * Serialize the number to a string using the given base. * * @param {number | undefined} [base] */ toString(base = 10) { return this.val.toString(base) } /** * Format the number using the given options. * * @param {import('./types.js').FormatOptions} [options] * @see https://mikemcl.github.io/bignumber.js/#toFor */ toFormat(options = {}) { options = { prefix: '', decimalSeparator: '.', groupSeparator: ',', groupSize: 3, secondaryGroupSize: 0, fractionGroupSeparator: ' ', fractionGroupSize: 0, suffix: '', ...options, }; const { decimalPlaces = 18, roundingMode = bignumber.BigNumber.ROUND_HALF_DOWN, ...rest } = options; return this.val.toFormat(decimalPlaces, roundingMode, rest) } toAttoFIL() { this.toFormat({ decimalPlaces: 2, roundingMode: 1, }); return this } toFemtoFIL() { return this.div(FEMTO_MUL) } toPicoFIL() { return this.div(PICO_MUL) } toNanoFIL() { return this.div(NANO_MUL) } toMicroFIL() { return this.div(MICRO_MUL) } toMilliFIL() { return this.div(MILLI_MUL) } toFIL() { return this.div(WHOLE_MUL) } toBigInt() { return BigInt(this.val.toString()) } toBytes() { if (this.val.isZero()) { return new Uint8Array(0) } const sign = this.val.isNegative() ? '01' : '00'; return utils.concat([ rfc4648.base16.decode(sign), bigToUint8Array(BigInt(this.val.abs().toString())), ]) } } const big0 = BigInt(0); const big1 = BigInt(1); const big8 = BigInt(8); /** * * @see https://jackieli.dev/posts/bigint-to-uint8array/ * @param {bigint} big */ function bigToUint8Array(big) { // @ts-ignore if (big < big0) { const bits = (BigInt(big.toString(2).length) / big8 + big1) * big8; const prefix1 = big1 << bits; // @ts-ignore big += prefix1; } let hex = big.toString(16); if (hex.length % 2) { hex = `0${hex}`; } const len = hex.length / 2; const u8 = new Uint8Array(len); let i = 0; let j = 0; while (i < len) { u8[i] = Number.parseInt(hex.slice(j, j + 2), 16); i += 1; j += 2; } return u8 } exports.ATTO_DECIMALS = ATTO_DECIMALS; exports.FEMTO_DECIMALS = FEMTO_DECIMALS; exports.MICRO_DECIMALS = MICRO_DECIMALS; exports.MILLI_DECIMALS = MILLI_DECIMALS; exports.NANO_DECIMALS = NANO_DECIMALS; exports.PICO_DECIMALS = PICO_DECIMALS; exports.Token = Token; exports.WHOLE_DECIMALS = WHOLE_DECIMALS; exports.isToken = isToken;