# RubiconMarket._buys will not work for V1 offers due to the reversion in cancel method.

RubiconMarket._buys reverts for V1 offers because cancel() tries to refund dust to address(0), blocking the buy flow.

- Payout: Contest reward
- Severity: high
- Program: Rubicon
- Credited: zhuXKET
- Disclosed: 2025-02-17
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-04-rubicon-findings/issues/1324
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-rubicon%3AH-01
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:legacy-endpoint technique:source-review vuln:business-logic vuln:dos

**Target:** RubiconMarket / SimpleMarket smart contracts
**Root cause:** SimpleMarket.cancel refunds pay tokens to _offer.owner, which is address(0) for V1 offers, causing ERC20 transfer revert; _buys calls cancel for dust offers and thus reverts.
**Steps:**
1. Create a V1 offer (owner = address(0), recipient set) with pay_amt below _dust threshold.
2. Call RubiconMarket._buys on that offer; after partial fill, dustId is set and cancel(id) is invoked.
3. cancel passes can_cancel but SimpleMarket.cancel attempts pay_gem.transfer(address(0), pay_amt), which reverts.
4. The revert propagates, blocking _buys.
**How found:** Code reading of _buys dust-cancel logic and SimpleMarket.cancel refund branch; noticed owner==address(0) for V1 offers leads to transfer to null address.
**Exploitation:** Not weaponized; a functional bug that blocks buying V1 offers when dust remains, causing denial of service for that path.
**Insight:** When auditing order-book contracts with legacy offer versions, check refund/cleanup paths for assumptions about owner vs recipient; dust-handling branches often call cancel and can revert on legacy structs.
