// Copyright (C) 2018 Zilliqa // // This file is part of zilliqa-js // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . /** * Adapted from https://github.com/ethjs/ethjs-unit/blob/master/src/index.js */ import BN from 'bn.js'; export enum Units { Zil = 'zil', Li = 'li', Qa = 'qa', } interface Options { pad: boolean; } const DEFAULT_OPTIONS = { pad: false, }; const unitMap = new Map([ [Units.Qa, '1'], [Units.Li, '1000000'], // 1e6 qa [Units.Zil, '1000000000000'], // 1e12 qa ]); const numToStr = (input: string | number | BN) => { if (typeof input === 'string') { if (!input.match(/^-?[0-9.]+$/)) { throw new Error( `while converting number to string, invalid number value '${input}', should be a number matching (^-?[0-9.]+).`, ); } return input; } else if (typeof input === 'number') { return String(input); } else if (BN.isBN(input)) { return input.toString(10); } throw new Error( `while converting number to string, invalid number value '${input}' type ${typeof input}.`, ); }; export const fromQa = ( qa: BN, unit: Units, options: Options = DEFAULT_OPTIONS, ): string => { if (unit === 'qa') { return qa.toString(10); } const baseStr = unitMap.get(unit); if (!baseStr) { throw new Error(`No unit of type ${unit} exists.`); } const base = new BN(baseStr, 10); const baseNumDecimals = baseStr.length - 1; let fraction = qa.abs().mod(base).toString(10); // prepend 0s to the fraction half while (fraction.length < baseNumDecimals) { fraction = `0${fraction}`; } if (!options.pad) { fraction = ( (fraction.match(/^([0-9]*[1-9]|0)(0*)/))[1] ); } const whole = qa.div(base).toString(10); return fraction === '0' ? `${whole}` : `${whole}.${fraction}`; }; export const toQa = (input: string | number | BN, unit: Units) => { let inputStr = numToStr(input); const baseStr = unitMap.get(unit); if (!baseStr) { throw new Error(`No unit of type ${unit} exists.`); } const baseNumDecimals = baseStr.length - 1; const base = new BN(baseStr, 10); // Is it negative? const isNegative = inputStr.substring(0, 1) === '-'; if (isNegative) { inputStr = inputStr.substring(1); } if (inputStr === '.') { throw new Error(`Cannot convert ${inputStr} to Qa.`); } // Split it into a whole and fractional part const comps = inputStr.split('.'); // eslint-disable-line if (comps.length > 2) { throw new Error(`Cannot convert ${inputStr} to Qa.`); } let [whole, fraction] = comps; if (!whole) { whole = '0'; } if (!fraction) { fraction = '0'; } if (fraction.length > baseNumDecimals) { throw new Error(`Cannot convert ${inputStr} to Qa.`); } while (fraction.length < baseNumDecimals) { fraction += '0'; } const wholeBN = new BN(whole); const fractionBN = new BN(fraction); let wei = wholeBN.mul(base).add(fractionBN); // eslint-disable-line if (isNegative) { wei = wei.neg(); } return new BN(wei.toString(10), 10); };