Skip to main content

Sealed reserve

Most auction protocols treat the reserve price as public: the seller announces it before bidding opens, and everyone knows the floor. SealedIP does something different. The seller's reserve is encrypted into a CDR vault alongside the bids, and it is revealed and verified only at settlement — in the same transaction that decrypts the bids.

This makes SealedIP two-sided sealed: bidders don't know the reserve, and the seller doesn't know the bids. Neither side sees the other's number until settlement.

Why seal the reserve?

A publicly-known reserve creates strategic distortions:

  • Bidders cluster at the floor. Knowing the minimum, rational bidders shade their bids toward the reserve rather than their true valuation. Auctions with a visible floor often receive bids that hover just above it regardless of actual demand.

  • The seller leaks information. Publishing a reserve reveals the seller's bottom line. A low reserve signals willingness to accept a low price. A high reserve can deter bidders from participating at all.

  • Coordination risk increases. When bidders know the floor, they can coordinate implicitly around it — all staying low, knowing the others will too.

A sealed reserve removes all of this. Bidders must bid based on their own valuation of the IP. The seller commits to a floor without revealing it. The result is a true two-sided private-information game.

How the seller seals a reserve

The reserve uses the exact same cryptographic format as a bid. There is no separate mechanism.

The payload format is the 149-byte BidPayload:

| address signer (20 bytes) | uint256 amount (32) | bytes32 nonce (32) | signature (65) |

For a bid, signer is the bidder. For the reserve, signer is the seller. Both use the same digest and signing scheme, so the same BidPayload.recoverSigner Solidity function verifies both.

Constraints on submitEncryptedReserve:

  • Caller must be the auction's seller (NotSeller reverts otherwise).
  • Auction must be in Open state before the deadline (BiddingClosed reverts otherwise).
  • One write per auction (ReserveAlreadyWritten reverts on a second attempt).

Floor default: 0

If the seller does not call submitEncryptedReserve, the auction record has reserveHasCiphertext = false. At settlement, the contract detects this and sets the floor to 0. Every bid becomes a valid winner candidate; the highest bid wins regardless of amount.

This means listing without sealing a reserve is valid. It's effectively a no-reserve auction in which the winner is whoever bids the most.

Reveal and verification at settle

The reserve is never stored in plaintext on-chain. It is only ever accessible as a ciphertext in CDR, reconstructed under the same two-gate condition as the bids.

At settlement, the orchestrator passes a ReserveReveal struct alongside the bid reveals:

struct ReserveReveal {
uint256 amount;
bytes32 nonce;
bytes signature; // 65 bytes: r || s || v
}

The contract checks reserveHasCiphertext. If true, it calls BidPayload.recoverSigner on the ReserveReveal. The recovered address must equal auction.seller. If it does not, the contract reverts with InvalidReserveReveal and the entire settlement fails. No bid is evaluated, no license mints, no transfers occur.

If the signature is valid, reserveReveal.amount becomes the floor. Bids below the floor are dropped and refunded. The highest bid at or above the floor wins.

What the reserve does and doesn't reveal to the orchestrator

The orchestrator sees the reserve plaintext when it reads the CDR vault to prepare the settle() call. This is a necessary property of the protocol: someone must submit the reveal to the contract.

What the orchestrator cannot do:

  • Forge a different reserve (the seller's signature would not match).
  • Submit a false reserve that reverts settlement (the signature check would fail with InvalidReserveReveal).
  • Use the reserve to select a different winner (the contract's on-chain logic picks the winner; the orchestrator only supplies data).

The trustlessness guarantee is that the orchestrator can only censor (refuse to call settle()). It cannot manipulate the outcome.

How validators treat the reserve vault

The reserve vault (reserveUuid) is registered on AuctionRevealCondition in the same call that creates the auction. It is bound to the same two-gate condition as the bid vaults:

  1. block.timestamp >= deadline
  2. SealedAuction.isTriggered(auctionId) == true

CDR validators process the reserve vault at the same time as the bid vaults. All vaults open at once. Neither the reserve nor any bid can be decrypted before both gates pass.

The information asymmetry that remains

Even with a sealed reserve, two pieces of public information give bidders some signal:

  • That a reserve was sealed: reserveHasCiphertext is a public boolean. Bidders know whether the seller committed to a floor, but not what it is.
  • Deposit amounts: each bidder's deposit is visible on-chain. A bidder who deposits exactly their true valuation exposes an upper bound.

These are inherent properties of the protocol, documented in Known limitations.

Summary

BidReserve
Who seals itBidderSeller
WhenBefore deadline, Open stateBefore deadline, Open state
CDR vault allocatedAt allocateBidSlotAt createAuction
Ciphertext writtensubmitEncryptedBidsubmitEncryptedReserve
Signer in payloadBidder addressSeller address
Default if not sealedSlot skipped, deposit refundedFloor = 0
Verified at settleBidPayload.recoverSigner == bidderBidPayload.recoverSigner == seller
On-chain error if invalidBid dropped silentlyInvalidReserveReveal (settle reverts)

See Settlement flow for the full sequenced breakdown, or Lifecycle for the step-by-step walkthrough.