DittoETH

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

`verifySellId` can return the wrong ID, which can lead to mess in the IDs

Summary

addOrder makes multiple function calls to find the position of the order that must be added, however when it reaches verifySellId it can accidentally add the order in the wrong place.

Vulnerability Details

When addOrder is called it tries to find the prev and next ID and insert the new order in the middle. However the call to findPrevOfIncomingId can actually return the next order and not the previous which result in the orders being 2 places further than they should be (since it is prev => current => next).

When findPrevOfIncomingId is called it will try and find the direction, with the help of verifyId

int256 direction = verifyId(
orders, asset, hintId, incomingOrder.price, nextId, incomingOrder.orderType
);

However this call to verifyId can actually return NEXT as the direction while it should return EXACT. This happens because verifyId calls verifySellId to check the direction, and verifySellId has a wrong check, more perilously an inconsistency in the checks.

function verifySellId(...) private view returns (int256 direction) {
bool check1 = orders[asset][_prevId].price <= _newPrice || _prevId == Constants.HEAD;
bool check2 = _newPrice < orders[asset][_nextId].price || _nextId == Constants.TAIL;
if (check1 && check2) { return Constants.EXACT; }
else if (!check1) { return Constants.PREV; }
else if (!check2) { return Constants.NEXT; }
}

As we can see from the above code snippet check1 checks for if orders[asset][_prevId].price is <= _newPrice while check2 checks for if _newPrice is < orders[asset][_nextId].price.

This discrepancy in the operators can cause an issue if the orders are 1 => 2 => 3 where the prices of 2 and 3 are equal. As check1 will be true, while check2 false, which will trigger else if (!check2) { return Constants.NEXT; } and return as NEXT while it should have returned EXACT => if (check1 && check2) { return Constants.EXACT; } .

With the return of NEXT from verifyId findPrevOfIncomingId will trigger this else if returning the next ID, which will be saved as prevId in addOrder, from there on it will get the next next ID and save it as nextId, and the mess will continue.

uint16 prevId = findPrevOfIncomingId(orders, asset, incomingOrder, hintId);// can return also next and exact?
uint16 nextId = orders[asset][prevId].nextId;

This all happens if the orders have 2 orders with the exact prices, which may be often occurring.

Impact

Complete mess in the orders, may lead to further complications

Tools Used

Manual review

Recommendations

Remove the discrepancy and change check 2. I would also suggest to do the same in verifyBidId since the checks there are the same and the issue could also occur there. However since it is the same I don't feel it is necessary to do a separate explanation for it.

function verifySellId(...) private view returns (int256 direction) {
bool check1 = orders[asset][_prevId].price <= _newPrice || _prevId == Constants.HEAD;
- bool check2 = _newPrice < orders[asset][_nextId].price || _nextId == Constants.TAIL;
+ bool check2 = _newPrice <= orders[asset][_nextId].price || _nextId == Constants.TAIL;
if (check1 && check2) { return Constants.EXACT; }
else if (!check1) { return Constants.PREV; }
else if (!check2) { return Constants.NEXT; }
}
Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
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.