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 source realm

Realm sealedbid is a SEALED-BID SECOND-PRICE (Vickrey) auction settled in ugnot, holding real coins. A SELLER lists a...

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.

Constants 5

const SaleOpen, SaleSettled, SaleUnsold, SaleCancelled

1const (
2	SaleOpen      = "open"      // in its commit, reveal, or awaiting-close phase
3	SaleSettled   = "settled"   // closed WITH a winner; proceeds credited
4	SaleUnsold    = "unsold"    // closed with no valid opening, or below reserve
5	SaleCancelled = "cancelled" // seller cancelled before any commitment
6)
source

Sale status values. Only Open is non-terminal; the phase inside Open is derived from the chain height, never stored.

const PhaseCommit, PhaseReveal, PhaseCloseable

1const (
2	PhaseCommit    = "commit"    // accepting commitments
3	PhaseReveal    = "reveal"    // accepting openings
4	PhaseCloseable = "closeable" // reveal window shut; Close will succeed
5)
source

Phase values returned by Phase for a sale that is still Open.

const MinBid, MaxBid, MinCommitBlocks, MinRevealBlocks, MaxWindowBlocks, CommitmentLen, MinSaltLen, MaxSaltLen, MaxLotLen, MaxBiddersPerSale, MaxSalesPerSeller, RenderLimit

 1const (
 2	// MinBid is the dust floor for any revealed bid, any deposit and any
 3	// non-zero reserve; MaxBid keeps every bid and the fee split far from
 4	// int64 overflow. The bidder must ESCROW at least their bid, so the real
 5	// ceiling is their balance.
 6	MinBid = int64(1_000)
 7	MaxBid = int64(1_000_000_000_000) // 1,000,000 GNOT
 8
 9	// Window bounds, in blocks. The reveal floor is higher than the commit
10	// floor on purpose: failing to commit costs nothing, while failing to
11	// reveal costs the whole deposit, so the window in which that can happen
12	// gets the wider floor. See the short-reveal-window hazard above.
13	MinCommitBlocks = int64(10)
14	MinRevealBlocks = int64(20)
15	MaxWindowBlocks = int64(7_500_000)
16
17	// CommitmentLen is the length of a commitment string: sha256 as 64
18	// lowercase hex characters, exactly as [CommitHash] emits it.
19	CommitmentLen = 2 * sha256.Size
20
21	// MinSaltLen is the required salt length. It bounds brute force over
22	// plausible bid amounts; it cannot bound a caller's choice of entropy.
23	MinSaltLen = 16
24	MaxSaltLen = 128
25
26	// MaxLotLen bounds the stored lot description.
27	MaxLotLen = 200
28
29	// MaxBiddersPerSale bounds the commitments on one sale. It is the reason
30	// the duplicate-commitment scan and the Close payout loop may be linear:
31	// both are O(MaxBiddersPerSale) and neither is caller-extensible.
32	//
33	// It is ALSO a scarce resource, which is why a sale carries minDeposit.
34	// A slot is never released — there is no withdraw-commitment path — so
35	// without a seller-sized entry price, 25 addresses committing MinBid
36	// each fill a sale for 25,000ugnot, reveal MinBid, and are refunded in
37	// full fee-free: an honest bidder is locked out for the cost of gas.
38	// Audit Y1, and the reason minDeposit exists. See [CreateSale].
39	MaxBiddersPerSale = 25
40
41	// MaxSalesPerSeller bounds one seller's stored sales; terminal sales do
42	// NOT free the slot, because a settled/unsold/cancelled sale is a
43	// permanent record a bidder and an auditor may need to read.
44	MaxSalesPerSeller = 50
45
46	// RenderLimit bounds every rendered list — an unbounded Render was YELLOW
47	// in three prior audits.
48	RenderLimit = 20
49)
source

Input bounds.

const Denom

1const Denom = "ugnot"
source

Denom is the only asset this realm accepts.

const MaxFeeBps

1const MaxFeeBps = int64(1000)
source

MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%. The fee applies only to the seller's PROCEEDS at close — never to a refund, never to the winner's change, and never to a forfeited deposit (see Close).

