Questions, answered in full
Every answer is open. Nothing here is behind a click, because the panels on the rest of the site link into this page and an answer you have to expand has not been given.
What this is
What is Glasshouse?
SwapVM ships two ways to allocate the right to fill an order. One asks who you are. The other asks what time it is. Glasshouse asks what you will pay — a sealed-bid, second-price auction, settled on chain.
It is one custom instruction: Opcode._2e, the free slot immediately after WhitelistSequential in SwapVM’s Conditions & access guards bank, gating the fill on a sealed-bid, second-price auction with a committed close. The winning bid is a price improvement in basis points applied to balanceIn, so ordinary SwapVM settlement delivers the surplus to the maker. No transfer, no custody, no fee router.
What am I actually bidding for?
Bidding is for the right to fill an order — to be the one who trades against it. You bid in basis points of improvement you are willing to give the maker, and the winner pays the runner-up’s bid, not their own.
How does a round work?
Three phases, disjoint in block space: commit, reveal, settle. The picture is the whole round at once; the steps under it are the same round from the bidder’s side.
What produced this: a drawing of the rule in src/book/GlasshouseBook.sol — the winner is the highest revealed bid and the price is max(second, reserve) — at the advocated window sizes in config/auction.json (30 / 30 / 15 blocks, seconds estimated at 2 s per block). The three bids are the same illustrative values the argument uses throughout. Paying the runner-up rather than your own bid is what makes bidding your true value safe: what you bid decides whether you win, not what you pay for it. It is not a record of an auction: no round on Base produced these numbers, and the board below shows the real ones.
- Connect a wallet on Base
A browser wallet — MetaMask, Rabby — on Base mainnet. You need a little ETH for gas. Reading the board needs no wallet at all; only bidding does.
- Seal a bid while the commit window is open
You send a hash of your number and a secret, never the number. Nobody — not other bidders, not the maker, not us — can read it. Your position in the queue is fixed the moment you commit, so bidding early cannot be punished and cannot be gamed.
- Open it while the reveal window is open
You send the number and the secret, and the contract checks they match the hash you committed. A bid that is never opened cannot win. This is the step people forget, and the one your record on this site remembers.
- If you win, fill inside your exclusive window
The highest revealed bid wins and pays the second-highest — or the reserve, if there was no second. For a short window only you may fill at that improved price. After it, anyone may fill at the base price instead.
The windows this Book is opened with are 30 blocks to commit, 30 to open and 15 exclusive — about a minute each at Base’s block time.
Why does the winner pay the runner-up’s price, not their own?
Because paying the runner-up rather than your own bid is what makes bidding your true value safe: what you bid decides whether you win, not what you pay for it. On the settlement chart the price line is drawn from the runner-up because that is where the number comes from — the winner’s own bid decided only that they won.
The price is clearingBps — max(reserveBps, secondBps) from GlasshouseBook.sol:229, not a figure computed by this site. The gap between what the winner bid and what the winner paid is printed on every receipt as what the winner kept, and it is the reason bidding true is safe.
Why are bids sealed?
Sealed rather than open, for shill resistance: an open second-price auction lets the maker insert a bid just under the top. A commitment is a hash of the bid, a salt and the order, so until the reveal window opens there is nothing for anyone — the maker included — to read and bid against.
Taking part
What do I need to bid?
A browser wallet — MetaMask, Rabby — on Base mainnet, and a little ETH for gas. That is all.
Reading this site needs no wallet at all; only bidding does. That is deliberate, and it is one of the better decisions in the product: a page arguing that you do not have to trust it should not ask you to sign something before it will show you why.
What happens if I seal a bid and never reveal it?
The bid is void. Nothing is taken from your wallet beyond gas — the bid simply does not count, and the round clears without it, which is exactly the cost of silence the bond exists to price.
This page cannot reveal for you. If nothing signs the reveal between the block after the commit window closes and the block the reveal window ends on, the bid is void. It is the step people forget, and the one your record on this site remembers: bids sealed against bids opened is a reliability record, and the contract cannot tell someone who left a bid sealed from someone who simply went away.
What is the bid secret and where is it kept?
Your bid is a hash of the number and a salt. The pair of them — the bps and the salt — is the bid secret, and it is stored in this browser only. Copy it if you might reveal from another one: a sealed bid can only be opened with its secret, by anyone, including us.
If you lost the browser but kept the numbers, the bid panel takes them back by hand. Nothing is validated there — the contract checks the pair at reveal, and a wrong one is rejected without costing the bid.
Who is the “house” bidder, and why is it in every round?
The demo maker bids in its own auctions, and it is labelled on the board. The keeper that keeps a live round on the page also places one bid per round, from the maker’s own address, so that a lone visitor sees a second price rather than the reserve — a second-price auction with one bidder clears at the reserve and demonstrates nothing.
It commits at index 0, before any visitor can have acted; it cannot read a sealed rival; it never reveals early; and its bond is 0. So it behaves as a randomised hidden reserve drawn from 60–200 bps, well under maxBps, and any real bidder can outbid it. It is marked house · the maker on its own bid card rather than explained away. A production maker would set reserveBps and not bid at all.
Why is there sometimes no round open?
A keeper opens the rounds. While it is running a new one arrives every couple of minutes and takes bids for its whole commit window — but nothing here can tell you whether it is running now, so this is what usually happens rather than a promise.
The keeper is a script, not a hosted service. When there is no round open the board says so, and the rounds table says which phase the most recent one reached.
Why are windows counted in blocks, not seconds?
Because the contract has no clock. It compares block.number against boundaries written when the round opened, so blocks are the enforced number.
Seconds are this page multiplying blocks by an assumed 2 s, and they are wrong whenever Base is not producing at exactly that rate. Printing the estimate in the same weight as the enforced number would be the page quietly promoting its own guess — so blocks lead and seconds follow, everywhere.
Is this a real market?
No. Live rounds run with bond 0 and dust-sized orders, and the maker bids in its own auctions so that a lone visitor sees a second price at all. This is a working mechanism, not a market.
What is real is the code path: the contracts are on Base mainnet, the auctions are real transactions, the clearing price is computed on chain, and every figure on this site can be re-derived from the Book’s own logs by someone who does not trust it. The figures on the evidence page are counts, never shares, for the same reason.
What is “Rehearsal”?
A replay, and every number in it is simulated. Nothing in rehearsal was read from Base or from any chain. A round on Base takes 75 blocks, about two and a half minutes, and the keeper only opens one while it is running — so the rehearsal replays the full lifecycle at 1 block / 0.4 s against the same components, using the contract’s own arithmetic over assigned bids.
It is produced by web/lib/simulate.js. The block numbers it shows count from a chosen origin and are not Base blocks, and bidding is disabled: a commitment signed against a synthetic order hash could never be revealed. Whenever it is on, a banner says so on every page.
Checking it
How do I check a round myself, without trusting this site?
Every figure on a receipt comes out of two calls. They need foundry and nothing else — no key, no account, no permission from us. Take the round’s maker and order hash off its row in the rounds table, and the block range off the round’s own page.
# the auction struct, exactly as the contract stores it cast call 0xc4ea91Fe700918220423ac307C6B1c59650FFbfe "auctions(address,bytes32)" <maker> <orderHash> --rpc-url https://mainnet.base.org # every commit and reveal on this round, in the order they landed cast logs --address 0xc4ea91Fe700918220423ac307C6B1c59650FFbfe --from-block <opened> --to-block <exclusiveEnd> --rpc-url https://mainnet.base.org
Every receipt on this site carries a Check this yourself panel with those two commands already filled in for that round, and a button to copy them.
What does “matches an independent replay” mean?
scripts/verify-run.mjs reads the Book’s logs, re-derives every auction outcome from the raw reveals without importing any of the three implementations of the clearing rule, and reports whether they agree. The settlement replay re-derives the contract’s own top-2 rule from the reveals and compares it against what settle() emitted — a check, never an echo.
What the evidence page shows is a snapshot of a run, not a live check, and everything about how it is presented keeps saying so: it prints the block it was taken at, the RPC it asked, and when. The failures are shown first and in full, because the verifier’s entire worth is that it reports what it could not confirm — a panel that showed only the passes would be advertising a tool while defeating it. The direct-chain reader cannot run the replay at all and reports null rather than false: not checked and checked and disagreed are very different claims.
What is the subgraph for, and what does it refuse to compute?
Two questions the chain reader cannot answer. One is order: everything else shows a round’s current state, and the timeline shows the sequence that produced it — which reveal took the lead from whom, and what the clearing price was immediately after each one. That ordering is the argument, not decoration: you can watch the number move to each new runner-up as the reveals land, and watch it not move when the winner’s own bid comes in. The other is history: whether an address has ever left a bid sealed needs every round the Book has ever had, not the recent window the board holds.
It re-derives nothing. The reveal event stores bestBpsAfter, secondBpsAfter and clearingBpsAfter — the state after that reveal was applied — specifically so a reader can replay a round without re-implementing the clearing rule.
What it refuses: not a win rate, not a share, not a rank, not a profit figure. Rounds won is a count and is shown as a count, and where a denominator exists it is printed beside the numerator rather than divided into it. Conformance to the generic indexing schema is not claimed either — it wants non-null dollar value-locked and revenue fields that an auction book does not have and that we would have to fabricate.
Why it is built this way
Why not a whitelist or a Dutch auction?
Because neither of them allocates by what the fill is actually worth. The figure below is the same three bidders under both rules — only the rule for choosing among them differs, and the axis each rule ignores is drawn rather than deleted.
What produced this: the three participants of test_Comparison_AllThreeGatesOnTheSameOrder in test/Comparison.t.sol, at their assigned valuations — Ada 400 bps, Bram 250, Cyd 100 — and their assigned arrival order, with Cyd first. Both panels plot the same three points at the same coordinates; only the rule for choosing among them differs. Arrival order is assigned in the test, not measured; on chain it is sold to whoever pays the builder most, which is the point. No axis here is a measured latency, and no point on it came from a live auction.
By identity — WhitelistSequential (0x2d). A hardcoded ladder of privileged takers, each with an exclusive window. An unlisted taker does not merely lose priority: it reverts until the entire cumulative ladder has elapsed. It is a cartel ladder written into the order.
By clock — DutchAuctionBalanceIn / Out (0x94 / 0x95). The price is a pure function of block.timestamp. Every transaction in a block shares one timestamp, so every bidder in that block faces an identical price and valuation cannot break the tie. Allocation is decided by intra-block ordering — by priority fee and builder placement — and the surplus above the posted price is competed away into priority fees paid to the builder, not returned to the maker. The descending clock does not price the order; it runs a latency auction whose proceeds leak out of the protocol.
A maker who does not want an auction leaves 0x2e out of the program, and nothing here applies to them — that is the point of it being an opcode rather than a protocol rule. The evidence page carries the measured difference: the same order, the same three participants, under all three gates, from one Foundry test.
Why not an AVS or restaking?
Because there is nothing here to secure. An off-chain auction committed by a signed operator quorum is an unverifiable assertion, and assertions need economic backing plus a challenge window — that design is correct for problems forced by latency. Loss-versus-rebalancing is per-block, and a commit-reveal auction cannot resolve inside one block, so those auctions must run off chain.
Taker priority on a resting order is not per-block. Glasshouse can afford 150 seconds, and for that price the winner is computed on chain from revealed bids: outcome() is a view function over state written permissionlessly by commit and reveal, with no owner, no admin and no upgrade path. Nobody makes a claim, so there is no claim to challenge. Adding an AVS would mean introducing a trusted party in order to then buy machinery to constrain it.
The general rule: verify on chain when you can afford the latency; secure economically off chain when you cannot. Where defection is possible we already use capital at risk — claimForfeit, claimUnrevealed, claimBond.
Where are the contracts, and who can change them?
GlasshouseBook at 0xc4ea…Fbfe and GlasshouseRouter at 0x5c3b…7DBf, both on Base mainnet (8453). The footer carries them on every page, with the 1inch Aqua the router points at.
GlasshouseBook has no owner, no upgrade path, no admin. Auction parameters are immutable after open(). The answer to who can change what outcome() returns? is nobody, which is what makes the quote/swap consistency argument airtight.
They are unaudited, and the known asymmetry is ours: bidders post bonds and the maker posts nothing, so a maker can open an auction against an order they never ship. The contract already refuses to mark a winner forfeited without positive evidence that someone else filled, precisely because it cannot distinguish a no-show from a misconfigured hook. A maker-side bond is the fix, and it means a new deployment — because the Book has no upgrade path by design.
Where is the long version?
The written argument is at /argument.html — the same case at essay length, with the source paths inline. Every decision and the reason for it is in DECISIONS.md, the design in docs/design/HLD.md, and everything else in the repository.