UNPKG

@coinify/currency

Version:

Helper functions for managing amounts in different currencies

191 lines (170 loc) 5.17 kB
const round = require('lodash/round'); const constants = require('./currencies.json'); /** * Returns the number of decimals after the floating point, with which * the amount should be formatted, depending on the currency. * * @param {string} currency * @returns {int} */ function getDecimalsForCurrency(currency) { if (!isValidCurrency(currency)) { throw new Error(`Invalid currency '${currency}'`); } return constants[currency].decimals; } /** * Convert an amount of money from the smallest sub-unit of the currency. * For example, for a BTC account, this function will convert * 12345678 to 0.12345678. * Likewise, for a USD account, 12345 is converted to 123.45. * This function is the inverse of {@self::toSmallestSubUnit} * * @param {int} amount * @param {string} currency * * @return number */ function fromSmallestSubunit(amount, currency) { const decimals = getDecimalsForCurrency(currency); return round(amount / Math.pow(10, decimals), decimals); } /** * Convert an amount of smallest sub-unit to the actual currency unit. * For example, for a BTC account, this function will convert * 0.12345678 to 12345678. * Likewise, for a USD account, 123.45 is converted to 12345. * This function is the inverse of {@self::fromSmallestSubUnit} * * @param {number} amount * @param {string} currency * * @return int */ function toSmallestSubunit(amount, currency) { const decimals = getDecimalsForCurrency(currency); return Math.round(amount * Math.pow(10, decimals)); } /** * Convert between sub-unit amounts of two currencies with a given rate, * correctly converting between sub-units with different decimal amounts. * E.g. to convert 1 BTC to USD at rate 250, call * convertSubunitAmount( 100000000, 250, 'BTC', 'USD' ) * * @param {int} amountSubUnit Amount to convert, denominated * in smallest sub-unit of $from_currency * @param {number} rate Rate to multiply the amount (non-sub-unit) with * in order to convert. * @param {string} fromCurrency Currency denominating input amount (amountSubUnit) * @param {string} toCurrency Currency denominating output. * * @return number */ function convertSubunitAmount(amountSubUnit, rate, fromCurrency, toCurrency) { const exponent = -getDecimalsForCurrency(fromCurrency) + getDecimalsForCurrency(toCurrency); return amountSubUnit * rate * Math.pow(10, exponent); } /** * Computes a rate between two amounts in two different currencies. * * The result is fromAmount / toAmount (in the main units of their respective currencies) * * @param {string} fromCurrency * @param {int} fromAmount Amount denominated in smallest sub-unit of fromCurrency * @param {string} toCurrency * @param {int} toAmount Amount denominated in smallest sub-unit of toCurrency */ function computeRateBetweenSubunitAmounts(fromCurrency, fromAmount, toCurrency, toAmount) { /* * If currencies are equal, rate is 1 */ if (fromCurrency === toCurrency) { return 1; } /* * If toAmount is 0, we don't want to divide by zero. * In this case, return NaN */ if (toAmount === 0) { return NaN; } /* * Convert both amounts to main units */ const fromAmountMainUnit = fromSmallestSubunit(fromAmount, fromCurrency); const toAmountMainUnit = fromSmallestSubunit(toAmount, toCurrency); /* * Compute and return rate */ return fromAmountMainUnit / toAmountMainUnit; } /** * Is the provided currency code a valid currency? (fiat or crypto) * * @param {string} code * @returns {boolean} */ function isValidCurrency(code) { return constants[code] !== undefined; } /** * Is the provided currency code a valid fiat currency? * * @param {string} code * @returns {boolean} */ function isValidFiatCurrency(code) { return isValidCurrency(code) && !constants[code].crypto; } /** * Is the provided currency code a valid crypto-currency? * * Currently, only BTC is supported * * @param {string} code * @returns {boolean} */ function isValidCryptoCurrency(code) { return isValidCurrency(code) && constants[code].crypto; } /** * Does provided currency code belong to a stablecoin? * * @param {string} code * @returns {boolean | undefined} */ function isStablecoin(code) { return isValidCurrency(code) ? constants[code].stablecoin : undefined; } function formatAmount(amount, currency) { const decimals = getDecimalsForCurrency(currency); return fromSmallestSubunit(amount, currency).toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals }); } /** * Returns full name of currency by given code * * @param {string} code * @returns {string} name of currency */ function getName(code) { if (!isValidCurrency(code)) { throw new Error(`Invalid currency '${code}'`); } return constants[code].name; } module.exports = { getDecimalsForCurrency, fromSmallestSubunit, toSmallestSubunit, convertSubunitAmount, computeRateBetweenSubunitAmounts, isValidCurrency, isValidFiatCurrency, isValidCryptoCurrency, isStablecoin, formatAmount, getName };