DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Precision Loss in Share Calculations Leading to Value Extraction

Summary

The _withdraw function performs multiple share-based calculations that can lead to precision loss and value extraction opportunities, particularly in scenarios with large numbers of shares or small deposit amounts.

Vulnerability Details

The function uses integer division for share calculations without properly accounting for rounding errors or precision loss. This occurs in multiple critical calculations involving shares, collateral amounts, and position sizes.

The vulnerability manifests in three key areas within the function:

  • Initial token balance calculation:

uint256 swapAmount = IERC20(indexToken).balanceOf(address(this)) * shares / totalShares;

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L1098C7-L1098C95

  • Collateral amount calculation:

uint256 collateralDeltaAmount = positionData.collateralAmount * shares / totalShares;

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L1106C6-L1106C92

  • Size calculation:

uint256 sizeDeltaInUsd = positionData.sizeInUsd * shares / totalShares;

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L1107C5-L1107C78

Each of these calculations follows the pattern of (amount * shares) / totalShares, which can lead to significant rounding errors, especially when:

  • The total number of shares is large

  • The withdrawal amount is small relative to total shares

  • Multiple withdrawals are performed sequentially

Impact

  • Users may receive less value than they should due to rounding down

  • Last depositor might be unable to withdraw their full amount

  • Malicious users could perform multiple small withdrawals to extract extra value

  • Accumulation of dust amounts over time could lead to locked funds

PoC:

contract PrecisionLossTest {
function demonstratePrecisionLoss() external {
PerpetualVault vault = PerpetualVault(vault_address);
// Setup: Deploy with 1000 total shares
// User A deposits 100 tokens (10 shares)
// User B deposits 900 tokens (90 shares)
// Scenario 1: User A withdraws in 2 parts
uint256 firstWithdraw = 5; // 5 shares
uint256 secondWithdraw = 5; // remaining 5 shares
// First withdrawal
bytes memory metadata = abi.encode(acceptablePrice);
MarketPrices memory prices = getCurrentPrices();
vault._withdraw(depositIdA, metadata, prices);
// Second withdrawal
vault._withdraw(depositIdA, metadata, prices);
// Assert: Total received amount is less than original deposit
uint256 totalReceived = firstWithdrawAmount + secondWithdrawAmount;
assert(totalReceived < originalDeposit); // Will pass, showing loss
}
}

Tools Used

  • Slither

  • Manual Review

  • Mathematical Analysis

Recommendations

  • Implement a decimal-based share calculation system:

function _withdraw(uint256 depositId, bytes memory metadata, MarketPrices memory prices) internal {
// Use higher precision for calculations
uint256 constant PRECISION = 1e18;
uint256 shares = depositInfo[depositId].shares;
require(shares > 0, "Zero shares");
// Improve precision in calculations
uint256 shareRatio = (shares * PRECISION) / totalShares;
if (_isLongOneLeverage(beenLong)) {
uint256 balance = IERC20(indexToken).balanceOf(address(this));
uint256 swapAmount = (balance * shareRatio) / PRECISION;
// Continue with improved precision...
}
// ... rest of the function
}
  • Add minimum withdrawal amount checks to prevent dust attacks

  • Implement rounding protection mechanisms

  • Add share price calculations that account for decimals

  • Consider implementing a withdrawal fee to discourage exploitation

  • Add invariant checks to ensure withdrawal amounts are reasonable

  • Document precise mathematical formulas for all share calculations

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

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

Give us feedback!