iso-filecoin
Version:
Isomorphic filecoin abstractions for RPC, signatures, address, token and wallet
290 lines (252 loc) • 5.7 kB
JavaScript
;
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;