Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 76 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,32 @@

**Private bids, guaranteed onchain delivery.**

CipherBid is an open-source Vickrey auction house for ERC-721 assets on Starknet. Every bidder escrows the same public STRK collateral cap through STRK20 while committing to a private bid amount. After the bidding window closes, bidders reveal their commitments, the highest valid bidder wins, and the NFT is delivered atomically at the greater of the reserve or second-highest valid bid.
CipherBid is a Vickrey NFT auction on Starknet where every accepted bidder locks the same STRK collateral cap through STRK20. The actual bid stays sealed until reveal. Settlement sends the NFT to the winner at the greater of the reserve or second-highest valid bid, and every refund, surplus, and seller payment returns through private STRK20 claims.

> **Verified mainnet demo:** auction [`1788040057342`](https://sourcesenseitherealone.github.io/cipherbid/auction/?id=1788040057342) completed the two-wallet private-bid lifecycle, atomic settlement, and both bidder claims. Bidder B entered `4 STRK`, not the frozen plan's `3 STRK`; this README and the public evidence preserve the actual chain value.
[Open the live mainnet auction](https://sourcesenseitherealone.github.io/cipherbid/auction/?id=1788040057342) · [Read the transaction ledger](docs/evidence/mainnet/transactions.md) · [Use the presentation script](docs/demo-presentation-script.md)

## Why equal collateral?
## The 30-second version

Many auction demos hide a bid with a hash but do not prove the bidder can pay. Escrowing each bidder's exact amount fixes funding but leaks the bid through the public token transfer.

CipherBid locks the same public `4 STRK` cap for both bidders. Observers see funded bids with equal collateral, but not whether the sealed bid is `2 STRK` or `4 STRK`. After reveal, the contract calculates the Vickrey price, transfers the escrowed NFT in the same settlement transaction, and accounts for every remaining STRK claim.

Atomic settlement means all-or-nothing delivery. Winner selection, second-price accounting, and the NFT transfer succeed together or the transaction reverts. There is no accepted state where CipherBid records a winner but leaves the NFT with the seller.

> **Verified mainnet result:** auction [`1788040057342`](https://sourcesenseitherealone.github.io/cipherbid/auction/?id=1788040057342) completed two private equal-cap bids, two reveals, second-price settlement, atomic NFT delivery, bidder claims, and the final seller claim. Five published CipherBid transactions touched the canonical STRK20 pool.

## Why this is more than a minimal commit/reveal demo

| Minimal commit/reveal demo | CipherBid |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| A hash can be submitted without funded collateral | Every accepted bid moves the same real STRK cap through the live STRK20 pool |
| Exact escrow can leak the bid before reveal | Equal collateral hides which value at or below the cap was committed |
| Delivery can remain a separate manual step | The NFT enters custody at creation and moves to the winner inside settlement |
| Refunds often use public transfers | Loser refund, winner surplus, and seller proceeds use STRK20 open-note claims |
| Recovery is left outside the demo | Password-encrypted credentials bind to network, contract, auction, role, and claim handle |
| Success is usually shown with local tests | Mainnet receipts, CipherBid events, pool traces, NFT ownership, and zero residual accounting |

## Why equal collateral matters

A STRK20 `privacy_invoke` withdraws tokens from the privacy pool to the helper through a public ERC-20 edge. Escrowing each bidder's variable bid would reveal that amount before the reveal phase. CipherBid therefore locks the same cap for every accepted bidder. The public transfer proves every bid is funded without disclosing whether the sealed bid is `2 STRK`, `4 STRK`, or another value at or below the cap.

Expand All @@ -35,43 +56,65 @@ The production frontend is live at [`https://sourcesenseitherealone.github.io/ci

The exportable live-auction route is `/auction?id=<positive-u64>`. It validates one auction ID, reads public Starknet state in the browser, verifies the deployed class/configuration and NFT custody, then renders wallet controls. Ready X still owns private-note discovery, proving, signing, and submission.

The [record-ready presentation script](docs/demo-presentation-script.md) follows this exact page and keeps the explanation under three minutes. `strk20.json.demo_video` stays empty until the updated recording is published and independently checked.

## Verified mainnet demo

The bounded mainnet demo uses one seller, two separate Ready X accounts, and one read-only observer:

| Term | Value |
| ----------------------- | ---------: |
| Reserve | `1 STRK` |
| Equal collateral cap | `4 STRK` |
| Bidder A sealed bid | `2 STRK` |
| Bidder B sealed bid | `4 STRK` |
| Verified winner | Bidder B |
| Verified clearing price | `2 STRK` |
| Loser refund | `4 STRK` |
| Winner surplus | `2 STRK` |
| Seller proceeds | `2 STRK` |
| Bidding window | 10 minutes |
| Reveal window | 5 minutes |
| Term | Value |
| ----------------------- | ----------------: |
| Reserve | `1 STRK` |
| Equal collateral cap | `4 STRK` |
| Bidder A sealed bid | `2 STRK` |
| Bidder B sealed bid | `4 STRK` |
| Verified winner | Bidder B |
| Verified clearing price | `2 STRK` |
| Loser refund | `4 STRK` |
| Winner surplus | `2 STRK` |
| Seller proceeds | `2 STRK`, claimed |
| Final house balance | `0 STRK` |
| Bidding window | 10 minutes |
| Reveal window | 5 minutes |

Both bidders shielded `24 STRK` and passed the ten-block maturity gate before the timed auction started. Public readiness verified registration, deposit amount, and maturity only. Ready X remained authoritative for unspent private-note balance.

## Architecture

```text
Seller / public Starknet account
├─ approves DemoERC721 token 99
└─ creates auction atomically ─────────────┐
Ready X bidder wallet CipherBid AuctionHouse
├─ owns viewing key and notes ├─ escrows the NFT
├─ discovers mature STRK notes ├─ accepts equal 4 STRK collateral
├─ creates proof ├─ stores Poseidon commitments
└─ submits Wallet API action ───────►├─ verifies reveals
├─ computes Vickrey clearing price
STRK20 pool ├─ transfers NFT atomically to winner
├─ screens public deposits └─ authorizes refunds/surplus/proceeds
├─ verifies private proof
└─ invokes AuctionHouse
```mermaid
flowchart LR
UI["CipherBid web app<br/>public reads and action descriptors"]
Wallet["Ready X<br/>keys, notes, proving, signing"]
RPC["Starknet RPC<br/>state and receipt readback"]
Pool["STRK20 pool<br/>private ingress and claims"]
House["AuctionHouse<br/>NFT custody and Vickrey accounting"]
NFT["ERC-721<br/>token 99"]
Recovery["Encrypted recovery bundle<br/>held by the user"]

UI -->|read public state| RPC
RPC --> House
UI -->|Wallet API request| Wallet
Wallet -->|private action| Pool
Pool -->|privacy_invoke| House
Wallet -->|standard lifecycle call| House
House -->|custody and settlement| NFT
UI -.->|encrypt and export| Recovery
Recovery -.->|import for reveal or claim| UI
```

CipherBid never receives the wallet's viewing key, private notes, proof witness, or signer key. Ready X owns those operations. The web app constructs bounded public descriptors, keeps active auction credentials in memory, encrypts recovery exports, and verifies every submitted transition through public RPC readback.

## Mainnet user flow

```mermaid
flowchart TD
A["Seller escrows NFT and creates auction"] --> B["Bidder A and Bidder B each lock the same 4 STRK cap"]
B --> C["Bids remain sealed until the reveal window"]
C --> D["Bidder A reveals 2 STRK; Bidder B reveals 4 STRK"]
D --> E["AuctionHouse selects Bidder B and clears at 2 STRK"]
E --> F["Settlement transfers token 99 to Bidder B"]
F --> G["Loser refund, winner surplus, and seller proceeds return through STRK20"]
G --> H["Final AuctionHouse STRK balance: 0"]
```

### Commitment binding
Expand Down Expand Up @@ -205,14 +248,15 @@ Do not add `--execute` until the printed plan, signer, network, public bidder re
- [Mainnet deployment](docs/evidence/mainnet/deployment.md)
- [Verified mainnet transaction ledger](docs/evidence/mainnet/transactions.md)
- [Verified mainnet auction lifecycle](docs/evidence/mainnet/auction-lifecycle.md)
- [Live demo presentation script](docs/demo-presentation-script.md)
- [Mainnet release candidate](docs/evidence/mainnet/release-candidate.md)
- [Canonical demo matrix](docs/evidence/task-0-demo-matrix.md)
- [Lifecycle specification](docs/evidence/task-2-3-lifecycle-specification.md)
- [Security invariants](docs/evidence/task-2-4-security-invariants.md)
- [Hackathon requirements matrix](docs/evidence/hackathon-requirements-matrix.md)
- [Sepolia rehearsal](docs/evidence/sepolia/demo-runbook.md)

`strk20.json` contains the two verified contracts, four successful pool-touching CipherBid lifecycle transactions, and the clean-browser-verified live auction URL. `demo_video` remains intentionally empty until the maximum-three-minute video is publicly playable and independently checked.
`strk20.json` contains two verified contracts, five successful pool-touching CipherBid transactions, and the clean-browser-verified auction URL. The video field remains empty until the updated recording is public and checked.

## Scope

Expand Down
120 changes: 120 additions & 0 deletions docs/demo-presentation-script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# CipherBid live demo presentation script

**Target length:** about 2 minutes 45 seconds at a natural pace

**Live page:** https://sourcesenseitherealone.github.io/cipherbid/auction/?id=1788040057342

## Before recording

1. Open the live auction in a clean browser window.
2. Set browser zoom to 100%.
3. Keep the page at the top before recording.
4. Do not open recovery files, wallet activity, private balances, or developer tools.
5. Scroll slowly enough that transaction links and values can be read.

## Talk track

### 0:00-0:18 - What CipherBid is

**On screen:** Keep the auction title, `MAINNET`, `SOLD`, and `2/2 bids` visible.

**Say:**

> CipherBid is a private-bid Vickrey auction for NFTs on Starknet. It solves two trust problems at once. A hidden bid is useless if the winner cannot pay, and a winning bid is unsafe if NFT delivery still depends on the seller acting later.

### 0:18-0:42 - The equal-collateral idea

**On screen:** Scroll to the reserve, collateral cap, and deadline cards.

**Say:**

> CipherBid makes every accepted bidder lock the same public collateral cap through STRK20. In this auction both bidders locked 4 STRK, so observers could see that both bids were funded without learning whether either bidder had committed 2 STRK or 4 STRK.

> The actual bid stayed sealed until the reveal window. This is the main difference from an unfunded hash commitment or variable public escrow.

### 0:42-1:08 - The verified auction result

**On screen:** Show the accepted bids and revealed amounts.

**Say:**

> This is the completed mainnet auction, not sample data. Bidder A revealed 2 STRK. Bidder B revealed 4 STRK.

> CipherBid uses a Vickrey rule: the highest valid bidder wins, but pays the greater of the reserve or the second-highest bid. Bidder B therefore won and paid 2 STRK.

### 1:08-1:36 - The atomic part

**On screen:** Show token `99`, the current owner, and the Atomic Delivery Receipt.

**Say:**

> The seller placed NFT token 99 into AuctionHouse custody when the auction was created. This removes the need to trust the seller after bidding starts.

> Settlement is atomic, which means all or nothing. The contract selects the winner, records the two-STRK clearing price, and transfers the NFT to Bidder B in one transaction. If the NFT transfer fails, the entire settlement reverts. CipherBid cannot record a successful winner while leaving the NFT with the seller.

> The page then reads the NFT owner back from Starknet before it says delivery is verified. It does not trust browser state or a wallet success message.

### 1:36-2:04 - Private claims and conservation

**On screen:** Show clearing price, seller proceeds, and verified transaction receipts.

**Say:**

> The remaining value returned through STRK20 open-note claims. Bidder A received the 4 STRK loser refund. Bidder B received the 2 STRK winner surplus. The seller claimed the 2 STRK clearing price.

> The seller claim is the fifth qualifying CipherBid transaction that touches the live STRK20 pool. After the claim, the AuctionHouse has zero actual STRK, zero accounted STRK, and no unexplained difference.

### 2:04-2:28 - What stays private

**On screen:** Keep the receipt and wallet boundary text visible.

**Say:**

> Ready X owns the viewing keys, private notes, proof generation, signing, and submission. CipherBid never asks for a viewing key or private-note witness. Recovery credentials are password-encrypted and held by the user.

> The privacy claim is precise: bid amounts are sealed until reveal. Deposits, timing, equal collateral, and the final revealed bids are public by design.

### 2:28-2:40 - Close with the differentiator

**On screen:** Return to the auction title and `SOLD` status.

**Say:**

> CipherBid gives us funded privacy before reveal, all-or-nothing NFT delivery at settlement, private claims afterward, and public receipts anyone can verify. That is what makes it more than a basic commit/reveal auction.

## Short answers for judge questions

### Why not use a normal commit/reveal auction?

A minimal commit/reveal design can accept an unfunded hash. If it escrows each bidder's exact amount, the public transfer can reveal the bid. CipherBid locks one equal cap for every accepted bidder.

### Why use a Vickrey price?

The winner pays the second-highest valid bid or the reserve, whichever is greater. In the verified auction, Bidder B revealed 4 STRK but paid 2 STRK.

### What does atomic delivery mean?

The AuctionHouse already holds the NFT. Winner selection, clearing-price accounting, and NFT transfer happen inside one settlement transaction. If delivery fails, settlement reverts instead of leaving a recorded winner without the asset.

### What does STRK20 do here?

STRK20 provides the private pool path for equal-cap bid ingress and the loser, winner, and seller claims. Ready X handles private-note discovery and proving.

### Is everything anonymous?

No. Deposits, timing, equal collateral, reveals, settlement, and open-note edges are public. CipherBid specifically protects the bid amount before reveal and keeps wallet private material out of the app.

### How do we know delivery and accounting are correct?

The public page reads the AuctionHouse state, transaction receipts, ERC-721 owner, token balance, and claim-consumed flags from Starknet. The final AuctionHouse actual and accounted STRK balances are both zero.

### Is this production audited?

No. It is a bounded, tested mainnet hackathon deployment. The repository documents its threat model and limitations instead of presenting it as a production audit.

## Public proof links

- [Live auction](https://sourcesenseitherealone.github.io/cipherbid/auction/?id=1788040057342)
- [Verified transaction ledger](evidence/mainnet/transactions.md)
- [Verified lifecycle and value conservation](evidence/mainnet/auction-lifecycle.md)
- [Seller claim transaction](https://starkscan.co/tx/0x24d92390b2f0ca629fe49e4c4355aaa2fe1fbf143bd4ba1e37b80e4575528e)
Loading