Functions 44

func BidsOn

Action
1func BidsOn(saleID int64) int64
source

BidsOn is how many commitments a sale holds; RevealedOn how many are opened.

func BlocksLeft

Action
1func BlocksLeft(saleID int64) int64
source

BlocksLeft is how many blocks remain in the sale's current window (0 once the reveal window has shut or the sale is terminal).

func Cancel

crossing Action
1func Cancel(cur realm, saleID int64)
source

Cancel withdraws a sale that has taken no commitment. Seller only. Once any commitment stands the sale can only run to Close — a seller cannot pull the lot out from under an escrowed bidder, and cannot strand a deposit.

func Claim

crossing Action
1func Claim(cur realm, amount int64)
source

Claim sends amount ugnot of the caller's claimable balance — refunds, winner's change, and seller proceeds — to the caller.

func ClaimAll

crossing Action
1func ClaimAll(cur realm)
source

ClaimAll sends the caller's entire claimable balance to the caller.

func Close

crossing Action
1func Close(cur realm, saleID int64)
source

Close settles a sale whose reveal window has shut. Anyone may call it; no sale's terminal state may depend on a party acting.

The clearing rule: among the OPENED bids the highest wins and pays max(second-highest, reserve, MinBid) — a strict Vickrey price, floored so that a lone bidder does not win for nothing and so that a reserve is honoured as a price and not merely as a veto. Ties on the highest amount go to the EARLIEST opening, which makes the tie-break deterministic and independent of storage order, and a tie sets the second price equal to the first, so a tied winner pays their full bid.

The money, all of it credited and none of it sent:

  • winner: change of `deposit - price`, fee-free (it is their own coin coming back, not a sale)
  • seller: `price`, at the sale's snapshotted fee
  • every other opened bid: its deposit back in full, fee-free
  • every UNOPENED commitment: deposit credited to the SELLER, fee-free. Fee-free is deliberate: a fee is a cut of a SALE, and taking a cut of liquidated damages would give the fee recipient a revenue interest in bidders failing to reveal.

If no bid was opened, or the highest opened bid is below the reserve, the sale is Unsold: every opened bid is refunded in full and only the unopened commitments forfeit.

func Commit

crossing Action
1func Commit(cur realm, saleID int64, commitment string)
source

Commit escrows a deposit behind a sealed bid. The caller attaches the deposit and supplies only `commitment` — CommitHash(amount, salt) as 64 lowercase hex characters — so the realm learns the bid's CEILING (the deposit) and not the bid. The deposit is the ceiling on what can later be opened, so escrow at least what you intend to bid; escrow more to hide it better. One commitment per bidder per sale, and a commitment already present on this sale is refused (copying another bidder's hash without knowing its contents would otherwise be free and could never be opened).

func CommitHash

Action
1func CommitHash(amount int64, salt string) string
source

CommitHash returns the commitment for a bid of `amount` under `salt`: the sha256 of a LENGTH-PREFIXED encoding of the two parts, as 64 lowercase hex characters. The length prefixes are what make the encoding unambiguous — without them a commitment to (12, "34salt...") and one to (1, "234salt...") could collide, and a bidder could open a commitment as a bid they never made. The scheme follows p/moul/agents/commit/v0; see the package doc for why it is reimplemented rather than imported.

This is a VIEW. Compute your commitment locally or with a read-only query — calling it inside a transaction publishes the salt and destroys the seal.

func CommitmentOf

Action
1func CommitmentOf(saleID int64, bidder address) string
source

CommitmentOf returns the bidder's stored commitment on a sale ("" if none).

func CreateSale

crossing Action
1func CreateSale(cur realm, lot string, reserve, minDeposit, commitBlocks, revealBlocks int64) int64
source

CreateSale lists a lot for sealed-bid second-price sale. The caller becomes the seller. `reserve` is 0 (no reserve) or a floor in [MinBid, MaxBid] that the HIGHEST revealed bid must meet for the sale to settle. Commitments are accepted for `commitBlocks` blocks, then openings for `revealBlocks` blocks, after which anyone may Close. The current protocol fee is snapshotted in. Returns the id.

