npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

precise-money

v0.1.15

Published

Cross-chain decimal & units primitives for crypto/fiat with BigInt.

Readme

Defy-labs/precise-money

npm version CI license

Cross‑chain decimal & units primitives built on BigInt. Safely convert between 2/6/7/9/18 decimals, apply slippage, format values without floating point, and normalize assets via a tiny registry. Thin chain adapters included.

⚠️ Disclaimer

Use at your own risk. This library is provided “AS IS”, without warranties of any kind, express or implied, including but not limited to merchantability, fitness for a particular purpose, non-infringement, or error-free operation.

  • No custody / no funds: this is a developer library. It does not custody assets, manage keys, or execute transactions on your behalf. You are fully responsible for how you integrate and use it.
  • No financial advice: nothing in this repository constitutes investment, trading, or legal advice.
  • Precision & conversions: blockchain tokens and FIAT currencies use different decimal schemes. Always validate decimals for each asset/network and test on a sandbox/testnet before mainnet usage. Mistakes in scaling/rounding can cause permanent loss of funds.
  • Security: review the code, pin dependencies, and consider external audits before production use.
  • Compliance: you are responsible for complying with all applicable laws and regulations in your jurisdiction.
  • Limitation of liability: to the maximum extent permitted by law, the authors/maintainers shall not be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including loss of funds) arising from the use of this software.

See LICENSE for the full terms.

TL;DR: this is low-level money math; double-check decimals, rounding, and chain adapters; test on testnets; you assume all risk.


Design goals: correctness first, chain‑neutral, minimal dependencies, pure integer math.


Features

  • Pure BigInt math (no floats), deterministic & auditable
  • Scale conversions between arbitrary decimals (e.g., 2 ↔ 7 ↔ 18)
  • Rounding modes: floor | ceil | round | bankers
  • Slippage helpers in basis points (BPS)
  • Price math as rational numbers {num, den} (no FP rounding)
  • Symbol‑aware helpers (optional layer) that read decimals from a registry
  • Adapters: Stellar / EVM / Solana / Cosmos (SDK‑free formatting)

Install

pnpm add precise-money
# or
npm i precise-money
import { toMinor, fromMinor } from 'precise-money';
import { evmEnsureDecimals } from 'precise-money/adapters/evm';

console.log(fromMinor(123n, 2)); // "1.23"

TypeScript: target ES2020 or newer.

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "lib": ["ES2020"],
    "skipLibCheck": true
  }
}

Quick Start

import { scaleUnits, toMinor, fromMinor, applySlippage } from '@defy-labs/precise-money'

// 2 → 7 decimals (e.g., fiat cents → on‑chain token with 7)
const cents = 12_500n;               // 125.00
const onchain = scaleUnits(cents, 2, 7); // 12_500 * 10^(5)

// safe parse/format
const minor = toMinor('1.23', 2);    // 123n
const human = fromMinor(6340065n, 7);// "0.6340065"

// slippage guard (minOut), 50 bps = 0.50%
const minOut = applySlippage(1_234_567n, 50); // 1_228_394n

All values that can move on‑chain should be BigInt. Only convert to string/number at the boundaries (UI/logs).


API Reference — Core (src/core.ts)

Pure utilities — no chain SDKs and no environment/registry access.

Types

export type Rounding = 'floor' | 'ceil' | 'round' | 'bankers'

scaleUnits(u, fromDec, toDec, opts?)

Converts an integer u between decimal precisions.

scaleUnits(12500n, 2, 7)        // → 1250000000n
scaleUnits(1250000000n, 7, 2)   // → 12500n (floor by default)
  • Parameters: u: bigint, fromDec: number, toDec: number, opts?: { mode?: Rounding }
  • Rounding: Only relevant when down‑scaling. Default: floor.

normalizeAmountInput(x)

Parses human input into { sign, int, frac } without floating point, accepting "1.234,56", "1_234.56", "-12.3", etc. Last ./, is treated as the decimal separator.

normalizeAmountInput('1.234,56') // → { sign: 1n, int: '1234', frac: '56' }

toMinor(human, decimals, opts?)

Converts a human number string/number into minor units (bigint).

toMinor('1.23', 2)         // → 123n
toMinor('0.6340065', 7)   // → 6340065n
  • Rounding: default round (half‑up behavior). Use floor/ceil/bankers if you need an explicit policy.

fromMinor(minor, decimals)

Formats minor units (bigint) into a fixed‑precision string.

fromMinor(123n, 2)        // → "1.23"
fromMinor(6340065n, 7)    // → "0.6340065"

mulDiv(a, b, c, mode?)

Computes (a * b) / c with integer rounding.

mulDiv(1000n, 3, 2, 'ceil') // → 1500n

applyBps(units, bps, mode?)

Applies a BPS factor to a bigint amount.

applyBps(1_000_000n, 50) // → 5_000n (0.50% of amount)

clampBps(bps)

Clamps BPS to [0, 10000].

clampBps(12345) // → 10000

slippageDown(amountMinor, bps)

For EXACT_IN: compute minOut by applying downward slippage.

slippageDown(1_000_000n, 50) // → 995_000n

slippageUp(amountMinor, bps)

For EXACT_OUT: compute maxIn using ceil division.

slippageUp(1_000_000n, 50) // → 1_005_000n (ceil)

applySlippage(amountOutMinor, slippageBps) / minOutForExactIn(...)

