# Truncation in OrderValidator can lead to resetting the fill and selling more tokens

uint120 truncation in Seaport OrderValidator resets a partial order's fill fraction to 0/0, letting an attacker refill and drain all approved ERC1155 tokens at the same price.

- Payout: Contest reward
- Severity: high
- Program: OpenSea
- Credited: Spearbit
- Disclosed: 2022-08-30
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-05-opensea-seaport-findings/issues/77
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AH-01
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** OrderValidator.sol partial-fill fraction accounting (ERC1155 partial orders)
**Root cause:** Filled numerator/denominator are stored as uint120 without overflow checks; cross-multiplying large fractions (e.g. 2**118/2**119 + 1/10) exceeds uint120 and truncates both to 0, resetting the fill state.
**Steps:**
1. Alice approves 100 ERC1155 tokens and places a PARTIAL_OPEN order for 10 tokens with ETH consideration.
2. Attacker fills 50% using fraction `2**118 / 2**119`, setting totalFilled=2**118, totalSize=2**119.
3. Attacker fills 10% using `1 / 10`; cross-multiply yields numerator=(2**118*10+2**119), denominator=2**119*10.
4. uint120 truncation in OrderValidator.sol#L228-L248 stores numerator=0, denominator=0, resetting fill.
5. Attacker continues filling the order repeatedly, draining all 1000 approved ERC1155 tokens at the same consideration.
6. Fix: add `require(filledNumerator + numerator <= type(uint120).max)` and `require(denominator <= type(uint120).max)` before storing.
**How found:** Code review of the partial-fill fraction arithmetic in OrderValidator; noticed uint120 casts on numerator/denominator without bounds checks and reasoned that cross-multiplying large valid fractions overflows and truncates to zero.
**Exploitation:** Attacker submits fills with oversized-but-valid fractions that overflow uint120 on cross-multiplication, zeroing the stored fill fraction, then repeatedly refills the same partial order to extract all approved tokens at the original price.
**Escalation:** Repeated refills drain the full approved balance; in a restricted token sale this lets the public buy more than the intended allocation, altering token distribution.
**Insight:** When a fraction is stored in a packed integer type, test whether cross-multiplication of two individually-valid fractions can exceed the storage width and truncate to zero — resetting accumulated state rather than reverting.
