DittoETH

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

The `OrdersFacet.cancelOrderFarFromOracle` function does not refund escrowed funds when cancelling orders

Summary

Canceling orders via the OrdersFacet.cancelOrderFarFromOracle function does not refund the users' escrowed funds.

Vulnerability Details

According to the docs, the OrdersFacet.cancelOrderFarFromOracle function is used to handle the scenario when the orderId is close to the limit, i.e., type(uint16).max (65,535), used to deter attackers spamming the order book.

However, by using this function and canceling orders, the users owning the orders do not get their initially escrowed ETH/ERC-20 funds refunded. This is different from the regular canceling functions, e.g., the OrdersFacet.cancelShort function which refunds the user in line 95.

This cancelOrderFarFromOracle function is seemingly intended to mitigate order book spamming attacks. However, in the case that the order book contains many legitimate orders (due to having many users placing orders across a wide price range), the users will lose their escrowed funds when canceling their orders.

contracts/facets/OrdersFacet.sol#L124-L178

124: function cancelOrderFarFromOracle(
125: address asset,
126: O orderType,
127: uint16 lastOrderId,
128: uint16 numOrdersToCancel
129: ) external onlyValidAsset(asset) nonReentrant {
130: if (s.asset[asset].orderId < 65000) {
131: revert Errors.OrderIdCountTooLow();
132: }
133:
134: if (numOrdersToCancel > 1000) {
135: revert Errors.CannotCancelMoreThan1000Orders();
136: }
137:
138: if (msg.sender == LibDiamond.diamondStorage().contractOwner) {
139: if (
140: orderType == O.LimitBid
141: && s.bids[asset][lastOrderId].nextId == Constants.TAIL
142: ) {
143: s.bids.cancelManyOrders(asset, lastOrderId, numOrdersToCancel);
144: } else if (
145: orderType == O.LimitAsk
146: && s.asks[asset][lastOrderId].nextId == Constants.TAIL
147: ) {
148: s.asks.cancelManyOrders(asset, lastOrderId, numOrdersToCancel);
149: } else if (
150: orderType == O.LimitShort
151: && s.shorts[asset][lastOrderId].nextId == Constants.TAIL
152: ) {
153: s.shorts.cancelManyOrders(asset, lastOrderId, numOrdersToCancel);
154: } else {
155: revert Errors.NotLastOrder();
156: }
157: } else {
158: //@dev if address is not DAO, you can only cancel last order of a side
159: if (
160: orderType == O.LimitBid
161: && s.bids[asset][lastOrderId].nextId == Constants.TAIL
162: ) {
163: s.bids.cancelOrder(asset, lastOrderId);
164: } else if (
165: orderType == O.LimitAsk
166: && s.asks[asset][lastOrderId].nextId == Constants.TAIL
167: ) {
168: s.asks.cancelOrder(asset, lastOrderId);
169: } else if (
170: orderType == O.LimitShort
171: && s.shorts[asset][lastOrderId].nextId == Constants.TAIL
172: ) {
173: s.shorts.cancelOrder(asset, lastOrderId);
174: } else {
175: revert Errors.NotLastOrder();
176: }
177: }
178: }

Impact

The owners (users) of the canceled orders will lose their escrowed funds.

Tools Used

Manual Review

Recommendations

Consider refunding the users' escrowed funds when canceling orders via the cancelOrderFarFromOracle function.

Even if the order book was spammed by an attacker, refunding the attackers' escrowed funds is acceptable as the order book will be cleared again, and if the attacker attempts to repeat the attack, the attacker has to cover the extensive gas costs.

Updates

Lead Judging Commences

0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-436

Support

FAQs

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