`minDeposit` is the ENTRY PRICE per commitment, and it is the seller's one defence against slot squatting: a sale has at most MaxBiddersPerSale slots, a slot is never released, and a squatter who reveals a dust bid is refunded in full fee-free — so with no entry price a rival locks an honest bidder out of any sale for the cost of gas. Size it to the lot, not to the dust floor. 0 means "no preference", resolved to the max(MinBid, reserve) floor, which is the weakest setting and leaves the squatting cost at MaxBiddersPerSale times that floor. The floor includes the reserve because a bid under the reserve cannot win, so escrowing less than the reserve buys only a slot.

func DepositOf

Action
1func DepositOf(saleID int64, bidder address) int64
source

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

func ForfeitedOf

Action
1func ForfeitedOf(saleID int64) int64
source

ForfeitedOf is the total deposit liquidated to the seller on a closed sale (0 before close). A cumulative statistic, not a held balance.

func HasRevealed

Action
1func HasRevealed(saleID int64, bidder address) bool
source

HasRevealed reports whether the bidder has opened their bid on a sale.

func IsCloseable

Action
1func IsCloseable(saleID int64) bool
source

IsCloseable reports whether Close would succeed right now.

func Lot

Action
1func Lot(saleID int64) string
source

Lot returns the sale's free-text lot description.

func MinDeposit

Action
1func MinDeposit(saleID int64) int64
source

MinDeposit is the resolved entry price for one commitment on this sale: the smallest deposit Commit accepts. It is at least max(MinBid, reserve) and is the seller's defence against slot squatting (audit Y1; see CreateSale).

func Phase

Action
1func Phase(saleID int64) string
source

Phase reports where a sale is: "commit", "reveal" or "closeable" while it is open, else its terminal status. The phase is derived from the chain height and is never stored, so it cannot disagree with the guards.

func Price

Action
1func Price(saleID int64) int64
source

Price is the SECOND price the winner actually paid once the sale has Settled, else 0. It is not the winning bid; see WinningBid.

func ProvisionalPrice

Action
1func ProvisionalPrice(saleID int64) int64
source

ProvisionalPrice is what the winner would pay if the sale closed right now: max(second bid, reserve, MinBid), or 0 if nothing is opened yet or the highest opening is below the reserve.

func Reveal

crossing Action
1func Reveal(cur realm, saleID int64, amount int64, salt string)
source

Reveal opens the caller's sealed bid on a sale whose commit window has shut. `amount` and `salt` must hash to the stored commitment, `amount` must be in [MinBid, MaxBid], and `amount` must not exceed the deposit — a bidder who committed above their escrow has no valid opening and forfeits at Close. Opening does not move coins; it moves the deposit from the `sealed` bucket to the `revealed` one and records the bid for the clearing scan.

func RevealedAmountOf

Action
1func RevealedAmountOf(saleID int64, bidder address) int64
source

RevealedAmountOf returns the bidder's OPENED bid amount (0 while sealed). A revealed bid is public by construction — it travelled in a transaction.

func SecondBid

Action
1func SecondBid(saleID int64) int64
source

SecondBid is the second-highest OPENED bid, or 0. With a tie on the highest it equals the highest.

func Status

Action
1func Status(saleID int64) string
source

Status is the sale's STORED state -- one of "open", "settled", "unsold" or "cancelled". It answers a different question from Phase: an open sale has three distinct phases but only one status, and a closed one has a status that no longer moves with the chain height.

func SweepDenom

crossing Action
1func SweepDenom(cur realm, denom string)
source

SweepDenom recovers out-of-band coins to the fee recipient. For the ledger denom the reserve is sealed + revealed + Liabilities() — escrowed deposits, claimable refunds, change, proceeds and the fee pot are all structurally unreachable.

func WinningBid

Action
1func WinningBid(saleID int64) int64
source

WinningBid is the highest OPENED bid on a sale, available as soon as it is revealed (a revealed bid is public). It is what the winner bid, not what they pay.

Dependencies

Source Files 2