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

Unchecked Token Transfer Amount Leading to Fund Lock

Summary

The _handleReturn function calculates token amounts for transfers using division operations that could result in precision loss and potential fund locking due to insufficient balance checks and rounding errors.

Vulnerability Details

Integer division rounding coupled with insufficient balance validation creates edge cases where funds can become permanently locked.

The vulnerability stems from several compounding issues:

  • Division before multiplication in share calculations:

amount = collateralToken.balanceOf(address(this)) * shares / totalShares;

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

OR

amount = withdrawn + balanceBeforeWithdrawal * shares / totalShares;

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

  • No validation that totalShares is non-zero before division

  • No check that the calculated amount is less than or equal to the contract's actual token balance

  • The division operation can round down to zero even when shares > 0, leading to locked funds

Impact

  • Users with small share amounts could have their funds permanently locked if the division rounds to zero

  • Contract could attempt to transfer more tokens than it has available

  • Mathematical precision loss could accumulate over time, leading to accounting errors

PoC:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VulnerabilityPoC {
function demonstrateRoundingLock() public pure returns (uint256) {
uint256 balance = 100;
uint256 shares = 1;
uint256 totalShares = 1000;
// This will round to 0 even though user has valid shares
uint256 amount = balance * shares / totalShares;
return amount; // Returns 0, locking user funds
}
}

Tools Used

  • Manual review

  • Slither static analyzer

  • Foundry testing framework

Recommendations

  • Implement SafeMath operations and minimum amount checks:

function _handleReturn(uint256 withdrawn, bool positionClosed, bool refundFee) internal {
require(totalShares > 0, "Invalid total shares");
(uint256 depositId) = flowData;
uint256 shares = depositInfo[depositId].shares;
require(shares > 0, "Invalid shares");
uint256 amount;
uint256 contractBalance = collateralToken.balanceOf(address(this));
if (positionClosed) {
// Multiply before divide to prevent precision loss
amount = (contractBalance * shares) / totalShares;
} else {
uint256 balanceBeforeWithdrawal = contractBalance - withdrawn;
amount = withdrawn + (balanceBeforeWithdrawal * shares) / totalShares;
}
require(amount > 0, "Amount too small");
require(amount <= contractBalance, "Insufficient balance");
// Rest of the function...
}
  • Consider implementing a minimum share amount to prevent dust amounts

  • Add comprehensive balance checks before transfers

  • Consider using fixed-point arithmetic libraries for better precision
    Add emergency withdrawal function for edge cases

Updates

Lead Judging Commences

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

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

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

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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

Give us feedback!