Placing a sealed bid
The bid dialog runs five steps backed by two contract calls. This page walks through each one, what it costs, and what failure looks like.
Before you bid
- Check the license terms. The dialog's top row shows "You'll receive: Story PIL license #N · flavor". This is what gets minted to your wallet if you win.
- Check the deadline. Hover the countdown for absolute time. There's no extension if you miss it.
- Check your WIP balance. Click the balance link below the amount field to autofill your max.
- Accept the sealed reserve. You cannot see the reserve floor when you bid — it is encrypted. You're choosing an amount without knowing the minimum the seller will accept. If your bid is below the revealed reserve at settle, it will not win.
Step 1 — Approve WIP
Your wallet approves the SealedAuction contract to spend up to your bid amount in WIP. This is one wallet popup. If you've already approved a higher amount in a previous bid, your wallet may skip this step automatically.
Cost: One ERC-20 approval. Standard gas.
What to watch for: Wallets sometimes mis-display the spender. Confirm the contract address matches the deployed SealedAuction address.
Step 2 — Reserve a vault slot (allocateBidSlot)
The contract pulls your deposit and calls into CDR to allocate a fresh
ciphertext uuid for your bid. The slot binds you to this auction and this uuid,
and registers the bid's CDR vault on the AuctionRevealCondition contract.
Cost: One contract call (allocateBidSlot). Includes the deposit transfer.
What to watch for: If the deposit transfer fails (insufficient WIP, prior
approval too small), the call reverts with
DepositTransferFailed. The dialog
shows the humanized error; click "Show details" for the raw revert reason.
The deposit you escrow here is the cap on your bid amount: the contract
enforces bid.amount ≤ deposit at settle. If you want to hide your maximum
intent, escrow more than your actual bid. The contract refunds the difference.
Step 3 — Sign sealed bid
Your wallet signs the bid payload off-chain. No transaction, just a signature popup. The payload is:
keccak256(abi.encode(auctionId, bidder, amount, nonce))
prefixed with "\x19Ethereum Signed Message:\n32" so browser wallets'
personal_sign output matches what the contract recovers at settle.
The full plaintext (bidder, amount, nonce, signature) forms a 149-byte
BidPayload. It gets encrypted in the next step.
Cost: Free (signature, not a transaction).
What to watch for: Some wallets show the hash but not the message. That's fine — the signature is over the hash.
Step 4 — Encrypt under TDH2
The SealedIP encryption API runs TDH2 threshold encryption on your signed payload under the active CDR network public key. This is server-side, not a wallet action. The WASM CDR crypto library does not run in browser bundles, which is why encryption is handled by the API route.
Cost: Free (off-chain).
What to watch for: Network errors during this step are recoverable — your slot is still allocated and you can retry the encryption + submit. The dialog handles this transparently.
Step 5 — Submit ciphertext (submitEncryptedBid)
The encrypted payload is submitted to the contract, which stores it against your uuid. After this lands, your bid is sealed on-chain.
Cost: One contract call. Minimal gas (storing bytes).
What to watch for: The contract enforces that:
- The caller matches the slot allocator (
NotBidderrevert if not) - No ciphertext already exists for this uuid (
CiphertextAlreadyWrittenif so) - The deadline hasn't passed (
BiddingClosedif so)
If any of those revert, your deposit is still escrowed. The slot remains allocated. You can't re-submit different ciphertext, but you can let the slot age out — when the auction settles, slots with no ciphertext are refunded automatically.
Total cost
| Step | Wallet popups | Gas |
|---|---|---|
| Approve WIP | 1 | low |
Reserve slot (allocateBidSlot) | 1 | medium (includes transfer) |
| Sign bid | 1 | none (signature) |
| Encrypt | 0 | none (off-chain) |
Submit ciphertext (submitEncryptedBid) | 1 | medium (stores bytes) |
| Total | 3 transactions + 1 signature | depends on gas price |
After the bid is sealed
You'll see a success screen with transaction hash links and the ciphertext uuid that identifies your bid.
Nothing else is required from you. The bid stays sealed until the deadline. When the auction settles:
- If you win, the license token mints to your wallet and the contract refunds your overpayment.
- If you lose, your full deposit is refunded.
- If no valid bid clears the revealed reserve, every deposit is refunded including yours.
You can close the tab.
You don't know the reserve when you bid
This is intentional. The reserve is sealed in its own CDR vault, separate from all bid vaults. It is revealed at settle alongside the bids. Bidding blind on both sides — you don't see others' bids, and you don't see the seller's floor — is the core of two-sided sealing.
Practical implication: bid what you believe the license is worth to you. There's no information advantage from waiting or watching.
Multiple bids on the same auction
The contract allows you to allocate multiple slots in the same auction. Each slot gets its own uuid and its own deposit. This is useful if you want to hedge: bid one amount with one deposit and a different amount with a different deposit.
The contract picks the highest single valid bid across all your slots at settle. It does not sum bids from the same wallet.
What you can change after submission
Nothing. The bid is sealed and on-chain. There is no cancel function, no edit function. If you want a different bid, allocate another slot with a new deposit — the contract treats each slot independently at settle.
Only the highest valid bid from any single wallet can win. Lower-amount bids from the same wallet are treated as losers and refunded normally.