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

Minimum Deposit Amount Can be Higher Than Maximum Deposit Amount blocking User Deposits

Summary

The setMinMaxDepositAmount function allows the owner to set minimum deposit amount higher than the maximum deposit
amount, which leads to a logical contradiction and prevents users from depositing funds into the protocol.

Though the issue is base on the protocol owner but its still a proper way to add some validation on some important functionality like this. For example i checked the deploy script which send the right values in the minDepositAmount and maxDepositAmount but no validation to also check in the initialize if minDepositAmount is less than maxDepositAmount

We trust the owner to send the right values according to the protocol but still the owner shouldnt make any mistakes

Vulnerability Details

function setMinMaxDepositAmount(uint256 _minDepositAmount, uint256 _maxDepositAmount) external onlyOwner {
// No validation that min <= max
minDepositAmount = _minDepositAmount;
maxDepositAmount = _maxDepositAmount;
}

When depositing, the contract validates:

function deposit(uint256 amount) external payable nonReentrant {
if (amount < minDepositAmount) {
revert Error.InsufficientAmount();
}
if (totalDepositAmount + amount > maxDepositAmount) {
revert Error.ExceedMaxDepositCap();
}
// ...
}

POC showing how deposits become impossible:

function test_minimumAndMaximumIssue() public {
// this issue allows the minimum deposit to be more than the maximum deposit
vm.prank( PerpetualVault(vault).owner());
PerpetualVault(vault).setMinMaxDepositAmount(1000, 0);
// the minimum Deposist amount is greater than the max deposit amount
assertGt(PerpetualVault(vault).minDepositAmount(), PerpetualVault(vault).maxDepositAmount());
IERC20 collateralToken = PerpetualVault(vault).collateralToken();
address bob = makeAddr("bob");
////////////////////////////////////////////////////////////
// Get USDC token and setup deposit
address whale = 0x489ee077994B6658eAfA855C308275EAd8097C4A;
vm.startPrank(whale);
uint256 amount = 1e10;
collateralToken.transfer(bob, amount);
vm.stopPrank();
// Create deposit and order
vm.startPrank(bob);
uint256 executionFee = PerpetualVault(vault).getExecutionGasLimit(true);
collateralToken.approve(vault, amount);
PerpetualVault(vault).deposit{value: executionFee * tx.gasprice}(amount);
vm.stopPrank();
//////////////////////////////////////////////////////////////////
MarketPrices memory prices = mockData.getMarketPrices();
bytes[] memory data = new bytes[](2);
data[0] = abi.encode(3380000000000000);
address keeper = PerpetualVault(vault).keeper();
vm.prank(keeper);
PerpetualVault(vault).run(true, false, prices, data);
GmxOrderExecuted(true);
delete data;
vm.prank(keeper);
PerpetualVault(vault).runNextAction(prices, data);
}

The Foundry Shell Response

➜ 2025-02-gamma git:(main) ✗ forge test --mt test_minimumAndMaximumIssue -vvvv --via-ir --rpc-url arbitrum
[⠒] Compiling...
[⠘] Compiling 1 files with Solc 0.8.28
[⠃] Solc 0.8.28 finished in 20.43s
Compiler run successful with warnings:
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.06ms (314.07µs CPU time)
Ran 1 test for test/PerpetualVault.t.sol:PerpetualVaultTest
[FAIL: ExceedMaxDepositCap()] test_minimumAndMaximumIssue() (gas: 157039)
│ ......... │ └─ ← [Return] true
│ └─ ← [Return] true
├─ [16366] TransparentUpgradeableProxy::fallback(10000000000 [1e10])
│ ├─ [14942] PerpetualVault::deposit(10000000000 [1e10]) [delegatecall]
│ │ └─ ← [Revert] ExceedMaxDepositCap()
│ └─ ← [Revert] ExceedMaxDepositCap()
└─ ← [Revert] ExceedMaxDepositCap()
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 786.52ms (776.26ms CPU time)
Ran 2 test suites in 4.33s (795.57ms CPU time): 1 tests passed, 1 failed, 0 skipped (2 total tests)
Failing tests:
Encountered 1 failing test in test/PerpetualVault.t.sol:PerpetualVaultTest
[FAIL: ExceedMaxDepositCap()] test_minimumAndMaximumIssue() (gas: 157039)
Encountered a total of 1 failing tests, 1 tests succeeded

Impact

MEDIUM - The owner can effectively pause all deposits by setting contradictory min/max values, bypassing the intended
depositPaused functionality.

Tools Used

  • Manual code review

Recommendations

Add validation in setMinMaxDepositAmount:

function setMinMaxDepositAmount(uint256 _minDepositAmount, uint256 _maxDepositAmount) external onlyOwner {
require(_minDepositAmount <= _maxDepositAmount, "Min must be <= max");
minDepositAmount = _minDepositAmount;
maxDepositAmount = _maxDepositAmount;
}

This ensures deposits always have a valid range where min ≤ amount ≤ max.

Updates

Lead Judging Commences

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

Admin is trusted / Malicious keepers

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. Keepers are added by the admin, there is no "malicious keeper" and if there is a problem in those keepers, that's out of scope. ReadMe and known issues states: " * System relies heavily on keeper for executing trades * Single keeper point of failure if not properly distributed * Malicious keeper could potentially front-run or delay transactions * Assume that Keeper will always have enough gas to execute transactions. There is a pay execution fee function, but the assumption should be that there's more than enough gas to cover transaction failures, retries, etc * There are two spot swap functionalies: (1) using GMX swap and (2) using Paraswap. We can assume that any swap failure will be retried until success. " " * Heavy dependency on GMX protocol functioning correctly * Owner can update GMX-related addresses * Changes in GMX protocol could impact system operations * We can assume that the GMX keeper won't misbehave, delay, or go offline. " "Issues related to GMX Keepers being DOS'd or losing functionality would be considered invalid."

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

Admin is trusted / Malicious keepers

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. Keepers are added by the admin, there is no "malicious keeper" and if there is a problem in those keepers, that's out of scope. ReadMe and known issues states: " * System relies heavily on keeper for executing trades * Single keeper point of failure if not properly distributed * Malicious keeper could potentially front-run or delay transactions * Assume that Keeper will always have enough gas to execute transactions. There is a pay execution fee function, but the assumption should be that there's more than enough gas to cover transaction failures, retries, etc * There are two spot swap functionalies: (1) using GMX swap and (2) using Paraswap. We can assume that any swap failure will be retried until success. " " * Heavy dependency on GMX protocol functioning correctly * Owner can update GMX-related addresses * Changes in GMX protocol could impact system operations * We can assume that the GMX keeper won't misbehave, delay, or go offline. " "Issues related to GMX Keepers being DOS'd or losing functionality would be considered invalid."

Support

FAQs

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