Skip to content

How to verify token decimals

Verify an ERC-20 token's decimals from the correct contract and block, then reconcile raw balances, transfers, allowances, and bridge conversions without floating-point errors.

Updated

For educational purposes only; not investment or security advice. A decimals value does not prove a token’s identity, value, backing, or safety.

Direct answer

For an ERC-20 token, decimals() is optional metadata that tells interfaces how to display integer token units. If it returns d, the conventional display amount is raw / 10^d. It does not change contract arithmetic and does not authenticate the token. Verify the chain, exact contract address, code or proxy implementation, and block before trusting the result.

Read decimals() directly through an independent RPC at a specified block, ABI-decode the return as uint8, and compare it with the issuer’s official contract registry and a reputable explorer. Then test the scale against raw balanceOf, transfer, allowance, receipt, and event values. Do not silently assume 18 when the call is absent, reverts, returns malformed data, or conflicts with other evidence.

How it works

ERC-20 stores and transfers unsigned integer amounts. A display layer inserts the decimal point; the contract still receives integers. Convert user input with decimal-string or arbitrary-precision integer arithmetic, never binary floating point. A quantity is representable only when multiplication by 10^d produces an integer.

Because decimals() is optional, a compliant token may omit it. A custom or upgradeable implementation may also return an unexpected value or change behavior after an upgrade. For an ERC-1967 proxy, inspect the proxy address, implementation or beacon, admin, and upgrade events; query token state at the proxy address and pin all comparisons to the same block.

Allowances and ERC-2612 permit values are raw integers too. A correctly displayed transfer does not prove that an approval, router minimum, bridge amount, or accounting database used the same scale. Source and destination bridge contracts can use different decimals, so compare human value and raw units on each side according to the bridge’s documented conversion and rounding rules.

Use this workflow:

  1. Pin the chain ID or domain, token contract, block number, RPC endpoint, and observation time.
  2. Confirm the address through the issuer’s official registry; treat name, symbol, icon, and search results as non-authoritative.
  3. Check deployed code and whether the address is a proxy; record the implementation or beacon, admin, and recent upgrades.
  4. Call decimals(), ABI-decode uint8, and record success, revert, empty, or malformed output rather than substituting a default.
  5. Read raw balanceOf, totalSupply, allowance, transaction calldata, receipt, and events at compatible blocks; format them with the observed scale.
  6. Recalculate transfer, approval, quote, and bridge amounts with integer arithmetic, including fees, rounding, dust, rebases, or transfer taxes.
  7. Simulate and send a small transaction, then reconcile raw pre- and post-balances; stop if any interface, RPC, event, or balance disagrees.

Examples

  • One raw value, two scales. With raw = 123456789, d = 6 displays 123.456789; with d = 18 it displays 0.000000000123456789. The difference is a factor of 10^12.
  • Representability matters. With d = 6, 1.25 tokens becomes 1250000 raw units. An input of 0.0000001 token is smaller than one raw unit and must be rejected or rounded under an explicit rule.
  • Approval mismatch. An allowance of 100 tokens at d = 6 is 100000000. Encoding it with d = 18 produces 100000000000000000000, an allowance 10^12 times larger than intended.
  • Bridge rescaling. If a documented 1:1 route converts a source token with d = 6 to a destination representation with d = 18, source raw 2500000 represents 2.5 tokens and destination raw 2500000000000000000 represents 2.5. Fees, caps, dust, and the actual received balance still require verification.

Risks

  • The correct address is queried on the wrong chain.
  • A copied name, symbol, or icon hides a different contract.
  • A proxy implementation or beacon changes after an earlier check.
  • An RPC or explorer serves stale, unfinalized, or inconsistent state.
  • Missing or malformed metadata is silently replaced with 18.
  • Binary floating-point conversion rounds a large or precise amount.
  • A wallet formats a transfer correctly but misformats an allowance or permit.
  • A database mixes raw units with human-readable amounts.
  • A bridge assumes equal decimals or rounds dust without disclosure.
  • Fee-on-transfer, rebase, mint, burn, pause, or freeze behavior breaks simple reconciliation.
  • Transaction calldata, events, receipts, and actual balance deltas disagree.
  • A decimals check is mistaken for proof of issuer, reserves, liquidity, or safety.

Common misconceptions

  • All ERC-20 tokens have 18 decimals. The metadata is optional, and implementations can return another value.
  • Decimals are bits of precision used by the EVM. They are a decimal display convention; token arithmetic remains integer arithmetic.
  • The explorer’s formatted balance is independent confirmation. It may rely on the same metadata call and can share the same error.
  • The same symbol and decimals identify the same asset. Identity also requires the correct chain and exact contract, plus issuer evidence.
  • A successful small transfer validates every integration. Approvals, routers, bridges, exchanges, and accounting systems may scale amounts separately.

Sources

Navigation

Search the wiki...