HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Usability Flaw - Requiring type(uint256).max for Maximum Liquidity Removal

Summary

The current implementation of the removeLiquidity() function in AaveDIVAWrapper.sol has a usability issue. Users must manually input type(uint256).max if they want to remove the maximum possible liquidity. This design choice will lead to confusion or errors during interaction with the protocol.

Vulnerability Details

Entrypoint - https://github.com/Cyfrin/2025-01-diva/blob/main/contracts/src/AaveDIVAWrapper.sol#L49C1-L56C1

and https://github.com/Cyfrin/2025-01-diva/blob/main/contracts/src/AaveDIVAWrapperCore.sol#L222C1-L224C10

The issue arises because the function checks if _positionTokenAmount == type(uint256).max to determine whether the user wants to remove their maximum allowable liquidity. While functional, this approach is unnecessarily complex for users. It assumes they are aware of the internal implementation details and know to input the specific value type(uint256).max.

if (_positionTokenAmount == type(uint256).max) {
_positionTokenAmountToRemove = _userBalanceShort > _userBalanceLong
? _userBalanceLong
: _userBalanceShort;
}

This forces users to input a specific constant (type(uint256).max) to achieve what should be an automatic process, introducing unnecessary friction.

Impact

Users are required to know and input a specific system-defined constant (type(uint256).max) to withdraw their maximum liquidity. This is counterintuitive and not user-friendly.

Higher Error Risk: If users fail to input the exact value, they may not successfully remove their desired liquidity.

Tools Used

Manual review

Recommendations

The contract could automatically handle this scenario without requiring user input, simplifying the interaction.

uint256 _positionTokenAmountToRemove = _positionTokenAmount;
if (_positionTokenAmount > _userBalanceShort || _positionTokenAmount > _userBalanceLong) {
_positionTokenAmountToRemove = _userBalanceShort > _userBalanceLong
? _userBalanceLong
: _userBalanceShort;
}

Another Option is to pass down a boolean (bool _maximumAmount) from the function input:

function removeLiquidity(
bytes32 _poolId,
uint256 _positionTokenAmount,
address _recipient
// @audit---------
bool _maximumAmount;
) external override nonReentrant returns (uint256) {
return _removeLiquidity(_poolId, _positionTokenAmount, _recipient, _maximumAmount);
}

In the Internal Function you could do this:

function _removeLiquidity(
bytes32 _poolId,
uint256 _positionTokenAmount,
address _recipient
// @audit---------
bool _maximumAmount;
) internal returns (uint256) {
// ......
uint256 _userBalanceShort = _shortTokenContract.balanceOf(msg.sender);
uint256 _userBalanceLong = _longTokenContract.balanceOf(msg.sender);
uint256 _positionTokenAmountToRemove = _positionTokenAmount;
// @audit---------
if (_maximumAmount) {
_positionTokenAmountToRemove = _userBalanceShort > _userBalanceLong ? _userBalanceLong : _userBalanceShort;
}
// Rest of code
}
Updates

Lead Judging Commences

bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Appeal created

cd_pandora Submitter
9 months ago
bube Lead Judge
9 months ago
cd_pandora Submitter
9 months ago
bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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