Convenience minOut for a target output.

applySlippage(1_000_000n, 50) // → 995_000n

priceRatioDecimals(quoteDecimals, priceStr)

Builds a rational price from a human string, scaled to quoteDecimals. Interprets price as QUOTE per 1 BASE.

priceRatioDecimals(2, '157.72') // → { num: 15772n, den: 100n }

convertUnitsByDecimals(amountUnits, fromDec, toDec, price, mode?)

Converts units using a price ratio and explicit decimals:

amount_from * (num/den) * 10^(toDec - fromDec)
const price = priceRatioDecimals(2, '157.72');
convertUnitsByDecimals(6_340_065n, 7, 2, price) // → fiat minor given OUT minor

divToDecimalString(numer, denom, scale?)

Integer division → decimal string with scale fractional digits (default 8).

divToDecimalString(123n, 10n, 4) // → "12.3000"

splitAmount(totalMinor, lotSizeMinor)

Splits a total amount into fixed‑size lots (last may be remainder).

splitAmount(10n, 3n) // → [3n, 3n, 3n, 1n]

Symbol‑Aware Helpers (src/symbolic.ts)

Ergonomic wrappers around core that resolve decimals from a registry (see below). Useful when you work with symbols/asset IDs instead of raw decimals.

Types & Defaults

export type RoundingModeCompat = 'trunc' | 'half_up' | 'floor' | 'ceil'
export const ASSET_DECIMALS = { USD: 2, BRL: 2, USDC: 6, BTC: 8, ETH: 18 } as const

assetKey(codeOrKey, issuer?)

Canonicalizes an asset key: "USDC" or "USDC:G...ISSUER" → uppercase composite.

getDecimals(symbolOrKey)

Looks up decimals by priority: DEC registry → environment override (e.g., STELLAR_DECIMALS_USDC) → business defaults → fallback 7.

toUnits(symbolOrKey, amount, mode?) / fromUnits(symbolOrKey, units)

Symbol‑aware variants of toMinor/fromMinor.

toUnits('USDC', '1.23')  // → 1230000n (6)
fromUnits('USDC', 1230000n) // → "1.23"

formatUnits(symbolOrKey, units, locale?, minFrac?, maxFrac?)

Locale formatting with a fallback for very large integers.

priceRatio(quoteSymbolOrKey, baseSymbolOrKey, priceStr)

Builds a price scaled to quote decimals by reading them via getDecimals.

convertUnits(amountUnits, fromSymbolOrKey, toSymbolOrKey, price, mode?)

Converts using symbol‑aware decimals.

avgFiatPricePerUnit({ filledQtyMinor, spentFiatMinor, outDecimals, fiatDecimals?, currency?, scale? })

Average FIAT per OUT unit as a string. Pass fiatDecimals explicitly or a currency code (e.g., "USD") that your app resolves via DEC.

Back‑compat alias: avgPriceBRL({ filledQtyMinor, spentCentavos, outDecimals, scale }).

Compatibility aliases

toMinorSymbol, fromMinorSymbol, clampBpsCompat, slippageDownCompat, slippageUpCompat.


Decimals Registry (src/registry.ts)

A minimal runtime registry so your app can declare/override decimals per asset.

type Chain = 'stellar' | 'evm' | 'solana' | 'cosmos'
export type AssetId = { chain: Chain; symbol: string; address?: string; issuer?: string; chainId?: number }

DEC.set('USD', 2)
DEC.set('USDC', 6)
DEC.setById({ chain: 'stellar', symbol: 'USDC', issuer: 'GABC...' }, 7)

Chain Adapters (src/adapters/*)

SDK‑free formatters that simply call toMinor/fromMinor with known decimals.

  • Stellar: stellarToMinor(human, decimals=7), stellarFromMinor(minor, decimals=7)
  • EVM: evmToMinor(human, decimals), evmFromMinor(minor, decimals)
  • Solana: solanaToMinor(human, decimals), solanaFromMinor(minor, decimals)
  • Cosmos: cosmosToMinor(human, decimals), cosmosFromMinor(minor, decimals)

You may add richer adapters in your app (e.g., fetch decimals() from ERC‑20 or mint.decimals from Solana) and feed the values into the registry.


Recipes

EXACT_IN swap guard (symbol‑aware)

import { toUnits, applySlippage, priceRatio, convertUnits } from '@defy-labs/precise-money'

const amountIn = toUnits('USDC', '250.00') // 250 * 1e6
const px = priceRatio('USD', 'TOKEN', '1.57') // USD per TOKEN
const expectedOut = convertUnits(amountIn, 'USD', 'TOKEN', px)
const minOut = applySlippage(expectedOut, 50) // -0.5%

Chunking a large order

import { splitAmount } from '@defy-labs/precise-money'

splitAmount(10_000_000n, 3_000_000n) // → [3_000_000n, 3_000_000n, 3_000_000n, 1_000_000n]

Testing

Minimal smoke tests are included with vitest. Add round‑trip invariants and real‑flow fixtures (e.g., PSP → DEX settlement) in your app.

pnpm test

Safety Notes

  • Keep all internal amounts as BigInt.
  • Be explicit with rounding when scaling down or dividing.
  • Do not add prices — add amounts/values and derive price at the end.
  • Centralize decimals in one place (registry + overrides) per environment.

License

MIT © defy‑labs