DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Valid

Order submission can left opposite order with dust amount

Summary

On creation of order, ethAmount and ercAmount must be higher than minimum threshold. Otherwise it introduces ability to create dust orders and collude orderbook algorithm

This prevents small orders from clogging up the Orderbook.

However this check can be bypassed while matching orders. Submitting bid orders can left ask and shortLimit orders with dust amount; and submitting ask or short orders can left bid order with dust amount
Suppose ask order can fulfill 10 ETH, bid created with amount 9.99 ETH, as a result orders are matched and there is ask order with too low amount.
This situation occurs while submitting askOrders, bidOrders and shortLimit orders

Vulnerability Details

Details are similar for all types of orders, I will describe by example of ask orders.

  1. At the time of creating ask order there is check that eth amount of order is higher than threshold:

uint256 eth = price.mul(ercAmount);
uint256 minAskEth = LibAsset.minAskEth(asset);
if (eth < minAskEth) revert Errors.OrderUnderMinimumSize();
  1. Then flow goes to LibOrders.sellMatchAlgo(). Refer to part that handles partial fulfillment of highest bid order:https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/libraries/LibOrders.sol#L673-L687

// If the product of remaining ercAmount and price rounds down to 0 just close the bid order
bool dustErcAmount = (highestBid.ercAmount - incomingAsk.ercAmount)
.mul(highestBid.price) == 0;
if (dustErcAmount || incomingAsk.ercAmount == highestBid.ercAmount) {
matchOrder(s.bids, asset, highestBid.id);
updateBidOrdersOnMatch(s.bids, asset, highestBid.id, true);
} else {
@> s.bids[asset][highestBid.id].ercAmount =
@> highestBid.ercAmount - incomingAsk.ercAmount;
updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
}
incomingAsk.ercAmount = 0;
matchIncomingSell(asset, incomingAsk, matchTotal);
return;

It just updates highestBid.ercAmount, there is no validation that left ercAmount is too low.
The same behavior in matching bidOrders and limitShort orders too

Impact

Small orders can clogg up the orderbook. Also protocol's assumption violated:

This minimum check is done for incoming Orders as well as existing ones, so new orders cannot be smaller than a certain amount, and matched limit orders are removed from the OB if the remaining amount is too small.

Tools Used

Manual Review

Recommendations

Refactor condition:
https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/libraries/LibOrders.sol#L674

+ uint256 minBidEth = LibAsset.minBidEth(asset);
+
- bool dustErcAmount = (highestBid.ercAmount - incomingAsk.ercAmount).mul(highestBid.price) == 0;
+ bool dustErcAmount = (highestBid.ercAmount - incomingAsk.ercAmount).mul(highestBid.price) < minBidEth;

https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/BidOrdersFacet.sol#L208

+ uint256 minAskEth = LibAsset.minAskEth(asset);
+
- bool dustErcAmount = (lowestSell.ercAmount - incomingBid.ercAmount).mul(lowestSell.price) == 0;
+ bool dustErcAmount = (lowestSell.ercAmount - incomingBid.ercAmount).mul(lowestSell.price) < minAskEth;
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-145

Support

FAQs

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