SSSwap

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

Lack of Token Decimal Normalization

Root + Impact

Description

The contract assumes all tokens have the same decimal precision as LP_TOKEN_DECIMALS = 9, but doesn't normalize token amounts based on their actual decimals.

  • The AMM should calculate liquidity provisions and swap amounts accurately, accounting for token decimals to maintain the constant product formula (x * y = k) and proportional LP token minting.

  • The codebases assumes all tokens use the same decimals as LP_TOKEN_DECIMALS = 9 (defined in constants.rs), without normalizing amounts based on token-specific decimals (e.g., USDC at 6, SOL at 8). This leads to incorrect LP token minting in liquidity_operations.rs and swap outputs in swap_operations.rs, causing financial losses or pool imbalances.

// Root cause in the codebase with @> marks to highlight the relevant sectionRoot + Impact:
rust
```rust
// liquidity_operations.rs
fn liquidity_calculation(amount_a: u64, amount_b: u64, ...) -> Result<u64> {
let amount_a: u128 = amount_a as u128; // @> No normalization for token_a.decimals
let amount_b: u128 = amount_b as u128; // @> No normalization for token_b.decimals
let liquidity: u128 = (amount_a * amount_b).checked_sqrt()?;
...
}
// swap_operations.rs
let numerator: u128 = (reserve_b as u128).checked_mul(amount_in as u128)?;
let denominator: u128 = (reserve_a as u128).checked_add(amount_in as u128)?;
let amount_out: u64 = numerator.div_floor(&denominator) as u64; // @> No decimal normalization
```

Risk

Likelihood:Medium

  • Reason 1: Tokens like USDC (6 decimals) or SOL (8 decimals) are commonly paired in AMMs, leading to frequent mismatches with LP_TOKEN_DECIMALS = 9.

  • Reason 2: Users interact with pools without explicit decimal checks, making this issue prevalent in diverse token pairs.

  • Impact: High

    • Users receive fewer LP tokens or swap outputs than expected, resulting in direct financial losses.

    • Pool imbalances disrupt the x * y = k invariant, enabling arbitrage attacks or further losses.Impact:

Proof of Concept

```rust
// Assume token A has 6 decimals, token B has 18 decimals
let amount_a = 1_000_000u64; // 1 token A
let amount_b = 1_000_000_000_000_000_000u64; // 1 token B
// Without normalization
let liquidity = (amount_a as u128 * amount_b as u128).sqrt();
println!("Incorrect Liquidity: {}", liquidity);
// Expected result after normalization
let norm_a = (amount_a as u128) * 10u128.pow(12); // normalize to 18 decimals
let norm_b = amount_b as u128; // already 18 decimals
let corrected_liquidity = (norm_a * norm_b).sqrt();
println!("Corrected Liquidity: {}", corrected_liquidity);
```

Recommended Mitigation:


diff
```diff
// liquidity_operations.rs
fn liquidity_calculation(amount_a: u64, amount_b: u64, token_a_decimals: u8, token_b_decimals: u8) -> Result<u64> {
- let amount_a: u128 = amount_a as u128;
- let amount_b: u128 = amount_b as u128;
+ let amount_a_normalized = amount_a as u128 * 10u128.pow((LP_TOKEN_DECIMALS - token_a_decimals) as u32);
+ let amount_b_normalized = amount_b as u128 * 10u128.pow((LP_TOKEN_DECIMALS - token_b_decimals) as u32);
let liquidity: u128 = (amount_a_normalized * amount_b_normalized).checked_sqrt()?;
...
}
```
Updates

Lead Judging Commences

0xtimefliez Lead Judge 10 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.