# Priority queue min accounting breaks when nodes are split in two

Tessera's min-heap priority queue corrupts ordering when a lowest bid is split in place, so wrong bids get popped and some users cannot withdraw funds.

- Payout: Contest reward
- Severity: medium
- Program: Tessera
- Credited: IllIllI
- Disclosed: 2023-04-18
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-12-tessera-findings/issues/32
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-tessera%3AM-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** GroupBuy bid priority queue (MinPriorityQueue.sol / GroupBuy.sol)
**Root cause:** processBidsInQueue mutates lowestBid.quantity in place without removing and re-inserting the node, so the binary heap's ordering invariant (price then quantity) is violated and the node stays at the root.
**Steps:**
1. Build heap where root A:(p:100,q:10) has child B:(p:100,q:10) and B has children D,E.
2. Call processBidsInQueue with quantity 5 so A.quantity is decremented to 5 in place (`lowestBid.quantity -= quantity`).
3. B now has larger quantity than A and should be root, but tree is not reshuffled.
4. Insert F:(p:100,q:6); swim-up compares F against A and pops A as min, though B should be min.
5. Nodes under B become unreachable/invisible; their owners cannot withdraw and are wrongly excluded from the buy.
**How found:** Compared README's stated tie-break rule (higher quantity first) against isGreater() implementation and noticed the heap invariant requires re-sorting on node mutation; traced processBidsInQueue's in-place quantity decrement as the violating write.
**Exploitation:** Not an external attack; triggered by normal bid processing when a large lowest bid is partially consumed, causing incorrect min selection and fund-withdrawal/queue-exclusion errors for affected bidders.
**Insight:** When auditing heap/priority-queue structures, check every mutation path: any in-place field change to a key used by the comparator (here quantity) must be remove-then-reinsert, otherwise ordering silently corrupts and nodes become unreachable.
