/**
* Converts a decimal amount of nano from one unit divider to another.
*
-* @param {bigint|string} amount - Decimal amount to convert
+* @param {(bigint|string)} amount - Decimal amount to convert
* @param {string} inputUnit - Current denomination
* @param {string} outputUnit - Desired denomination
*/
-export async function convert (amount: bigint | string, inputUnit: string, outputUnit: string): Promise<string> {
- if (typeof amount === 'bigint') {
- amount = amount.toString()
+export async function convert (amount: bigint | string, inputUnit: string, outputUnit: string): Promise<string>
+export async function convert (amount: unknown, inputUnit: unknown, outputUnit: unknown): Promise<string> {
+ if (typeof amount !== 'bigint' && typeof amount !== 'string') {
+ throw new Error('Invalid amount', { cause: typeof amount })
}
- if (typeof amount !== 'string' || amount === '' || !/^[0-9]*\.?[0-9]*$/.test(amount)) {
- throw new Error('Invalid amount')
+ if (typeof amount === 'string' && !/^[0-9]+\.?[0-9]*$/.test(amount)) {
+ throw new Error('Invalid amount', { cause: amount })
}
- let [i = '', f = ''] = amount.toString().split('.')
- i = i.replace(/^0*/g, '')
- f = f.replace(/0*$/g, '')
-
- inputUnit = inputUnit.toUpperCase()
- outputUnit = outputUnit.toUpperCase()
- if (!(inputUnit in UNITS)) {
+ if (typeof inputUnit !== 'string') {
+ throw new TypeError('Invalid input unit', { cause: typeof inputUnit })
+ }
+ if (typeof outputUnit !== 'string') {
+ throw new TypeError('Invalid output unit', { cause: typeof outputUnit })
+ }
+ if (typeof inputUnit !== 'string' || UNITS[inputUnit.toUpperCase()] == null) {
throw new Error(`Unknown denomination ${inputUnit}, expected one of the following: ${Object.keys(UNITS)}`)
}
- if (!(outputUnit in UNITS)) {
+ if (typeof outputUnit !== 'string' || UNITS[outputUnit.toUpperCase()] == null) {
throw new Error(`Unknown denomination ${outputUnit}, expected one of the following: ${Object.keys(UNITS)}`)
}
+ let i = '0', f = '0'
+ if (typeof amount === 'bigint') {
+ i = amount.toString()
+ } else {
+ [i, f] = amount.split('.')
+ }
+
// convert to raw
- i = `${i}${f.slice(0, UNITS[inputUnit])}`.padEnd(i.length + UNITS[inputUnit], '0')
- f = f.slice(UNITS[inputUnit])
- if (i.length > 39) {
- throw new Error('Amount exceeds Nano limits')
+ const inUnit = UNITS[inputUnit.toUpperCase()]
+ i = i.padEnd(i.length + inUnit, '0')
+ let int = BigInt(i)
+ if (f != null) {
+ f = f.padEnd(f.length + inUnit, '0')
+ let frac = BigInt(f)
+ const remainder = frac % (10n ** BigInt(inUnit))
+ if (remainder > 0n) {
+ throw new Error('Amount contains fractional raw', { cause: remainder })
+ }
+ frac /= 10n ** BigInt(f.length - inUnit)
+ int += frac
}
- if (i.length < 1) {
- throw new Error('Amount must be at least 1 raw')
+ if (int > MAX_SUPPLY) {
+ throw new Error('Amount exceeds available supply')
}
- if (f.length > 0) {
- throw new Error('Amount contains fractional raw')
+ if (int < 1n) {
+ throw new Error('Amount must be at least 1 raw')
}
+ i = int.toString().padStart(40, '0')
// convert to desired denomination
- if (UNITS[outputUnit] !== 0) {
- f = i.padStart(40, '0').slice(-UNITS[outputUnit]) + f
- i = i.slice(0, -UNITS[outputUnit])
- }
- i = i.replace(/^0*/g, '') ?? '0'
- f = f.replace(/0*$/g, '')
- return `${i}${f ? '.' : ''}${f}`
+ const outUnit = UNITS[outputUnit.toUpperCase()]
+ f = i.slice(40 - outUnit).replace(/0*$/g, '')
+ i = i.slice(0, 40 - outUnit).replace(/^0*/g, '')
+
+ return `${i === '' ? '0' : i}${f === '' ? '' : '.'}${f}`
}
function hash (data: string | string[], encoding?: 'hex'): Uint8Array<ArrayBuffer>