Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Exchange state machine · Boson Protocol
Skip to content

Exchange state machine

Every commitment to an offer becomes an exchange with a state. The state machine is small, but the transitions are the protocol's contract surface.

State table

StateWho can transition outToActionFunds movement
COMMITTEDBuyerREDEEMEDredeemVoucher(exchangeId)none (yet)
COMMITTEDBuyerCANCELLEDcancelVoucher(exchangeId)buyer loses buyerCancelPenalty, gets rest back
COMMITTEDSellerREVOKEDrevokeVoucher(exchangeId)seller loses sellerDeposit to buyer
COMMITTEDAnyoneEXPIREDexpireVoucher(exchangeId)buyer loses buyerCancelPenalty, gets rest back
REDEEMEDAnyoneCOMPLETEDcompleteExchange(exchangeId) after dispute windowseller treasury receives price
REDEEMEDBuyerDISPUTEDraiseDispute(exchangeId) within dispute windowescrow held pending resolution

What "anyone" means

completeExchange and the auto-EXPIRED transitions can be triggered by any wallet. Sellers typically batch-call them via complete_exchange to release earnings. See Build → Sellers → Manage funds.

Windows

Three time windows govern the state machine. All set on the offer at creation:

  • Validity window (validFromDatevalidUntilDate) — when buyers can commit.
  • Voucher window (voucherRedeemable{From,Until,ValidDuration}) — when the buyer can redeem.
  • Dispute window — fixed disputeDuration after redemption.

A buyer who doesn't redeem before the voucher window closes loses the price; the seller still loses sellerDeposit. This protects the buyer against silent seller absence.

Where to look in code

SurfaceWhere
ContractsExchangeCommitFacet, ExchangeHandlerFacet, DisputeHandlerFacet
Core SDKExchanges mixin, Disputes mixin
MCPcommit_to_offer, redeem_voucher, cancel_voucher, revoke_voucher, complete_exchange, raise_dispute — see Reference → MCP tools

Common footguns

  • Don't read the new state until the subgraph indexes it. See Eventing & indexing.
  • The EXPIRED transition is implicit. No event fires when a voucher silently expires; you must check voucherRedeemableUntilDate against block.timestamp. The subgraph synthesizes the state for you.
  • cancelVoucher is buyer-initiated; revokeVoucher is seller-initiated. Different functions; different penalties.

Next