Token Decimal Mismatch Remediation
How to fix token decimal mismatch vulnerabilities in cross-token operations.
Token Decimal Mismatch Remediation
Overview
Related Detector: Token Decimal Mismatch
Token decimal mismatch occurs when operations involve tokens with different decimal precisions without proper adjustment, causing up to 1000x value errors. The fix requires reading decimal values from both mints and applying conversion before any cross-token arithmetic.
Recommended Fix
Before (Vulnerable)
// No decimal check -- 6-decimal and 9-decimal tokens treated identically
let amount_out = calculate_price(amount_in);
After (Fixed)
let decimals_in = Mint::unpack(&mint_in.data.borrow())?.decimals;
let decimals_out = Mint::unpack(&mint_out.data.borrow())?.decimals;
// Normalize amounts to common precision
let amount_out = if decimals_in == decimals_out {
calculate_price(amount_in)
} else if decimals_out > decimals_in {
let factor = 10u64.pow((decimals_out - decimals_in) as u32);
calculate_price(amount_in).checked_mul(factor)?
} else {
let factor = 10u64.pow((decimals_in - decimals_out) as u32);
calculate_price(amount_in).checked_div(factor)?
};
Alternative Mitigations
1. Require same decimals
For simpler protocols, reject operations between tokens with different decimals:
require!(mint_in_decimals == mint_out_decimals, ErrorCode::DecimalMismatch);
2. Use TransferChecked
TransferChecked validates expected decimals, catching mismatches at the SPL Token program level:
spl_token::instruction::transfer_checked(
&spl_token::id(), source, mint, dest, authority, &[], amount, expected_decimals,
)?;
3. Fixed-point arithmetic library
Use a fixed-point math library that handles decimal normalization internally.
Common Mistakes
Mistake 1: Hardcoding decimal values
// WRONG: assumes all tokens have 6 decimals
let ui_amount = raw_amount / 1_000_000;
// CORRECT: read decimals from mint
let ui_amount = raw_amount / 10u64.pow(mint.decimals as u32);
Mistake 2: Converting in the wrong direction
// WRONG: multiplying when should divide (or vice versa)
// 6-decimal to 9-decimal: MULTIPLY by 10^3
// 9-decimal to 6-decimal: DIVIDE by 10^3
Mistake 3: Ignoring precision loss in division
// RISKY: division truncates, causing rounding errors
let adjusted = amount / factor;
// Consider: rounding up for safety in certain financial contexts