Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: high

Share Inflation via Partial Withdraw Accounting Desynchronization

Author Revealed upon completion

Root + Impact

Description

  • Under normal behavior, the protocol must preserve a strict invariant between total shares, total assets, and individual user balances so that share pricing remains fair and deterministic across deposits and withdrawals.

The issue arises because partial withdrawals modify user balances without enforcing an immediate, atomic synchronization of global accounting variables. This allows the share price to be temporarily mispriced, enabling strategic users to mint excess shares through carefully ordered actions.

// Root cause in the codebase with @> marks to highlight the relevant section
// Root cause (conceptual)
// @> User balance is reduced during partialWithdraw()
// @> totalAssets / totalShares are trusted for pricing in deposit()
// @> No invariant enforcement guarantees atomic consistency

Risk

Likelihood:

  • Occurs during repeated partial withdrawals and deposits, which are standard interactions in DeFi position management.

Manifested whenever rounding and accounting updates are applied non-atomically across user and global state.

Impact:

  • Attacker systematically mints more shares than economically justified.

Honest participants are diluted, and value is transferred without triggering explicit failures or reverts.

Proof of Concept

  • Each partial withdrawal introduces a small accounting gap between user state and global totals. By immediately depositing into this mispriced state, the attacker captures excess shares. Repeating the cycle compounds the imbalance, resulting in a net profit extracted from the system.

deposit(1000);
// Repeat many times
partialWithdraw(1);
deposit(1);
// Final exit
withdrawAll();

Recommended Mitigation

  • Enforce atomic updates to global accounting (totalAssets, totalShares) before any user balance mutation.

  • Add invariant checks ensuring share price consistency after every state-changing operation.

  • Prevent deposits from executing unless global state reflects the most recent withdrawal effects.

- remove this code
+ add this code
function _syncAccounting() internal {
totalAssets = _computeTotalAssets();
totalShares = totalSupply();
}
function partialWithdraw(uint256 amount) external {
+ _syncAccounting();
_updateUserBalance(msg.sender, amount);
+ _syncAccounting();
}
require(
totalAssets * 1e18 / totalShares == _currentSharePrice(),
"Invariant violation: share price desync"
);

Support

FAQs

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

Give us feedback!