Reproduced Exploit
Rubicon — Some offers can't be cancelled
1. SimpleMarket.offer(pay, payGem, buy, buyGem, owner, recipient) creates an offer without inserting it into the sorted _rank list or the unsorted _near list. 2. RubiconMarket.cancel overrides the base cancel and requires the offer to be sorted or successfully _hided from _near. 3. For a SimpleMark…
Chain
Other
Category
untagged
Date
Apr 2023
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 48949-h-10-some-offers-cant-be-cancelled-code4rena-rubicon-rubicon. Standalone Foundry PoC and full write-up: 48949-h-10-some-offers-cant-be-cancelled-code4rena-rubicon-rubicon_exp in the
evm-hack-registrymirror.
Vulnerability classes: wrong-condition · locked-funds · cross-contract-state-consistency
Reproduction: self-contained Foundry PoC with only
forge-std— no fork, no RPC. Full trace: output.txt. PoC: test/48949-h-10-some-offers-cant-be-cancelled-code4rena-rubicon-rubicon_exp.sol.
Key info#
| Impact | HIGH — maker's pay_gem locked forever; cancel reverts with "can't hide" |
| Protocol | Rubicon — RubiconMarket / SimpleMarket |
| Vulnerable code | SimpleMarket.offer (6-arg) never inserts into _rank/_near; RubiconMarket.cancel requires sorted or hidden |
| Bug class | Wrong condition / cross-contract state consistency — cancel assumes book insertion that SimpleMarket.offer never performs |
| Finding | Code4rena 2023-04-rubicon · #48949 · H-10 · reporter carrotsmuggler |
| Report | code4rena.com/reports/2023-04-rubicon |
| Source | AuditVault |
| Status | Confirmed by sponsor. Reproduced as a standalone local synthetic. |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
SimpleMarket.offer(pay, payGem, buy, buyGem, owner, recipient)creates an offer without inserting it into the sorted_ranklist or the unsorted_nearlist.RubiconMarket.canceloverrides the base cancel and requires the offer to be sorted or successfully_hided from_near.- For a SimpleMarket offer,
isOfferSortedis false and_hidereturns false →require(_hide(id), "can't hide")reverts. - Maker cannot cancel;
pay_gemstays locked in the market.
The vulnerable code#
function cancel(uint256 id) external returns (bool success) {
require(offers[id].owner == msg.sender, "not owner");
if (isOfferSorted(id)) {
// unsort path…
} else {
// @> VULN: SimpleMarket offers are neither sorted nor on the unsorted list
require(_hide(id), "can't hide");
}
// refund pay_gem…
}
Fix: override the 6-arg offer in RubiconMarket to route through the sorted offer(..., pos, rounding, owner, recipient) path that inserts into _rank.
Root cause#
Cancel was upgraded in RubiconMarket to maintain the order book, but the SimpleMarket 6-arg offer API was left un-overridden. Offers created through that API never enter _rank or _near, so the cancel bookkeeping invariants always fail.
Preconditions#
- User places an offer via the SimpleMarket 6-arg
offer(used in protocol tests / legacy API). - User later calls
cancelon that offer id.
Attack walkthrough#
- Maker approves and calls
market.offer(90e18, pay, 100e18, buy, maker, maker). - Offer id is created;
paytokens sit in the market; no_rank/_nearentry. - Maker calls
cancel(id)→"can't hide"→ tokens remain locked.
Diagrams#
Impact#
Makers who use the SimpleMarket offer API permanently lock their pay_gem. Confirmed by Rubicon (daoio).
Taxonomy#
genome: wrong-condition,locked-funds,cross-contract-state-consistencyseverity/high·sector/token·platform/code4rena
Sources#
- AuditVault finding #48949
- Code4rena report 2023-04-rubicon
- Repo@commit: code-423n4/2023-04-rubicon ·
contracts/RubiconMarket.sol(offer L511, cancel L871)
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 48949-h-10-some-offers-cant-be-cancelled-code4rena-rubicon-rubicon_exp (evm-hack-registry mirror).
- AuditVault finding: 48949-h-10-some-offers-cant-be-cancelled-code4rena-rubicon-rubicon.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.