The code below represents the logic for matching sell orders. It searches for suitable buy orders in the order book and executes matching operations.
function sellMatchAlgo(
address asset,
STypes.Order memory incomingAsk,
MTypes.OrderHint[] memory orderHintArray,
uint256 minAskEth
) internal {
AppStorage storage s = appStorage();
uint16 startingId = s.bids[asset][Constants.HEAD].nextId;
STypes.Order storage highestBidInitial = s.bids[asset][startingId];
if (incomingAsk.price > highestBidInitial.price) {
if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
addSellOrder(incomingAsk, asset, orderHintArray);
}
return;
}
MTypes.Match memory matchTotal;
while (true) {
STypes.Order memory highestBid = s.bids[asset][startingId];
if (incomingAsk.price <= highestBid.price) {
if (incomingAsk.ercAmount.mul(highestBid.price) == 0) {
updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
incomingAsk.ercAmount = 0;
matchIncomingSell(asset, incomingAsk, matchTotal);
return;
}
matchHighestBid(incomingAsk, highestBid, asset, matchTotal);
if (incomingAsk.ercAmount > highestBid.ercAmount) {
incomingAsk.ercAmount -= highestBid.ercAmount;
highestBid.ercAmount = 0;
matchOrder(s.bids, asset, highestBid.id);
startingId = highestBid.nextId;
if (startingId == Constants.TAIL) {
incomingAsk.shortRecordId =
matchIncomingSell(asset, incomingAsk, matchTotal);
if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
addSellOrder(incomingAsk, asset, orderHintArray);
}
s.bids[asset][Constants.HEAD].nextId = Constants.TAIL;
return;
}
} else {
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;
}
} else {
updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
incomingAsk.shortRecordId =
matchIncomingSell(asset, incomingAsk, matchTotal);
if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
addSellOrder(incomingAsk, asset, orderHintArray);
}
return;
}
}
}
In the loop, there's no explicit exit condition defined, so it will loop indefinitely until some condition is met.if incomingAsk.ercAmount (the quantity of the sell order) is very large, and highestBid.ercAmount (the current quantity of the buy order) is very small, it can indeed lead to multiple iterations of the loop, ultimately consuming all available Gas and triggering an "out of gas" error.
The function may be unavailable.
Inside the loop, add a counter and set a reasonable maximum number of iterations. Forcefully exit the loop when the iteration count reaches the limit to prevent infinite looping.