---
# System prepended metadata

title: Cow Protocol Bug Report

---

# Bug Report: Settlement price check incorrectly ignores side-specific rounding for JIT/EIP-1271 orders

## Summary
- **Issue**: The settlement "limit price" check at line 368 of `GPv2Settlement.sol` treats prices without considering the side-specific rounding (ceil/floor) used later for fund transfers, causing valid JIT executions to revert with "GPv2: limit price not respected".
- **Impact**: Blocks valid JIT orders (including CoW AMM JIT liquidity and any third-party JIT) even when executed amounts and prices exactly match what the pool would trade.
- **Evidence**: The failing simulation would succeed if the L368 check were removed; the EIP-1271 order had already passed `isValidSignature`.

## Evidence
- **Contract reference**: See the limit-price check around L368–L400 in `GPv2Settlement.sol` ([link](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol#L368-L400)).
- **Local calculation showing correct rounding alignment**:
```python
def ceil_div(a: int, b: int) -> int:
    return -(a // -b)

# amounts at which cowamm pool would trade (see isValidSignature in tenderly sim)
sell_amount = 1643088602855085104587
buy_amount = 82539116533970260

# prices provided by the solver that yield the amounts above with ceil division
# used for sell orders
sell_token_price = 44400000000
buy_token_price = 883861337875367

# proof math works
assert ceil_div(sell_amount * sell_token_price, buy_token_price) == buy_amount

# replicate revert
assert sell_amount * sell_token_price >= buy_amount * buy_token_price, "GPv2: limit price not respected"
```
- **Simulation**: Revert occurs at the line 368 check; removing this check would let the settlement succeed. The EIP-1271 order was validated by `isValidSignature`. See Tenderly run: [shared simulation](https://dashboard.tenderly.co/shared/simulation/f87631c0-d23c-412f-b184-0cd7e7faf40d).

## Expected behavior
- The settlement price check should use the same rounding convention as the subsequent fund transfer computation:
  - **Sell-side constrained/JIT where buy leg is derived**: use ceil division on the buy leg.
  - **Buy-side constrained where sell leg is derived**: use floor division on the sell leg.
- A JIT order that exactly matches the pool's executable amount under those roundings should pass.

## Actual behavior
- The check at L368 compares unrounded products, which can reject a trade that is valid once the side-specific rounding is applied, resulting in a revert with "GPv2: limit price not respected."

## Root cause
- Mismatch between the high-level limit-price inequality and the side-specific rounding used to derive actual transfer amounts. The check does not replicate the ceil/floor distinction the contract later applies for fund transfers.

## Scope
- Affects CoW AMM JIT orders and any JIT order where execution amounts depend on rounding to meet pool constraints.

## Proposed fix (Solidity outline)
- Perform the limit price check on the same rounded quantities used for transfers.
- Example (pseudocode; adapt to existing types and helpers):
```solidity
if (isSellOrder) {
    // Compare using ceil on the buy leg (quote) as done for transfers.
    uint256 effectiveBuy =
        ceilDiv(executedSellAmount * sellTokenPrice, buyTokenPrice);
    require(
        effectiveBuy >= executedBuyAmount,
        "GPv2: limit price not respected"
    );
} else {
    // Compare using floor on the sell leg (base) as done for transfers.
    uint256 effectiveSell =
        (executedBuyAmount * buyTokenPrice) / sellTokenPrice; // floor
    require(
        executedSellAmount >= effectiveSell,
        "GPv2: limit price not respected"
    );
}
```
- Alternatively, compute the exact transfer amounts first (using the same rounding paths as the fund transfer logic) and assert the limit-price condition on those derived amounts.

## Notes
- The Tenderly trace confirms the EIP-1271 signature was accepted by `isValidSignature` before the revert, and removing L368's check allows a successful execution, reinforcing that the rounding mismatch is the blocker.
- The numeric example in `notes.md` matches the pool's executable amount via `ceil_div` and satisfies the inequality when rounding is aligned.

## References
- Contract lines: `GPv2Settlement.sol` L368–L400 ([link](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol#L368-L400))
- Tenderly simulation: [shared simulation](https://dashboard.tenderly.co/shared/simulation/f87631c0-d23c-412f-b184-0cd7e7faf40d)
