Skip to main content

Auction lifecycle

A SealedIP auction passes through one or more of five states:

StatePillMeaning
OpenOpenAccepting sealed bids; seller may also seal reserve
Open past deadlineClosingDeadline passed; awaiting trigger()
TriggeredSettlingValidators decrypting bids and reserve
SettledSettledWinner has license, seller paid
ExpiredNoWinnerEndedNo bid cleared the revealed reserve; all deposits refunded
ExpiredEmptyEndedNo bids placed; nothing to do

Open past deadline is not a separate on-chain state. It is still Open, but with block.timestamp >= deadline. The UI and SDK use the deadline to distinguish "still bidding" from "waiting to settle".

State transitions

Each transition is gated by exactly one function on the SealedAuction contract. Calling a function from the wrong state reverts with WrongState or WrongStateForSettle.

Step 1 — Seller lists

createAuction takes three arguments: ipId, licenseTermsId, and deadline. There is no plaintext reserve price argument. The contract allocates the seller-side CDR vault (reserveUuid) and registers it on AuctionRevealCondition. No deposit is escrowed yet. Listing costs only gas.

Reverts:

Step 2 — Seller seals reserve

The seller signs a BidPayload (the same 149-byte format used for bids, but with signer = seller) and encrypts it under the CDR threshold key. The ciphertext is written to the reserve vault. Seller-only, Open state, before deadline, one write per auction.

If the seller never calls submitEncryptedReserve, the reserve defaults to 0 at settle: every bid is a valid winner candidate.

Reverts:

Step 3 — Bidder allocates slot

allocateBidSlot pulls the WIP deposit into escrow and allocates a fresh CDR vault for this bid, also bound to the AuctionRevealCondition. The deposit is an upper bound on the bid amount; the actual bid is hidden in the ciphertext.

Reverts:

Step 4 — Bidder seals bid

The bidder signs and encrypts the bid payload, then writes the ciphertext to their CDR vault. Bidder-only (per uuid), Open state, before deadline, one write per slot.

A bidder can allocate multiple slots in the same auction to submit multiple independent sealed bids. Each gets its own uuid and its own deposit.

Reverts:

Step 5 — Trigger

trigger() is permissionless. Anyone can pay gas to call it once the deadline has passed. The contract checks the deadline, flips state to Triggered, and emits AuctionTriggered. This second gate (alongside the time gate) is what opens the AuctionRevealCondition for both bid vaults and the reserve vault.

If bidCount == 0, state goes directly to ExpiredEmpty and no validator work happens.

Reverts:

Step 6 — Validators reveal

CDR validators detect the AuctionTriggered event off-chain. The AuctionRevealCondition now returns true for every vault in this auction (both bid vaults and the reserve vault), so validators begin submitting partial decryption shares. Once a quorum (t-of-n) arrives per uuid, the orchestrator can reconstruct the plaintext payload for each vault.

Validators only see their own shares. The complete plaintext is never persisted in CDR.

Step 7 — Settle

settle() accepts the array of bid reveals plus the reserve reveal (ReserveReveal {amount, nonce, signature}). The contract verifies the reserve signature (signer must recover to the seller address) before evaluating any bid. If no reserve was sealed, the floor is 0. Every step is atomic. See Settlement flow for the full per-step breakdown.

Out-of-order or stalled auctions

If trigger() is never called, the auction sits in Open state. Deposits remain escrowed; bidding is closed past the deadline.

If validators do not publish shares, the auction sits in Triggered state. The orchestrator can retry settle() once shares arrive. If shares never arrive, the auction is effectively stuck. This is the central availability assumption: see Orchestrator availability.

If settle() reverts, state stays Triggered. The orchestrator identifies the bad input, removes it, and retries.