exact-money
v0.1.0
Published
integer only token amounts. no floats, asset aware decimals, explicit rounding
Downloads
29
Maintainers
Readme
exact-money
integer only token amounts. no floats, asset aware decimals, explicit rounding.
npm i exact-moneywhy
0.1 + 0.2 // 0.30000000000000004
1234567.123456789012345678 // 1234567.1234567891 <- 18 decimals do not fita float cannot hold a token balance. exact-money never converts to one — amounts are bigint in the smallest unit, and there is no toNumber.
use
import { asset } from 'exact-money'
const USDT = asset('USDT', 6)
const ETH = asset('ETH', 18)
const a = USDT.parse('12.5') // 12500000n
a.add(USDT.parse('1')).format() // '13.500000'
a.format({ trim: true }) // '12.5'
a.bps(250).format({ trim: true }) // '0.3125' (2.5%)it will not let you mix assets
USDT.parse('1').add(USDC.parse('1'))
// MoneyError: cannot add USDT and USDCsame symbol on two chains is two assets:
const eth = asset('USDT', 6, 'ethereum')
const tron = asset('USDT', 6, 'tron')
eth.parse('1').add(tron.parse('1'))
// MoneyError: cannot add USDT@ethereum and USDT@tronand decimals are part of identity. this is a real bug shape — a config declaring XAUT0 with 6 decimals while the docs say 18:
asset('XAUT0', 6).equals(asset('XAUT0', 18)) // false
asset('XAUT0', 6).parse('1').raw // 1000000n
asset('XAUT0', 18).parse('1').raw // 1000000000000000000nrounding is never implicit
USDT.parse('1').div(3) // throws: result is not exact, pass a rounding mode
USDT.parse('1').div(3, 'floor') // 0.333333
USDT.parse('1').div(4) // 0.25, exact so no mode neededmodes: floor ceil trunc half-up half-even exact. negatives round the way you'd expect — floor on -3.5 is -4.
split with no dust
USDT.units(100n).split(3).map(p => p.raw) // [34n, 33n, 33n]the parts always add back up to the original. no rounding leak.
parsing refuses to lose digits
USDT.parse('1.1234567')
// MoneyError: 1.1234567 has 7 decimal places, USDT has 6truncating here is how people lose money quietly. it throws instead.
rescale across precisions
const six = asset('T', 6), eighteen = asset('T', 18)
six.parse('1.5').rescale(eighteen).raw // 1500000000000000000n
eighteen.units(1n).rescale(six) // throws, would lose precision
eighteen.units(1n).rescale(six, 'floor').raw // 0napi
| | |
|---|---|
| asset(symbol, decimals, chain?) | |
| .parse('12.5') | string only, never a number |
| .units(12500000n) | raw smallest unit |
| add sub neg abs | same asset enforced |
| mul(int) div(int, mode) | amount × amount is not money |
| bps(points, mode?) | 250 is 2.5% |
| split(n) | dust free |
| rescale(asset, mode?) | |
| cmp eq lt lte gt gte | |
| format({ trim, group }) | exact, no exponent |
| .raw | the bigint |
zero dependencies. types included.
licence
MIT
