Token Balance 100x Off: How to Fix Decimals Reading Errors
A balance difference of 100 times doesn't mean your tokens are lost — it's because you viewed the balance without converting by the token's decimals. On-chain storage is always in raw integers; only when displaying do you divide by 10 ** decimals. USDC has 6 decimals, WBTC has 8, and most tokens have 18. Reading the wrong precision can cause the display to be off by a factor of 10^n.
1. First, Identify the Root Cause: Are You Using the "Raw Value" or the "Display Value"?
Token balances are stored on-chain in the smallest unit — USDC is stored in "micro-dollars" (1 USDC = 1,000,000 units), WBTC in "satoshis" (1 WBTC = 100,000,000 units). Wallets and block explorers convert them for you automatically, but if you call RPCs, write scripts, or look at contract return values without converting, you'll see a long integer that appears to have "several extra zeros."
Prerequisite: You have a transaction's on-chain data (raw balance), or you see a balance in your wallet that doesn't match the block explorer.
2. Step 1: Verify the True Balance Using a Block Explorer
Step 1: Check the token balance on the block explorer
What to do: Open the block explorer for the chain (Etherscan for Ethereum, BSCScan for BSC) and paste the wallet address to check the balance.
How to do it: MetaMask's official docs also mention this — if the wallet display is wrong, use the block explorer balance as the benchmark.
When it's done: The balance shown on the explorer matches what you expect. If it doesn't, your wallet or code is reading the precision incorrectly.
Step 2: Look at the "converted value" and the "raw value" on the explorer
What to do: On the token detail page or transaction details, find the field for Raw Value or "Value in Wei".
How to do it: Compare the human-readable value to the raw value and note the multiplier. For USDC, raw value
1000000displays as1.000000; for an 18-decimal token, raw1000000000000000000displays as1. If your code divides the raw value by10 ** 18but the actual decimals are 6, the result will be off by 10^12 — easily making it look like "one hundred times" the amount.When it's done: You have confirmed the token's decimals and whether your conversion formula matches.
3. Step 2: Fix How You Read Token Decimals
Step 3: Dynamically call the token's decimals() method instead of hardcoding 18
What to do: In your code or scripts, call the token contract's
decimals()function to get the real precision instead of assuming all tokens use 18.How to do it:
Case A (using ethers.js / Web3.js):
await tokenContract.decimals()to get the value, then useparseUnits(value, decimals)orformatUnits(balance, decimals)to convert.Case B (manual debugging): In the block explorer, go to "Contract" → "Read Contract", find the
decimalsfunction, and check the return value.
When it's done: Your code no longer uses the hardcoded number
18but dynamically fetches the real value from the chain.
Step 4: Check whether you added the correct token contract address in your wallet
What to do: If the wallet display is wrong, you might have added an incorrect token contract address (e.g., an old contract or a fake token).
How to do it:
Remove the token that is showing incorrectly from your wallet.
Copy the correct contract address from CoinMarketCap or the project's official documentation and re-add it.
When it's done: After re-adding, the wallet balance matches the block explorer.
Risk Warning: Malicious tokens can return a
decimals()value that does not match the actual transfer logic, or return different values based on the caller address. If you rely on a token'sdecimals()for amount calculations in a DeFi protocol and the token is unverified, there is a risk of manipulation. Auditing firm Zokyo emphasizes never to assume all tokens are 18 decimals — this is already a documented vulnerability type.
Common Failure Reasons
"Wallet balance doesn't match the explorer, but the decimals setting is correct"
This can be more than a precision issue. MetaMask's official documentation lists other possibilities:
RPC node latency or caching: Switch to a different RPC URL in the network settings and try again.
Tokens with built-in mechanisms: Rebase tokens (auto-adjusting supply) or "transfer-fee" tokens (charging a tax on each transaction) have dynamically changing balances — it might not be a misreading by your wallet. Check the project's whitepaper to confirm if such mechanisms exist.
After making these fixes, how to confirm it's correct?
In your own script or wallet, compare the balance for the same wallet address before and after the correction — if the new displayed number divided by the old one equals exactly 10 ** (18 - actual decimals) (for USDC, 10^12 times), it means you were previously reading the wrong decimals. Next, when doing amount calculations in a contract, always use parseUnits to convert the user's "display value" to the "on-chain raw value" before passing it as an argument, and never pass a number with decimals directly.
