Skip to main content
Bitnob doesn’t use one unit everywhere. Balances, transactions, and withdrawals use integers in the smallest unit of the currency. Trading and payouts use decimal strings in major units. Cards use micro-units. Check the units by product table before you parse or send an amount. Whatever unit the API uses, your own ledger should store integers in the smallest unit. This page explains why, and how to convert safely.

Why integers

A decimal number stored as a floating-point value is an approximation. 0.1 + 0.2 does not equal 0.3 in most languages, and the error compounds across a ledger.
An integer count of the smallest unit has no such error. 10 + 20 is exactly 30 kobo, every time, in every language. The rule is short:
Never hold a monetary amount in a float or a double. Hold an integer count of the smallest unit, and convert only for display.

Smallest units

Token decimals are a property of the token, not of the chain. USDT uses 6 decimals on both Tron and Ethereum, but many ERC-20 tokens use 18. Read the value from configuration rather than assuming it.
Keep the multiplier for each asset in one place in your code. A multiplier hard-coded at three call sites will eventually disagree with itself.

Units by product

On card webhooks, amount is the integer to store and reconcile against. display_amount is the same value as a floating-point number in dollars, there so you can show it without dividing. Never do arithmetic on display_amount.
When an endpoint takes or returns major-unit decimal strings, parse them with a decimal library straight into your smallest-unit integer. Never pass them through a float.

Convert at the edges only

Keep every internal calculation in the smallest unit. Convert to a human-readable string at the last possible moment, in the display layer.
The same rule applies on the way in. Parse a user’s input into the smallest unit immediately, then never touch the decimal form again.

Display

Two habits prevent most user confusion:
  • Label the currency every time. 500 is ambiguous. ₦500.00 and 0.005 BTC are not.
  • Round only for display, and keep the stored value exact. A balance of 99,987,600 satoshis can display as 0.9999 BTC. It must still be stored as 99,987,600.
Never round a stored amount to match what you showed. The display is a view of the value, not the value.

Exchange rates

A conversion has two parts: a rate, and the moment it applied. Record both.
1

Get a quote

Request a rate through Get exchange rate, Convert amount, or a product quote.
2

Store the rate with the transaction

Save the rate, the timestamp, and the quote identifier alongside your record.
3

Apply it in the smallest unit

Multiply integers. Convert to a decimal form only if you must.
4

Use a decimal library if you must divide

Use decimal.js or big.js on JavaScript, the decimal module on Python, or BigDecimal on Java. Never a float.
Storing the rate is what lets you answer a dispute months later. Without it, you can show what a user received but not why.

Fees

The amount a recipient gets is not the amount you sent. Network fees, chain gas, and service fees all reduce it. Decide explicitly which side pays, then make that visible:
  • Show the fee as its own line, not folded into the total.
  • Show the amount the recipient will actually receive.
  • Store both the gross amount and the fee, not just the net.
Payout quotes give you this directly. amount is what leaves your balance, settlement_amount is what arrives, and exchange_rate.effective_rate is the rate after fees. Show users the effective rate.

Multi-currency records

If you hold more than one currency, keep each balance in its own field with its own unit. Do not store a single number and a currency code that varies by row, because the smallest unit differs per currency and the arithmetic will silently mix scales. Tie the legs of a conversion together with a shared reference, so that a single transaction reads as one event rather than two unrelated rows.

Test the edges

Precision bugs hide at the extremes. Cover these cases:

Summary

  1. Check which unit each endpoint uses, then store integers in the smallest unit.
  2. Calculate in that unit.
  3. Convert and round only for display.
  4. Store the rate and the time with every conversion.
  5. Read token decimals from configuration.
  6. Test the smallest and largest amounts you support.