Mammoth Mocha Koala
Medium
When an order is added to pending, both global and local positions are immediately updated with the order's amounts.
During settlement, if the oracle version is invalid, the order is invalidated (amounts set to zero), and the pending is subtracted.
However, the positions (global and local) are not reverted to their pre-order state. This results in positions being permanently increased by invalid orders, breaking the invariant that positions should only reflect valid, settled orders
function _update(
Context memory context,
UpdateContext memory updateContext,
Order memory newOrder,
Guarantee memory newGuarantee,
address orderReferrer,
address guaranteeReferrer
) private notSettleOnly(context) {
// advance to next id if applicable
if (context.currentTimestamp > updateContext.orderLocal.timestamp) {
updateContext.orderLocal.next(context.currentTimestamp);
updateContext.guaranteeLocal.next();
updateContext.liquidator = address(0);
updateContext.orderReferrer = address(0);
updateContext.guaranteeReferrer = address(0);
context.local.currentId++;
}
if (context.currentTimestamp > updateContext.orderGlobal.timestamp) {
updateContext.orderGlobal.next(context.currentTimestamp);
updateContext.guaranteeGlobal.next();
context.global.currentId++;
}
// update current position
updateContext.currentPositionGlobal.update(newOrder);
updateContext.currentPositionLocal.update(newOrder);
// apply new order
updateContext.orderLocal.add(newOrder);
updateContext.orderGlobal.add(newOrder);
context.pendingGlobal.add(newOrder);
context.pendingLocal.add(newOrder);
updateContext.guaranteeGlobal.add(newGuarantee);
updateContext.guaranteeLocal.add(newGuarantee);
// update collateral
context.local.update(newOrder.collateral);
// protect account
if (newOrder.protected()) updateContext.liquidator = msg.sender;
// apply referrer
_processReferrer(updateContext, newOrder, newGuarantee, orderReferrer, guaranteeReferrer);
// request version, only request new price on non-empty market order
if (!newOrder.isEmpty() && newGuarantee.isEmpty()) oracle.request(IMarket(this), context.account);
// after
InvariantLib.validate(context, updateContext, newOrder, newGuarantee);
// store
_storeUpdateContext(context, updateContext);
// fund
if (newOrder.collateral.sign() == 1) token.pull(msg.sender, UFixed18Lib.from(newOrder.collateral.abs()));
if (newOrder.collateral.sign() == -1) token.push(msg.sender, UFixed18Lib.from(newOrder.collateral.abs()));
// events
}
When a new order is submitted (via _update), both the global (context.latestPositionGlobal) and local (context.latestPositionLocal) positions are immediately updated with the order’s amounts (e.g., maker, long, short).
Example: A user opens a long position of +100. The global/long position increases by 100 instantly.
The order is added to pendingGlobal/pendingLocal to await settlement.
During settlement (_processOrderGlobal/_processOrderLocal), the code checks if the oracle version associated with the order is valid:
if (!oracleVersion.valid) newOrder.invalidate(newGuarantee); If invalid, the order is "invalidated" (its amounts are zeroed out).
The invalidated order (now zeroed) is subtracted from the pending aggregate:
context.pendingGlobal.sub(newOrder); // Subtracts zero Position Not Reverted:
The global/local positions (context.latestPositionGlobal/context.latestPositionLocal) remain updated with the original (now invalid) order amounts.
The positions are never rolled back to their pre-order state.
No response
No response
A user submits an order increasing their position by X.
The pending and current positions are updated to include X.
Settlement finds the oracle version invalid, invalidates the order (sets amounts to zero), subtracts the pending, but doesn't revert the position.
The user's position remains increased by X, despite the order being invalid, leading to incorrect accounting.
This allows invalid orders to permanently alter positions, leading to incorrect collateral requirements, exposure calculations, and potential financial exploitation.
No response
When invalidating an order during settlement, revert the position changes made when the order was initially added. This can be done by subtracting the original order amounts from the current positions during invalidation.
Only update positions after an order is validated during settlement, not when it’s initially placed.