UNPKG

@thorwallet/xchain-util

Version:
563 lines (554 loc) 20.2 kB
import BigNumber from 'bignumber.js'; /** * Helper to delay anything within an `async` function * * @param ms delay in milliseconds * * @example * * ``` * const anyAsyncFunc = async () => { * // do something * console.log('before delay') * // wait for 200ms * await delay(200) * // and do other things * console.log('after delay') * } * ``` */ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * Shortcut to create a BigNumber * * @param {string | number | BigNumber.Instance} value * @returns {BigNumber} The BigNumber interface from the given value. */ const bn = (value) => new BigNumber(value); /** * Helper to check whether a BigNumber is valid or not * * @param {BigNumber} value * @returns {boolean} `true` or `false`. * */ const isValidBN = (value) => !value.isNaN(); /** * Helper to create a big number from string or number * If it fails to create a big number, a big number with value 0 will be returned instead * * @param {string|number|undefined} value * @returns {BigNumber} The BigNumber interface from the given value. If invalid one is provided, will return `0`. * */ const bnOrZero = (value) => { const b = value ? bn(value) : bn(0); return isValidBN(b) ? b : bn(0); }; /** * Helper to validate a possible BigNumber * If the given valie is invalid or undefined, 0 is returned as a BigNumber * * @param {BigNumber|undefined} value * @returns {boolean} `true` or `false`. */ const validBNOrZero = (value) => (value && isValidBN(value) ? value : bn(0)); /** * Format a BaseNumber to a string depending on given decimal places * * @param {BigNumber} value * @param {number} decimal The decimal place. (optional) * @returns {string} The formatted string from the given BigNumber and decimal place. * */ const formatBN = (value, decimal = 2) => value.toFormat(decimal); /** * The enumuration for symbol position. * `before` or `after` */ var SymbolPosition; (function (SymbolPosition) { SymbolPosition["BEFORE"] = "before"; SymbolPosition["AFTER"] = "after"; })(SymbolPosition || (SymbolPosition = {})); /** * Formats a big number value by prefixing it with `$` * * @param {BigNumber} n * @param {number} decimalPlaces The decimal place. (optional) * @param {string} symbol The currency symbol. (optional) * @param {position} position The symbol position. (optional) * @returns {string} The formatted string from the given BigNumber, decimal places, symbol and position. */ const formatBNCurrency = (n, decimalPlaces = 2, symbol = '$', position = SymbolPosition.BEFORE) => { const value = formatBN(n, decimalPlaces); if (position === SymbolPosition.BEFORE) { return `${symbol}${value}`; } return `${value}${symbol}`; }; /** * Helper to get a fixed `BigNumber` * Returns zero `BigNumber` if `value` is invalid * * @param {number|string|BigNumber|undefined} value * @param {number} decimalPlaces The decimal place. (optional) * @returns {BigNumber} The BigNumber interface from the given value and decimal. * */ const fixedBN = (value, decimalPlaces = 2) => { const n = bn(value || 0); const fixedBN = isValidBN(n) ? n.toFixed(decimalPlaces) : bn(0).toFixed(decimalPlaces); return bn(fixedBN); }; /** * Removes leading / trailing zeros from a string of numbers * (1) Regex to remove trailing zeros https://stackoverflow.com/a/53397618/2032698 * (2) Regex to remove leading zeros https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch06s06.html * * @param {string} value * @returns {string} The result after removing trailing zeros. */ const trimZeros = (value) => value // (1) remove trailing zeros .replace(/(\.[0-9]*[1-9])0+$|\.0*$/, '$1') // (2) remove leading zeros .replace(/\b0*([1-9][0-9]*|0)\b/, '$1'); /** * Binance Chain */ const BNBChain = 'BNB'; /** * Bitcoin Chain */ const BTCChain = 'BTC'; /** * Ethereum Chain */ const ETHChain = 'ETH'; /** * Thorchain */ const THORChain = 'THOR'; /** * Cosmos Chain */ const CosmosChain = 'GAIA'; /** * Polkadot Chain */ const PolkadotChain = 'POLKA'; /** * Bitcoin Cash */ const BCHChain = 'BCH'; /** * Litecoin Chain */ const LTCChain = 'LTC'; /** * All possible chains XChainJS currently supports * */ const chains = [BNBChain, BTCChain, ETHChain, THORChain, CosmosChain, PolkadotChain, BCHChain, LTCChain]; /** * Type guard to check whether string is based on type `Chain` * * @param {string} c The chain string. * @returns {boolean} `true` or `false` */ const isChain = (c) => chains.includes(c); /** * Convert chain to string. * * @param {Chain} chainId. * @returns {string} The string based on the given chain type. */ const chainToString = (chainId) => { switch (chainId) { case 'THOR': return 'Thorchain'; case 'BTC': return 'Bitcoin'; case 'BCH': return 'Bitcoin Cash'; case 'LTC': return 'Litecoin'; case 'ETH': return 'Ethereum'; case 'BNB': return 'Binance Chain'; case 'GAIA': return 'Cosmos'; case 'POLKA': return 'Polkadot'; default: return 'unknown chain'; } }; var Denomination; (function (Denomination) { /** * values for asset amounts in base units (no decimal) */ Denomination["BASE"] = "BASE"; /** * values of asset amounts (w/ decimal) */ Denomination["ASSET"] = "ASSET"; })(Denomination || (Denomination = {})); /** * Guard whichs checks whether value is a BigNumber.Value or not * * @param {unknown} v * @returns {boolean} `true` or `false`. * */ const isBigNumberValue = (v) => typeof v === 'string' || typeof v === 'number' || v instanceof BigNumber; /** * Default number of asset decimals * For history reason and by starting the project on Binance chain assets, it's 8 decimal. * * For example: * ``` * RUNE has a maximum of 8 digits of decimal * 0.00000001 RUNE == 1 ð (tor) * ``` * */ const ASSET_DECIMAL = 8; /** * Factory to create values of assets (e.g. RUNE) * * @param {string|number|BigNumber|undefined} value - The asset amount, If the value is undefined, AssetAmount with value `0` will be returned. * @param {number} decimal The decimal places. (optional) * @returns {AssetAmount} The asset amount from the given value and decimal. * **/ const assetAmount = (value, decimal = ASSET_DECIMAL) => { const amount = fixedBN(value, decimal); return { type: Denomination.ASSET, amount: () => amount, plus: (v, d = decimal) => assetAmount(amount.plus(isBigNumberValue(v) ? v : v.amount()), d), minus: (v, d = decimal) => assetAmount(amount.minus(isBigNumberValue(v) ? v : v.amount()), d), times: (v, d = decimal) => assetAmount(amount.times(isBigNumberValue(v) ? v : v.amount()), d), div: (v, d = decimal) => assetAmount(amount.div(isBigNumberValue(v) ? v : v.amount()), d), lt: (v) => amount.lt(isBigNumberValue(v) ? v : v.amount()), lte: (v) => amount.lte(isBigNumberValue(v) ? v : v.amount()), gt: (v) => amount.gt(isBigNumberValue(v) ? v : v.amount()), gte: (v) => amount.gte(isBigNumberValue(v) ? v : v.amount()), eq: (v) => amount.eq(isBigNumberValue(v) ? v : v.amount()), decimal, }; }; /** * Factory to create base amounts (e.g. tor) * * @param {string|number|BigNumber|undefined} value - The base amount, If the value is undefined, BaseAmount with value `0` will be returned. * @param {number} decimal The decimal places of its associated AssetAmount. (optional) * @returns {BaseAmount} The base amount from the given value and decimal. **/ const baseAmount = (value, decimal = ASSET_DECIMAL) => { const amount = fixedBN(value, 0); return { type: Denomination.BASE, amount: () => amount, plus: (v, d = decimal) => baseAmount(amount.plus(isBigNumberValue(v) ? v : v.amount()), d), minus: (v, d = decimal) => baseAmount(amount.minus(isBigNumberValue(v) ? v : v.amount()), d), times: (v, d = decimal) => baseAmount(amount.times(isBigNumberValue(v) ? v : v.amount()), d), div: (v, d = decimal) => baseAmount(amount.div(isBigNumberValue(v) ? v : v.amount()).decimalPlaces(0, BigNumber.ROUND_DOWN), d), lt: (v) => amount.lt(isBigNumberValue(v) ? v : v.amount()), lte: (v) => amount.lte(isBigNumberValue(v) ? v : v.amount()), gt: (v) => amount.gt(isBigNumberValue(v) ? v : v.amount()), gte: (v) => amount.gte(isBigNumberValue(v) ? v : v.amount()), eq: (v) => amount.eq(isBigNumberValue(v) ? v : v.amount()), decimal, }; }; /** * Helper to convert values for a asset from base values (e.g. RUNE from tor) * * @param {BaseAmount} base * @returns {AssetAmount} The asset amount from the given base amount. * */ const baseToAsset = (base) => { const decimal = base.decimal; const value = base .amount() .div(Math.pow(10, decimal)) .decimalPlaces(decimal); return assetAmount(value, decimal); }; /** * Helper to convert asset to base values (e.g. tor -> RUNE) * * @param {AssetAmount} asset * @returns {BaseAmount} The base amount from the given AssetAmount. * */ const assetToBase = (asset) => { const value = asset .amount() .multipliedBy(Math.pow(10, asset.decimal)) .integerValue(); return baseAmount(value, asset.decimal); }; /** * Guard to check whether value is an amount of asset or not * * @param {BaseAmount|AssetAmount} v * @returns {boolean} `true` or `false`. * */ const isAssetAmount = (v) => v.type === Denomination.ASSET; /** * Guard to check whether value is an amount of a base value or not * * @param {BaseAmount|AssetAmount} v * @returns {boolean} `true` or `false`. * */ const isBaseAmount = (v) => v.type === Denomination.BASE; /** * Formats an `AssetAmount` into `string` based on decimal places * * If `decimal` is not set, `amount.decimal` is used * Note: `trimZeros` wins over `decimal` * * @param {Params} param The asset amount format options. * @returns {string} The formatted asset amount string from the given options. */ const formatAssetAmount = ({ amount, decimal, trimZeros: trimZeros$1 = false, }) => { // strict check for `undefined` value as negate of 0 will return true and passed decimal value will be ignored const formatted = formatBN(amount.amount(), decimal === undefined ? amount.decimal : decimal); // Note: `trimZeros` wins over `decimal` return trimZeros$1 ? trimZeros(formatted) : formatted; }; /** * Formats a `BaseAmount` value into a `string` * * @param {BaseAmount} amount * @returns {string} The formatted base amount string from the given base amount. */ const formatBaseAmount = (amount) => formatBN(amount.amount(), 0); /** * Base "chain" asset of Binance chain. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetBNB = { chain: BNBChain, symbol: 'BNB', ticker: 'BNB' }; /** * Base "chain" asset on bitcoin main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetBTC = { chain: BTCChain, symbol: 'BTC', ticker: 'BTC' }; /** * Base "chain" asset on bitcoin cash main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetBCH = { chain: BCHChain, symbol: 'BCH', ticker: 'BCH' }; /** * Base "chain" asset on litecoin main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetLTC = { chain: LTCChain, symbol: 'LTC', ticker: 'LTC' }; /** * Base "chain" asset on ethereum main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetETH = { chain: ETHChain, symbol: 'ETH', ticker: 'ETH' }; const RUNE_TICKER = 'RUNE'; /** * Base "chain" asset for RUNE-67C on Binance test net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetRune67C = { chain: BNBChain, symbol: 'RUNE-67C', ticker: RUNE_TICKER }; /** * Base "chain" asset for RUNE-B1A on Binance main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetRuneB1A = { chain: BNBChain, symbol: 'RUNE-B1A', ticker: RUNE_TICKER }; /** * Base "chain" asset on thorchain main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetRuneNative = { chain: THORChain, symbol: RUNE_TICKER, ticker: RUNE_TICKER }; /** * Base "chain" asset for RUNE on ethereum main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetRuneERC20 = { chain: ETHChain, symbol: `${RUNE_TICKER}-0x3155ba85d5f96b2d030a4966af206230e46849cb`, ticker: RUNE_TICKER, }; /** * Base "chain" asset for RUNE on ethereum main net. * * Based on definition in Thorchain `common` * @see https://gitlab.com/thorchain/thornode/-/blob/master/common/asset.go#L12-24 */ const AssetRuneERC20Testnet = { chain: ETHChain, symbol: `${RUNE_TICKER}-0xd601c6A3a36721320573885A8d8420746dA3d7A0`, ticker: RUNE_TICKER, }; /** * Helper to check whether asset is valid * * @param {Asset} asset * @returns {boolean} `true` or `false` */ const isValidAsset = (asset) => !!asset.chain && !!asset.ticker && !!asset.symbol; /** * Creates an `Asset` by a given string * * This helper function expects a string with following naming convention: * `AAA.BBB-CCC` * where * chain: `AAA` * ticker (optional): `BBB` * symbol: `BBB-CCC` or `CCC` (if no ticker available) * * @see https://docs.thorchain.org/developers/transaction-memos#asset-notation * * If the naming convention fails, it returns null * * @param {string} s The given string. * @returns {Asset|null} The asset from the given string. */ const assetFromString = (s) => { var _a; const data = s.split('.'); if (data.length <= 1 || ((_a = data[1]) === null || _a === void 0 ? void 0 : _a.length) < 1) { return null; } const chain = data[0]; // filter out not supported string of chains if (!chain || !isChain(chain)) return null; const symbol = data[1]; const ticker = symbol.split('-')[0]; return { chain, symbol, ticker }; }; /** * Returns an `Asset` as a string using following naming convention: * * `AAA.BBB-CCC` * where * chain: `AAA` * ticker (optional): `BBB` * symbol: `BBB-CCC` or `CCC` (if no ticker available) * * @see https://docs.thorchain.org/developers/transaction-memos#asset-notation * * @param {Asset} asset The given asset. * @returns {string} The string from the given asset. */ const assetToString = ({ chain, symbol }) => `${chain}.${symbol .split('-') .map((x, i) => { if (i === 0) { return x; } return x.toLowerCase(); }) .join('-')}`; /** * Currency symbols currently supported */ var AssetCurrencySymbol; (function (AssetCurrencySymbol) { AssetCurrencySymbol["RUNE"] = "\u16B1"; AssetCurrencySymbol["BTC"] = "\u20BF"; AssetCurrencySymbol["SATOSHI"] = "\u26A1"; AssetCurrencySymbol["ETH"] = "\u039E"; AssetCurrencySymbol["USD"] = "$"; })(AssetCurrencySymbol || (AssetCurrencySymbol = {})); /** * Returns currency symbols by given `Asset` * * @param {Asset} asset The given asset. * @returns {string} The currency symbol from the given asset. */ const currencySymbolByAsset = ({ ticker }) => { switch (true) { case ticker === RUNE_TICKER: return AssetCurrencySymbol.RUNE; case ticker === AssetBTC.ticker: return AssetCurrencySymbol.BTC; case ticker === AssetETH.ticker: return AssetCurrencySymbol.ETH; case ticker.includes('USD'): return AssetCurrencySymbol.USD; default: return ticker; } }; /** * Formats an asset amount using its currency symbol * * If `decimal` is not set, `amount.decimal` is used * If `asset` is not set, `$` will be used as currency symbol by default * `trimZeros` is `false` by default * Note: `trimZeros` wins over `decimal` * * @param {Params} params The asset amount currency format options. * @return {string} The formatted asset amount string using its currency format. */ const formatAssetAmountCurrency = ({ amount, asset, decimal, trimZeros: shouldTrimZeros = false, }) => { var _a; const amountFormatted = formatAssetAmount({ amount, // strict check for `undefined` value as negate of 0 will return true and passed decimal value will be ignored decimal: decimal === undefined ? amount.decimal : decimal, trimZeros: shouldTrimZeros, }); const ticker = (_a = asset === null || asset === void 0 ? void 0 : asset.ticker) !== null && _a !== void 0 ? _a : ''; if (ticker) { // RUNE let regex = new RegExp(`${AssetRune67C.ticker}|${AssetRuneB1A.ticker}|${AssetRuneNative.ticker}`, 'i'); if (ticker.match(regex)) return `${AssetCurrencySymbol.RUNE} ${amountFormatted}`; // BTC regex = new RegExp(AssetBTC.ticker, 'i'); if (ticker.match(new RegExp(AssetBTC.ticker, 'i'))) { const base = assetToBase(amount); // format all < ₿ 0.01 in statoshi if (base.amount().isLessThanOrEqualTo('1000000')) { return `${AssetCurrencySymbol.SATOSHI} ${formatBaseAmount(base)}`; } return `${AssetCurrencySymbol.BTC} ${amountFormatted}`; } // ETH regex = new RegExp(AssetETH.ticker, 'i'); if (ticker.match(regex)) return `${AssetCurrencySymbol.ETH} ${amountFormatted}`; // USD regex = new RegExp('USD', 'i'); if (ticker.match('USD')) return `${AssetCurrencySymbol.USD} ${amountFormatted}`; return `${amountFormatted} ${ticker}`; } return `$ ${amountFormatted}`; }; /** * Formats a `BaseAmount` into a string of an `AssetAmount` * * If `decimal` is not set, `amount.decimal` is used * Note: `trimZeros` wins over `decimal` * * @param {Params} params The base amount currency format options. * @return {string} The formatted base amount string using its currency format. */ const formatBaseAsAssetAmount = ({ amount, decimal, trimZeros = false, }) => formatAssetAmount({ amount: baseToAsset(amount), decimal, trimZeros }); export { AssetBCH, AssetBNB, AssetBTC, AssetCurrencySymbol, AssetETH, AssetLTC, AssetRune67C, AssetRuneB1A, AssetRuneERC20, AssetRuneERC20Testnet, AssetRuneNative, BCHChain, BNBChain, BTCChain, CosmosChain, Denomination, ETHChain, LTCChain, PolkadotChain, RUNE_TICKER, THORChain, assetAmount, assetFromString, assetToBase, assetToString, baseAmount, baseToAsset, bn, bnOrZero, chainToString, chains, currencySymbolByAsset, delay, fixedBN, formatAssetAmount, formatAssetAmountCurrency, formatBN, formatBNCurrency, formatBaseAmount, formatBaseAsAssetAmount, isAssetAmount, isBaseAmount, isBigNumberValue, isChain, isValidAsset, isValidBN, trimZeros, validBNOrZero }; //# sourceMappingURL=index.esm.js.map