Web3 Builders

ERC-20 Decimals and Units: Avoiding Costly Bugs

How ERC-20 token decimals work, why the same token can use different decimals on different chains, and the coding habits that prevent unit conversion bugs.

Ruler with tiny decimal markings measuring a coin

Token amounts on Ethereum-compatible chains are stored as whole numbers. There are no decimal points inside a smart contract. A token's decimals setting tells wallets and apps how to display those whole numbers to humans. Mixing up decimals is one of the most common and expensive bugs in crypto development, capable of turning a one dollar payment into a one trillion dollar request or a million dollar transfer into a fraction of a cent.

How decimals work

Every ERC-20 token has a decimals value, commonly 18 or 6. The contract stores balances in the smallest unit.

Token decimals Human amount On-chain units
18 1.0 1000000000000000000
6 1.0 1000000
6 25.37 25370000
18 25.37 25370000000000000000

To convert a human amount to units, multiply by 10 raised to the number of decimals. To display units, divide by the same figure.

The same token, different decimals

A dangerous assumption is that a token uses the same decimals everywhere. Some stablecoins use 6 decimals on most networks but 18 on others, depending on how they were issued or bridged. A symbol like USDC or USDT on one chain says nothing certain about decimals on another.

Always read the decimals value from the specific contract on the specific chain, or keep a verified configuration per network and token.

Floating point is not your friend

JavaScript numbers are floating point values with limited precision. Large unit values exceed the safe integer range, and decimal arithmetic can introduce rounding errors.

Avoid code like this:

const units = amount * 10 ** decimals; // loses precision

Prefer string-based parsing into BigInt:

function toUnits(amount, decimals) {
  const [whole, frac = ''] = amount.split('.');
  return BigInt(whole) * 10n ** BigInt(decimals) + BigInt((frac + '0'.repeat(decimals)).slice(0, decimals) || '0');
}

This approach keeps every digit exact and avoids rounding surprises.

Two identical coins with different numbers of dots beneath them
The same symbol can hide different decimal settings on different chains.

Common bug patterns

Watch for these mistakes in reviews:

  1. Hardcoding 18 decimals for all tokens
  2. Using a floating point number for amounts sent to contracts
  3. Formatting amounts for display and then using the formatted string in calculations
  4. Comparing a human amount with an on-chain unit value
  5. Truncating instead of rounding, or rounding instead of truncating, without a clear rule
  6. Forgetting that user input may include commas or too many decimal places

Each has caused real losses in production apps.

Validation habits

Build safeguards around amounts:

  • Reject inputs with more decimal places than the token supports
  • Show a confirmation screen with the human amount and token before sending
  • In tests, include tokens with 6 and 18 decimals
  • Compare computed units against expected values in unit tests
  • Log both human and unit values when debugging

Payment matching needs exact units

When detecting payments, compare units exactly. Converting on-chain values back to floating point before comparing can produce false mismatches. Store expected amounts as unit strings in your database and compare as BigInt.

Approvals use units too

Token approvals and allowances are also stored in units. An approval for "100 USDC" is really an approval for 100 multiplied by 10 raised to the token's decimals. If an app calculates the approval with the wrong decimals, it can request far more spending permission than intended, or too little for the transaction to succeed. Show the human amount in your interface, compute units from verified decimals, and let users see the exact approval before they sign. Reviewing approval amounts in tests with both 6 and 18 decimal tokens catches this early.

Display considerations

For display, trim trailing zeros and group digits sensibly, but keep the full precision available to users who want it. Show token symbols next to amounts and, for stablecoins, consider showing a dollar approximation.

Proud Globe stores each order amount in token units per network, including stablecoins with 18 decimals on BNB Chain and 6 decimals elsewhere. The exact units are what the detector compares when you pay for a tile.

Educational content only. Nothing here is financial, legal or tax advice. Crypto assets carry risk, so check the details for your own situation.