SSSwap

First Flight #41
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: high
Valid

Lack of Decimal Normalization in Token Pair Handling in the `liquidity_operations.rs`

Description: The AMM doesn't normalize token decimals when calculating swap amounts and liquidity provision, leading to potential imbalances with tokens of different decimal places. The AMM performs calculations directly on token amounts without normalizing for different decimal places. When tokens with different decimal places (e.g., USDC with 6 decimals and WETH with 18 decimals) are paired, this leads to imbalanced liquidity provision and incorrect swap rates. The liquidity_operations::liquidity_calculation function and swap calculations treat all token amounts as if they have the same decimal precision.

Impact: Users providing liquidity or performing swaps with token pairs that have different decimal places will experience unexpected and unfair token exchange rates. This could lead to:

  1. Imbalanced liquidity provision where one token is over-represented

  2. Incorrect pricing during swaps

  3. Potential economic loss for users

Proof of Concept: Consider a pool with USDC (6 decimals) and WETH (18 decimals):

// User provides 1 USDC and 0.001 WETH
// Raw amounts: 1_000_000 (USDC) and 1_000_000_000_000_000 (WETH)
// Without normalization, the AMM treats these as equal value
// LP token calculation: sqrt(1_000_000 * 1_000_000_000_000_000) ≈ 31,622,776,601
// This is heavily skewed toward the higher decimal token

Recommended Mitigation: Implement decimal normalization in all calculations by scaling token amounts to a common decimal base:

fn normalize_amount(amount: u64, token_decimals: u8, target_decimals: u8) -> Result<u128> {
if token_decimals <= target_decimals {
// Scale up
let scale_factor = 10u128.pow((target_decimals - token_decimals) as u32);
Ok(amount as u128 * scale_factor)
} else {
// Scale down
let scale_factor = 10u128.pow((token_decimals - target_decimals) as u32);
Ok((amount as u128).checked_div(scale_factor).ok_or(AmmError::CalculationFailure)?)
}
}
// Then use in liquidity calculation
fn liquidity_calculation(amount_token_a: u64, amount_token_b: u64,
decimals_a: u8, decimals_b: u8) -> Result<u64> {
// Normalize to 18 decimals (common standard)
let normalized_a = normalize_amount(amount_token_a, decimals_a, 18)?;
let normalized_b = normalize_amount(amount_token_b, decimals_b, 18)?;
let lp_amount_to_mint_u128 = normalized_a
.checked_mul(normalized_b)
.ok_or(AmmError::Overflow)?
.sqrt();
// Convert back to LP token decimals
let lp_amount = normalize_amount(lp_amount_to_mint_u128 as u64, 18, LP_TOKEN_DECIMALS)?;
if lp_amount == 0 {
return err!(AmmError::LpAmountCalculation);
}
Ok(lp_amount as u64)
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 13 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Protocol is incompatible with differenct decimals

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.