Dispute state machine
A dispute is the side state machine that runs when an exchange is contested. Once raised, the parent exchange sits in DISPUTED until the dispute terminates.
State table
| State | Who transitions out | To | Action |
|---|---|---|---|
RESOLVING | Buyer | RETRACTED | retractDispute(exchangeId) — buyer gives up |
RESOLVING | Both, mutual EIP-712 | RESOLVED | resolveDispute(exchangeId, buyerPercent, sigB, sigS) |
RESOLVING | Buyer (after window) | ESCALATED | escalateDispute(exchangeId) |
RESOLVING | Anyone | EXPIRED | expireDispute(exchangeId) |
ESCALATED | DR | DECIDED | decideDispute(exchangeId, buyerPercent) |
ESCALATED | DR | REFUSED | refuseEscalatedDispute(exchangeId) |
ESCALATED | Buyer | RETRACTED | retractDispute(exchangeId) — buyer gives up |
ESCALATED | Both, mutual EIP-712 | RESOLVED | resolveDispute(exchangeId, buyerPercent, sigB, sigS) |
ESCALATED | Anyone | REFUSED | expireEscalatedDispute(exchangeId) |
Funds movement on terminal state
| Terminal | Buyer gets | Seller gets | DR gets |
|---|---|---|---|
RETRACTED | nothing (gives up) | price + their deposit back | nothing |
RESOLVED | buyerPercent of escrow | rest | nothing |
DECIDED | buyerPercent of escrow | rest | their fee |
REFUSED | full escrow (price + escalation fee) | their deposit back | nothing |
EXPIRED (from RESOLVING) | nothing | price + their deposit | nothing |
Where to look in code
| Surface | Where |
|---|---|
| Contracts | DisputeHandlerFacet |
| Core SDK | Disputes mixin |
| MCP | raise_dispute, retract_dispute, resolve_dispute, escalate_dispute, decide_dispute, refuse_escalated_dispute, expire_dispute(_batch), expire_escalated_dispute, extend_dispute_timeout, create_dispute_resolution_proposal |
Common footguns
- Mutual resolution requires signatures from both buyer and seller. EIP-712 typed-data. See Signing & meta-transactions.
- DR fees come out of the escrow on
DECIDED, but are refunded to the buyer onREFUSED. Plan UX accordingly. - The dispute machine doesn't reset the exchange. Once a dispute terminates, the parent exchange goes to
COMPLETED(or stays in a terminal state mirroring the dispute outcome) — read both states when reconstructing history.