DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: high
Invalid

Matched rewards can be frontrun

Summary

Liquidity Providers (LPs) are generating Ditto rewards from matched trades, but the current system allows others to front-run and steal those rewards.

Vulnerability Details

All LPs are rewarded with a fixed rate:

totalReward = elapsedTime * dittoMatchedRate

Each LPs gets rewards based on the percentage of shares owned:

userReward = userShares / totalShares * totalReward

An LP can monitor the mempool, identify transactions that match orders, and front-run the transaction to secure rewards before new shares are minted.

function testFrontrunDittoMatchedRewardDistribution() public {
// user1 provides liquidty to orderbook and matches trade
vm.startPrank(receiver);
createLimitBid(DEFAULT_PRICE, DEFAULT_AMOUNT);
skipTimeAndSetEth({skipTime: skipTime, ethPrice: 4000 ether});
createLimitAsk(DEFAULT_PRICE, DEFAULT_AMOUNT);
vm.stopPrank();
// user2 provides liquidity to orderbook
vm.startPrank(sender);
createLimitBid(DEFAULT_PRICE, DEFAULT_AMOUNT);
skipTimeAndSetEth({skipTime: skipTime, ethPrice: 4000 ether});
// calculate the pending reward before second trade is matched
uint matchedRewardsWhenFrontrun = diamond.getDittoMatchedReward(vault, receiver);
// match trade
createLimitAsk(DEFAULT_PRICE, DEFAULT_AMOUNT);
// calculate pending reward after second trade is matched
uint matchedRewardsWhenNotFrotrun = diamond.getDittoMatchedReward(vault, receiver);
// comparison
assertLt(matchedRewardsWhenNotFrotrun, matchedRewardsWhenFrontrun);
console.log("matchedRewardsWhenFrontrun are", matchedRewardsWhenFrontrun);
console.log("matchedRewardsWhenNotFrotrun are", matchedRewardsWhenNotFrotrun);
}

Impact

Sophisticated LPs can exploit the system by front-running matched orders to unfairly claim Ditto returns meant for others. This undermines the liquidity mining model, deterring potential liquidity providers and compromising the effectiveness of risk mitigation strategies for liquidations.

Recommendations

Consider changing the way of allocating rewards:

userReward = eth * (timeTillMatch / 1 days) * dittoMatchedRate;

This formula ensures rewards are tied to each unit of matched liquidity, not to the shared pool. This way, matched trades are not at the expense of other liquidity providers.

Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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