Skip to content

PayPal sandbox only. No real money moves.

LearnHow it works

Auction states and soft close

The states a lot moves through, who can hammer it, and how the soft-close clock extends the end.

Verifiedbacked by a real run or committed evidence

Page last changed 7 Nov 2026 (from the git history)

An auction in GAVEL is one lot at a time, and a lot has a clock. This page shows the states a lot can be in, which event moves it from one to the next, and exactly how the clock can be extended when someone bids at the last moment.

Verified against the code, not measured

States and rules come from the engine and the room host. The clock numbers in the simulator are the demo lot of the server, not a general setting. Nothing here is a timing measurement; for latency see Latency.

Lot states, hammer and soft close

Read from code
Lot states, hammer and soft closeTop: the lot state machine. PENDING moves to OPEN on LOT_OPENED, OPEN to CLOSING on LOT_CLOSING, CLOSING to SETTLING on LOT_HAMMERED, and SETTLING to DONE on LOT_SETTLED or LOT_UNSOLD. A hammer with no bids goes from CLOSING straight to DONE. LOT_ABORTED from PENDING, OPEN or CLOSING ends in DONE. Bottom: a time axis for the demo lot, a ten minute lot with a 30 second soft-close window, a 30 second extension, at most five extensions and a hard close at 750 seconds, with the end marker moved by each simulated bid.LOT_ABORTED: kill switch, from PENDING, OPEN or CLOSINGPENDINGOPENCLOSINGSETTLINGDONELOT_OPENEDLOT_CLOSINGLOT_HAMMEREDLOT_SETTLEDor LOT_UNSOLDBID_RECORDED, END_EXTENDEDhammer with no bids: LOT_UNSOLDmoney: settlement sagaWire status in a room snapshot: OPEN, CLOSING, SETTLING or DONE, plus a paused flag. The enum also lists SCHEDULED and PAUSED;the server never sends them as a status, the web client derives PAUSED from paused: true.Soft close clock, demo lot (seconds after the lot opens)window 30 s, extension 30 s, at most 5 extensions, hard close 750 swindow0100200300400500600700planned end 600hard close 750ends at 600Numbers: packages/server/src/boot/compose.ts demoAuctionConfig; hard close = duration + min(5 x 30 s, 1 h).With these numbers the limit of five extensions stops the clock before the hard close.

Scroll the figure sideways, or open it full size.

Hover, focus or tap a state for what it means and where it lives.

Pick how many seconds before the end a bid arrives, then place it.

Source: packages/engine/src/evolve.ts (which entry sets which status), engine/src/rules/softclose.ts (the extension rule, copied into the simulator above), engine/src/decide/hammer.ts and packages/server/src/runtime/ledger-engine.ts (hard close margin). The clock numbers are the demo lot in packages/server/src/boot/compose.ts; the engine default is soft close off. This is a map of rules, not a measurement.
Text version of this diagram

Lot states and the entry that moves between them:

  • PENDING to OPEN: LOT_OPENED.
  • OPEN to CLOSING: LOT_CLOSING, written at the deadline by a tick, or by an early hammer.
  • CLOSING to SETTLING: LOT_HAMMERED.
  • SETTLING to DONE: LOT_SETTLED, or LOT_UNSOLD when no candidate is left.
  • CLOSING to DONE: a hammer with no bids writes LOT_UNSOLD.
  • PENDING, OPEN or CLOSING to DONE: LOT_ABORTED, the kill switch before the hammer. A SETTLING lot cannot be aborted.

Soft close for the demo lot: the lot lasts 600 seconds, the window is 30 seconds, an extension sets the end to the bid time plus 30 seconds (never earlier than the old end), at most 5 extensions happen, and the hard close is at 750 seconds. A bid 30 seconds before the end changes nothing, a bid 1 second before the end moves it by 29 seconds.

The five states

StateEntered byWhat is allowed
PENDINGLOT_ADDEDNothing yet. Lots open one at a time, in lot order.
OPENLOT_OPENED (OpenLot)Bids. Pause, resume and extend by the auctioneer.
CLOSINGLOT_CLOSINGNo bids. The hammer.
SETTLINGLOT_HAMMEREDNothing on the auction side: the lot is frozen and the settlement saga runs.
DONELOT_SETTLED, LOT_UNSOLD or LOT_ABORTEDNothing.

Two things that look like states are not:

  • Paused is a flag on an OPEN lot. LOT_PAUSED records how much time was left, LOT_RESUMED restores it. While paused, a bid is answered auction_paused (retryable) and the clock does not tick.
  • SCHEDULED and PAUSED appear in the shared status list, but the server never sends them as the status of a room. The web client shows PAUSED when the snapshot says paused: true on an OPEN room.

How a lot ends

The deadline passes. The room arms a timer for endTs. When it fires, the room ticks the engine, which writes LOT_CLOSING if the deadline has passed. A bid that arrives after the deadline but before the timer is also rejected, and writes LOT_CLOSING first.

The lot is hammered. From CLOSING (or from OPEN, if the auctioneer closes early), the hammer ranks the bids, takes the top one as the winner, prices it and lists the fallback candidates. The result is LOT_HAMMERED. With no bids at all it is LOT_UNSOLD and the lot goes straight to DONE.

Money starts. After the hammer the room asks the payments side to settle, once per process, after saving the request. Registration for the room closes at the hammer.

Who hammers

The auctioneer console posts a hammer control to the HTTP control route (it is not a WebSocket frame). The control carries a controlId, so a repeat returns the same result and appends nothing. It may also carry the price and the leader paddle the auctioneer saw; if either has moved, the hammer is refused with conflict (price_changed or leader_changed) and nothing is hammered. Hammering an OPEN lot needs the explicit close-now argument; a paused lot cannot be hammered.

The soft-close clock

Soft close stops a lot from ending on a last-second bid by giving rivals time to answer. The rule is small and exact (extension() in the engine):

  1. It applies to an accepted bid only. A rejected bid never moves the clock.
  2. If the bid time is earlier than endsAt - windowMs, nothing happens.
  3. Otherwise the new end is max(endsAt, bidTime + extensionMs), never later than the hard close.
  4. It stops once maxExtensions extensions have been used.

So with a 30 second window and a 30 second extension, a bid 1 second before the end moves the end by 29 seconds, and a bid exactly 30 seconds before the end changes nothing.

Prop

Type

The server derives the hard close for a room as the lot duration plus the smaller of maxExtensions x extensionMs and one hour. For the demo lot (a ten minute lot, 30 second window, 30 second extension, at most five extensions) that is 750 seconds after opening. With those numbers the five-extension limit stops the clock before the hard close.

Edit on GitHub

Last updated on