Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Fees remain in receivedFees, allowing infinite conversion in convertAccumulatedFeesToWeth

Summary

Ensure market.receivedFees is updated after converting fees to WETH in convertAccumulatedFeesToWeth to maintain correct state transitions. The current code does not do this.

Vulnerability Details

ctx.tokensSwapped = dexSwapStrategy.executeSwapExactInput(swapCallData);
}
// uint256 -> ud60x18
ctx.receivedWethX18 = wethCollateral.convertTokenAmountToUd60x18(ctx.tokensSwapped);
}
// handles distribution of the weth reward between the protocol and market
_handleWethRewardDistribution(market, asset, ctx.receivedWethX18);
// emit event to log the conversion of fees to weth
emit LogConvertAccumulatedFeesToWeth(ctx.receivedWethX18.intoUint256());
}
function _handleWethRewardDistribution(
Market.Data storage market,
address assetOut,
UD60x18 receivedWethX18
)
internal
{
// cache the total fee recipients shares as UD60x18
UD60x18 feeRecipientsSharesX18 = ud60x18(MarketMakingEngineConfiguration.load().totalFeeRecipientsShares);
// calculate the weth rewards for protocol and vaults
UD60x18 receivedProtocolWethRewardX18 = receivedWethX18.mul(feeRecipientsSharesX18);
UD60x18 receivedVaultsWethRewardX18 =
receivedWethX18.mul(ud60x18(Constants.MAX_SHARES).sub(feeRecipientsSharesX18));
// calculate leftover reward
UD60x18 leftover = receivedWethX18.sub(receivedProtocolWethRewardX18).sub(receivedVaultsWethRewardX18);
// add leftover reward to vault reward
receivedVaultsWethRewardX18 = receivedVaultsWethRewardX18.add(leftover);
// adds the weth received for protocol and vaults rewards using the assets previously paid by the engine
// as fees, and remove its balance from the market's `receivedMarketFees` map
market.receiveWethReward(assetOut, receivedProtocolWethRewardX18, receivedVaultsWethRewardX18);
// recalculate markes' vaults credit delegations after receiving fees to push reward distribution
Vault.recalculateVaultsCreditCapacity(market.getConnectedVaultsIds());
}

The function retrieves the accumulated fees for an asset (market.receivedFees.tryGet(asset)), converts them to WETH, and calls _handleWethRewardDistribution.

It fails to update the receivedFees for the asset after processing, meaning the same fees are reprocessed indefinitely in subsequent calls.

Impact

Fees already converted to WETH remain in receivedFees, allowing keepers to repeatedly claim them. Causes double-spending of fees, corrupting reward distributions and draining protocol funds.

Tools Used

Manual Review

Recommendations

Deduct the processed fees for the asset from market.receivedFees in convertAccumulatedFeesToWeth

Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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