QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: high
Valid

Permanently Locked Owner Fees in `UpliftOnlyExample` Contract Due to Missing Withdrawal Mechanism

Summary

The UpliftOnlyExample contract collects owner fees from swap operations and stores them in the contract address (address(this)), but lacks any mechanism to withdraw these accumulated fees. This results in permanently locked funds as neither the contract owner nor any other party can access these fees once collected.

Vulnerability Details

The issue occurs in the fee collection mechanism implemented in the onAfterSwap hook function of the UpliftOnlyExample contract. Here's a detailed breakdown:

Fee Collection Process:
UpliftOnlyExample.sol#L343

if (hookSwapFeePercentage > 0) {
uint256 hookFee = params.amountCalculatedRaw.mulUp(hookSwapFeePercentage);
// ... fee calculation logic ...
uint256 quantAMMFeeTake = IUpdateWeightRunner(_updateWeightRunner).getQuantAMMUpliftFeeTake();
uint256 ownerFee = hookFee;
if (quantAMMFeeTake > 0) {
uint256 adminFee = hookFee / (1e18 / quantAMMFeeTake);
ownerFee = hookFee - adminFee;
address quantAMMAdmin = IUpdateWeightRunner(_updateWeightRunner).getQuantAMMAdmin();
_vault.sendTo(feeToken, quantAMMAdmin, adminFee);
}
if (ownerFee > 0) {
@> _vault.sendTo(feeToken, address(this), ownerFee);
emit SwapHookFeeCharged(address(this), feeToken, ownerFee);
}
}

Here, you can see that owner fees are sent to address(this) instead of a withdrawable address.

The UpliftOnlyExample contract inherits from Ownable but doesn't implement any withdrawal functionality. No function exists in the contract or its inherited contracts to transfer tokens from the contract address.

Impact

All owner fees collected through swap operations are permanently locked in the contract

Tools Used

Manual Review

Recommendations

Add Withdrawal Function in UpliftOnlyExample contract:

function withdrawFees(IERC20 token, uint256 amount) external onlyOwner {
require(amount > 0, "Amount must be greater than 0");
require(token.balanceOf(address(this)) >= amount, "Insufficient balance");
token.transfer(owner(), amount);
emit FeeWithdrawn(address(token), amount, owner());
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_ownerFee_cannot_be_withdrawn

Likelihood: High, every swap. Impact: High, funds are stuck.

Support

FAQs

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