SSSwap

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

Stale Token Account State Causes Incorrect Liquidity Calculations


Summary

The add_liquidity and remove_liquidity functions in liquidity_operations.rs perform multiple CPIs to the SPL-Token program—such as transfer_tokens, mint_to, burn, and transfer_checked—that mutate token accounts (vault_a, vault_b) or token mint accounts (lp_mint). However, the contract fails to invoke .reload() on these accounts immediately after the CPIs. This results in the in-memory state of these accounts remaining stale, leading to incorrect reserve or supply values being used in subsequent calculations.

This oversight can cause miscalculated LP issuance or redemption, reserve imbalances, and in extreme cases, arithmetic errors such as overflows or division by zero that disrupt the AMM logic or block transactions.

Vulnerability Details

Anchor frameworks cache the initial deserialized state of all accounts in ctx.accounts. When a CPI mutates the underlying token account or mint on-chain, the cached in-memory structure does not reflect this update unless .reload() is explicitly called. Failing to reload mutated accounts means subsequent logic will operate on outdated data.

For example, in add_liquidity, after a CPI call deposits tokens into vault_a, the value of ctx.accounts.vault_a.amount remains unchanged unless reloaded. If the LP minting logic relies on this stale value to calculate the number of LP tokens to mint, the calculation will be incorrect. Similar issues arise in remove_liquidity if the LP token supply or vault balances are used post-modification without reloading.

This can lead to LP over-issuance, under-redemption, or logic errors when assumptions about account state diverge from actual on-chain state.

Impact

Incorrect reserve or supply values due to stale memory representations can cause:

  • Over-issuance or under-issuance of LP tokens, diluting other LPs or benefiting attackers.

  • Reserve imbalance, distorting pool ratios and enabling arbitrage.

  • Division-by-zero or overflow errors, leading to transaction reverts and potential denial of service.

  • Loss of user trust, as the protocol behaves unpredictably due to desynchronized internal state.

Proof of Concept

Initial state:

  • vault_a.amount = 1000

  • vault_b.amount = 1000

  • lp_mint.supply = 1000

User deposits amount_a = 100, triggering a transfer CPI to vault_a. On-chain vault_a.amount = 1100, but in-memory it remains 1000. LP tokens are minted based on vault_a.amount = 1000, leading to a miscalculation:

  • Expected mint amount: (100 * 1000) / 1100 = 90.9...

  • Actual mint amount using stale data: (100 * 1000) / 1000 = 100

User receives 10 LP tokens more than justified, diluting the pool.

Tools Used

This issue was identified via manual review.

Recommendations

Explicitly call .reload()? on all SPL token accounts and mints immediately after any CPI that changes their state.

In add_liquidity:

transfer_tokens(..., &ctx.accounts.vault_a, ...)?;
+ ctx.accounts.vault_a.reload()?;
transfer_tokens(..., &ctx.accounts.vault_b, ...)?;
+ ctx.accounts.vault_b.reload()?;
mint_to(cpi_ctx, lp_to_mint)?;
+ ctx.accounts.lp_mint.reload()?;

In remove_liquidity:

burn(cpi_ctx_burn, lpt_to_redeem)?;
+ ctx.accounts.lp_mint.reload()?;
transfer_checked(..., &ctx.accounts.vault_a, ...)?;
+ ctx.accounts.vault_a.reload()?;
transfer_checked(..., &ctx.accounts.vault_b, ...)?;
+ ctx.accounts.vault_b.reload()?;
Updates

Lead Judging Commences

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