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.
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.
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.
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.
This issue was identified via manual review.
Explicitly call .reload()?
on all SPL token accounts and mints immediately after any CPI that changes their state.
add_liquidity
:remove_liquidity
:The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.