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.

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.

Common bug patterns
Watch for these mistakes in reviews:
- Hardcoding 18 decimals for all tokens
- Using a floating point number for amounts sent to contracts
- Formatting amounts for display and then using the formatted string in calculations
- Comparing a human amount with an on-chain unit value
- Truncating instead of rounding, or rounding instead of truncating, without a clear rule
- 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.