SSSwap

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

Incorrect LP Token Minting Due to Stale Vault State in provide_liquidity Function

Root + Impact

https://github.com/CodeHawks-Contests/2025-05-ssswap/blob/27a2ef878023b0111ec4acc33503d99ae1ae36fa/programs/amm/src/instructions/liquidity_operations.rs#L251-L269

Description

In the provide_liquidity function, the protocol transfers Token A and Token B to the vaults via CPI, then immediately calculates the number of LP tokens to mint using liquidity_calculation. However, it fails to call .reload() on the vault accounts after the transfers. As Anchor's AccountInfo caches on-chain state, the calculation uses outdated balances (pre-transfer), which can result in incorrect LP minting.

// Root cause in the codebase with @> marks to highlight the relevant section
transfer_tokens(...)?; // Token A transferred
transfer_tokens(...)?; // Token B transferred
@> let lp_to_mint: u64 = liquidity_calculation(amount_a, amount_b)?; // Uses stale vault state

Risk

Likelihood:

  • This occurs every time the provide_liquidity function is invoked without reloading the vault accounts.

  • Anchor caches account data unless .reload() is explicitly called.

  • An attacker can automate this to repeatedly exploit incorrect LP minting logic.

Impact:

  • LP token recipients will receive more tokens than they should.

  • When these LP tokens are later redeemed, the pool may be drained of assets.

  • Honest liquidity providers suffer from dilution, harming protocol trust and solvency.

Proof of Concept

  • Initial pool: 1000 Token A and 1000 Token B.

  • Attacker adds 100 A + 100 B via provide_liquidity.

  • Tokens are transferred, vaults are updated on-chain but not in memory.

  • No .reload(), vaults still show 1000 A + 1000 B.

  • LP calculation assumes lower total liquidity → mints more LP tokens.

  • Attacker redeems later for more than fair share, extracting excess value.


Recommended Mitigation

Reload account state after transfers but before any calculations relying on vault data:

+ context.accounts.vault_a.reload()?; // Fetch up-to-date Token A balance
+ context.accounts.vault_b.reload()?; // Fetch up-to-date Token B balance
+ context.accounts.lp_mint.reload()?; // Optional: ensure LP mint state is fresh
let lp_to_mint: u64 = liquidity_calculation(amount_a, amount_b)?;
Updates

Lead Judging Commences

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

lack of account reload causes liquidity calculations to be outdated

Support

FAQs

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