Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Valid

Incorrect WETH reward distribution in `FeeDistributionBranch.sol#_handleWethRewardDistribution()`

Summary

The _handleWethRewardDistribution function contains a critical flaw in the way WETH rewards are distributed between protocol and vault fee recipients. The calculation directly multiplies the received WETH with the total fee shares without normalizing them, leading to incorrect distribution. This could result in over- or under-compensation of protocol or vaults, causing financial discrepancies.

Vulnerability Details

In the reward distribution mechanism, the function aims to split the received WETH between the protocol and vaults based on predefined shares. However, the calculations do not normalize the shares relative to a total (e.g., 100% or Constants.MAX_SHARES). Instead, the raw shares are applied, leading to potential over-allocation when combined.

UD60x18 receivedProtocolWethRewardX18 = receivedWethX18.mul(feeRecipientsSharesX18);
UD60x18 receivedVaultsWethRewardX18 = receivedWethX18.mul(ud60x18(Constants.MAX_SHARES).sub(feeRecipientsSharesX18));

Issue: The sum of receivedProtocolWethRewardX18 and receivedVaultsWethRewardX18 may exceed or not equal receivedWethX18 due to lack of normalization.

Expected behavior: The sum should always equal receivedWethX18.

PoC

Inputs:

  • receivedWethX18 = 100e18

  • feeRecipientsSharesX18 = 0.6e18 (intended as 60%)

  • Constants.MAX_SHARES = 1e18 (representing 100%)

Expected values:

  • Protocol: 60 WETH

  • Vaults: 40 WETH

Actual values:

receivedProtocolWethRewardX18 = 100e18 * 0.6e18; // Results in 60e36 (Incorrect scaling)
receivedVaultsWethRewardX18 = 100e18 * (1e18 - 0.6e18); // Results in 40e36 (Incorrect scaling)

The values are significantly larger than expected due to improper scaling with UD60x18 fixed-point math.

Test:

contract WethRewardDistributionTest is Test {
MarketMakingEngine engine;
Market.Data market;
function setUp() public {
engine = new MarketMakingEngine();
}
function testIncorrectWethDistribution() public {
uint256 receivedWeth = 100e18;
uint256 feeRecipientsShare = 0.6e18; // 60%
uint256 maxShares = 1e18; // 100%
uint256 protocolReward = receivedWeth * feeRecipientsShare;
uint256 vaultReward = receivedWeth * (maxShares - feeRecipientsShare);
assertEq(protocolReward + vaultReward, receivedWeth, "Distribution does not sum to 100 WETH");
}
}

This demonstrates that the sum of rewards exceeds receivedWeth, confirming incorrect calculations.

Impact

Vaults or protocol could receive more or less than their fair share of rewards, leading to financial imbalances.

Tools Used

Manual review.

Recommendations

Normalize the shares to a percentage of Constants.MAX_SHARES:

UD60x18 protocolShare = feeRecipientsSharesX18.div(ud60x18(Constants.MAX_SHARES));
UD60x18 receivedProtocolWethRewardX18 = receivedWethX18.mul(protocolShare);
UD60x18 receivedVaultsWethRewardX18 = receivedWethX18.sub(receivedProtocolWethRewardX18);
Updates

Lead Judging Commences

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

Appeal created

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Rounding errors in _handleWethRewardDistribution cause overestimation of available rewards leading to protocol insolvency when users claim fees tag.

Support

FAQs

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