Part 2

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

[M-07] Incorrect Total Share Calculation Allows Exceeding Maximum Fee Shares

Summary

The configureFeeRecipient function incorrectly verifies the cumulative share allocations when updating an existing fee recipient. It adds the new share to totalFeeRecipientsShares without first subtracting the recipient's previous share, allowing the total shares to exceed Constants.MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES.

Vulnerability Details

The function configureFeeRecipient is responsible for setting fee recipients and their share allocations. The issue arises in the verification step that checks whether the total fee recipient shares exceed the maximum allowed limit.

Currently, the function performs the cap verification before subtracting the old share of an existing fee recipient. This creates a scenario where the system only considers the new share when checking against Constants.MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES, instead of factoring in the net change in shares.

if (share > 0) {
UD60x18 totalFeeRecipientsSharesX18 = ud60x18(marketMakingEngineConfiguration.totalFeeRecipientsShares);
if (
totalFeeRecipientsSharesX18.add(ud60x18(share)).gt(
ud60x18(Constants.MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES)
)
) {
revert Errors.FeeRecipientShareExceedsLimit();
}
}

The function checks whether totalFeeRecipientsShares + new share exceeds the max limit. However, it does not first remove the old share if the recipient already exists.

Impact

The impact is High, as it allows protocol fee allocations to surpass their intended limits, leading to potential financial inconsistencies. This could cause misallocated fees, making it difficult for governance or owners to fairly distribute protocol rewards.

If fee allocations surpass the intended cap, it could:

  • Disrupt fee distribution logic, resulting in unfair or unpredictable payouts.

  • Enable an owner to allocate more than the allowed amount, potentially blocking future allocations.

  • Lead to financial instability if calculations depend on an assumed hard cap.

The likelihood is Medium as:

  • The function is restricted to onlyOwner, reducing the risk of arbitrary exploitation.

  • However, any owner or governance entity can mistakenly configure fee recipients in a way that exceeds intended limits.

  • The issue does not require complex exploitation—simply updating an existing recipient's share in an incremental manner can expose the bug.


  • Assume MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES = 100.

  • Current fee recipients:

    • Alice has a share of 40.

    • Bob has a share of 50.

    • totalFeeRecipientsShares = 90.

  • The owner tries to update Alice’s share:

    configureFeeRecipient(Alice, 60);

    configureFeeRecipient(Alice, 60);

    • The check adds 60 to 90 without first subtracting Alice’s old 40.

    • 90 + 60 = 150 (exceeds 100, revert occurs)

    • The correct calculation should have been (90 - 40) + 60 = 110, which should still revert, but the logic is flawed.

  • Risk of Bypassing Limits

    • An attacker could incrementally increase the share allocations by first setting low values and then progressively increasing them.

    • By doing so, they can exceed the maximum allocation, disrupting fee distribution logic and impacting protocol stability.

POC

function testFeeRecipientUpdate() public {
address owner = address(0x123);
address recipient = address(0x456);
// Assume max configurable fee shares is 100
uint256 MAX_SHARES = Constants.MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES;
// Step 1: Owner sets recipient’s share to 40
vm.prank(owner);
contractInstance.configureFeeRecipient(recipient, 40);
// Step 2: Owner updates recipient’s share to 70
vm.prank(owner);
contractInstance.configureFeeRecipient(recipient, 70); // Expected: Should revert, but may pass incorrectly
// Check if total shares exceed the max limit
uint256 totalShares = contractInstance.totalFeeRecipientsShares();
assert(totalShares <= MAX_SHARES); // Should always be true
}

Recommendations

The function should subtract the old share before checking the cap, ensuring that totalFeeRecipientsShares remains within limits.

uint256 adjustedTotal = marketMakingEngineConfiguration.totalFeeRecipientsShares;
// Subtract the old fee recipient share before verification
if (oldFeeRecipientShares > 0) {
adjustedTotal -= oldFeeRecipientShares;
}
// Now check against the cap
if (adjustedTotal + share > Constants.MAX_CONFIGURABLE_PROTOCOL_FEE_SHARES) {
revert Errors.FeeRecipientShareExceedsLimit();
}
// Proceed with updating total shares
marketMakingEngineConfiguration.totalFeeRecipientsShares = adjustedTotal + share;

This ensures the total never exceeds the cap when updating an existing recipient.

  • Prevents bypassing limits via incremental updates.

  • Improves protocol stability by enforcing correct fee allocation logic.

Updates

Lead Judging Commences

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

MarketMakingEngineConfigurationBranch::configureFeeRecipient can cause DOS when attempting to change share value of an existing address

Support

FAQs

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