Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

sealedbid package

Overview

Realm sealedbid is a SEALED-BID SECOND-PRICE (Vickrey) auction settled in ugnot, holding real coins. A SELLER lists a lot; BIDDERS commit to a price they never disclose, by publishing only sha256(amount, salt) and escrowing a deposit; after the commit window closes they OPEN their bids by revealing (amount, salt); the highest revealed bid wins and the winner pays the SECOND highest price, their deposit refunded down to it.

It is a different clearing rule and a different threat model from this portfolio's other market realm. r/auction is ascending English open-outcry: every bid is public the instant it lands, bidders react to each other, and the winner pays their own bid. Here no bidder learns another's number until the commit window has shut, so there is nothing to react to and nothing to front-run; and because the winner pays the runner-up's price, bidding one's true valuation is the dominant strategy rather than a mistake. The two realms answer the same question — who gets the lot, at what price — by mechanisms that do not resemble each other.

COMMIT-REVEAL is the mechanism this portfolio did not have. Nothing in it hashed anything, on any of the three trees, before this realm.

Custody — the conservation invariant is the product

A custodial realm, so the monetary invariant governs it as it does r/loans, r/auction and r/deposits:

Example
1Held == sealed + revealed + feeledger.Liabilities()

where `sealed` is the sum of deposits behind commitments that have NOT been opened in a non-terminal sale, `revealed` the sum of deposits behind commitments that HAVE been opened and are awaiting close, and the ledger holds every claimable kind — losing-bid refunds, the winner's change, seller proceeds — plus the fee pot. The two held buckets are disjoint by construction: opening a bid moves its deposit from the first to the second, never duplicating it, and Close drains both into the ledger. Every value transition preserves the equality; see the DELTA-form conservation test.

CORRECTION, recorded rather than shipped silently: the §7 discovery gate for this realm predicted THREE disjoint held buckets, naming `forfeited` as the third. Architecture refuted that. A forfeited deposit is credited to the seller at Close in the same transaction that voids the commitment, so it is never held as a distinct balance across any two transactions — there is no third bucket to be disjoint from. `forfeited` survives as a CUMULATIVE STATISTIC (ForfeitedOf, ForfeitedTotal), which is a report and not a custody claim. The gate's file is not edited; this is the amendment.

Payouts are PULL, never push. Close credits balances and sends nothing, so a hostile or contract-dead loser cannot make Close fail for everyone else, and the settlement path has no cross-call reentrancy surface.

Non-revelation, and why withholding never pays

A sealed-bid auction has a liveness problem an ascending one does not: a bidder who dislikes the outcome can simply never open their bid, stalling settlement or distorting the price. The answer here is adopted by citation rather than invented — r/moul/agents/bubblerumble6 reached it independently, and its policy is the right one: a commitment not opened in its window is VOID, nothing is refunded, and the deposit is credited to the seller. So withholding a reveal costs the withholder their whole deposit and pays them nothing, and the auction always closes: Close is permissionless and needs no bidder to act.

Two consequences of that policy are hazards, stated rather than discovered:

  • A SHORT REVEAL WINDOW IS A SELLER'S INCENTIVE TO SET. The seller keeps every unopened deposit, so a seller who picks the MinRevealBlocks floor profits from bidders who cannot act in time. The window is public before anyone commits and is reported by [SaleInfo] and Phase; a bidder who does not like it should not commit. The realm does not second-guess a disclosed term.
  • A COMMITMENT ABOVE ONE'S DEPOSIT CAN NEVER BE OPENED. Reveal requires `amount <= deposit`, so a bidder who commits to more than they escrowed has no valid opening and forfeits. That is the deposit-as-ceiling rule, and it is what makes the escrow sufficient: commit to no more than you put up.

Privacy — what is hidden, and what is not

The BID AMOUNT is hidden until the bidder opens it. The DEPOSIT is not: it is a coin transfer and is public, and since a valid bid cannot exceed its deposit, the deposit leaks an UPPER BOUND on the bid. A bidder who wants more privacy than that over-deposits and takes the change back at Close. This is the real ceiling of the scheme and is stated here because no amount of hashing moves it.

The salt is what keeps the commitment from being brute-forced over the plausible round numbers, so MinSaltLen bytes are required. The realm can check a salt's LENGTH and can never check its ENTROPY: a 16-byte salt of "aaaaaaaaaaaaaaaa" satisfies the guard and protects nothing. Use random bytes. Compute the commitment locally, or with a READ-ONLY query against CommitHash — never in a transaction, which would publish the salt and defeat the whole mechanism.

What the realm settles, and what it does NOT

The realm settles PAYMENT and records the WINNER; it does not custody or deliver the lot, which is free text and changes hands off-chain (or by a downstream realm reading [Winner]). The same scope decision r/auction states, for the same reason: an on-chain-asset auction is a v2 and this v1 omits the asset-escrow surface deliberately.

REALM-CALLER CAVEAT, inherited from the siblings: coinio's receipt guard admits only EOA payers, so every BIDDER is an EOA. The SELLER is whoever calls CreateSale; a realm may sell, but a realm seller must expose its own crossing path to Claim or its proceeds strand — the seller's concern, not a third-party hazard. assertNoSend reads the ORIGIN envelope, so every non-payable function refuses a transaction that attached coins anywhere.

Why crypto/sha256 directly

The discovery gate found genuine prior art for the hashing scheme — p/moul/agents/commit/v0, whose Hash is LENGTH-PREFIXED so that Hash("ab","c") differs from Hash("a","bc") — and deferred the import-or- reimplement choice to architecture. It is resolved here in favour of calling crypto/sha256 directly and FOLLOWING that package's scheme, because the package resolves to a DIFFERENT IMPORT PATH on each chain this portfolio targets, and the namespace rewriter substitutes only our own address. An import of it would make the mainnet twin of this realm non-substitutable, which is a structural cost that 1.4 KB of saved code does not buy.

Function

DepositOf

func DepositOf(saleID int64, bidder address) int64
Ask AI Claude ChatGPT

DepositOf returns the bidder's escrowed deposit on a sale (0 if none).

Params

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/sealedbid.DepositOf(,)"

Result