Part 2

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

Misdirected WETH Transfers Leading to Permanent Fund Loss

Summary

The sendWethToFeeRecipients function incorrectly sends protocol WETH rewards to the WETH token contract address instead of the intended marketMakingEngineConfiguration.protocolFeeRecipients address. This critical flaw results in irreversible loss of funds, as the WETH contract cannot process or recover these tokens.

Vulnerability Details

  • The function passes the WETH token address (e.g., 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) as the recipient parameter to distributeProtocolAssetReward, instead of the actual marketMakingEngineConfiguration.protocolFeeRecipients address.

Technical Analysis

  1. Intended Workflow:

    • The distributeProtocolAssetReward function should distribute WETH rewards to predefined marketMakingEngineConfiguration.protocolFeeRecipients address

  2. Actual Behavior:

    • WETH rewards are sent to the WETH contract address, which lacks functionality to forward or recover these funds.

    • Example: Sending 100 ETH to 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 locks the tokens permanently.

Exploit Scenario

  1. Setup:

    • Protocol accumulates 500 ETH in rewards (availableProtocolWethReward = 500e18).

    • distributeProtocolAssetReward is configured to send rewards to the WETH contract address.

  2. Attack:

    • A legitimate transaction calls sendWethToFeeRecipients.

    • The function transfers 500 ETH to the WETH contract address.

  3. Result:

    • The 500 ETH is permanently stuck in the WETH contract, as it cannot initiate transfers or interact with external addresses.

Impact

WETH rewards sent to the WETH contract are irrecoverable

Tools Used

Manuel Review

Recommendations

Retrieve protocolFeeRecipients from the marketMakingEngineConfiguration and pass them to distributeProtocolAssetReward

function sendWethToFeeRecipients(uint128 marketId) external onlyRegisteredEngine(marketId) {
// loads the fee data storage pointer
Market.Data storage market = Market.loadExisting(marketId);
// reverts if no protocol weth rewards have been collected
if (market.availableProtocolWethReward == 0) revert Errors.NoWethFeesCollected();
// loads the market making engine configuration data storage pointer
MarketMakingEngineConfiguration.Data storage marketMakingEngineConfiguration =
MarketMakingEngineConfiguration.load();
// cache the weth address
address weth = marketMakingEngineConfiguration.weth;
+ address protocolFeeRecipient = marketMakingEngineConfiguration.protocolFeeRecipients;
// load weth collateral configuration
Collateral.Data storage wethCollateralData = Collateral.load(weth);
// convert collected fees to UD60x18 and convert decimals if needed, to ensure it's using the network's weth
// decimals value
uint256 availableProtocolWethReward =
wethCollateralData.convertUd60x18ToTokenAmount(ud60x18(market.availableProtocolWethReward));
// get total shares
UD60x18 totalShares = ud60x18(marketMakingEngineConfiguration.totalFeeRecipientsShares);
// this condition can never be hit since if shares is zero, availableProtocolWethReward
// will also be zero, since in convertAccumulatedFeesToWeth at the end if there are no
// fee recipient shares all weth rewards would go to the vaults
if (totalShares.isZero()) {
// if total shares is zero, revert
revert Errors.NoSharesAvailable();
}
// set to zero the amount of pending weth to be distributed
market.availableProtocolWethReward = 0;
// sends the accumulated protocol weth reward to the configured fee recipients
_ marketMakingEngineConfiguration.distributeProtocolAssetReward(weth, availableProtocolWethReward);
+ marketMakingEngineConfiguration.distributeProtocolAssetReward(protocolFeeRecipient, availableProtocolWethReward);
// emit event to log the weth sent to fee recipients
emit LogSendWethToFeeRecipients(marketId, availableProtocolWethReward);
}
Updates

Lead Judging Commences

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

Support

FAQs

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